Extensions/MailItemExtensions.cs
author Thomas
Tue, 03 Apr 2018 10:17:13 +0200
branchOUT-369
changeset 2100 91f72cbed562
parent 2075 e9022f6bf4fc
child 2092 c42b45e45ebc
permissions -rw-r--r--
Close branch OUT-369
     1 using MimeKit;
     2 using pEpCOMServerAdapterLib;
     3 using System;
     4 using System.Collections.Generic;
     5 using System.IO;
     6 using System.Text;
     7 using Outlook = Microsoft.Office.Interop.Outlook;
     8 
     9 namespace pEp
    10 {
    11     /// <summary>
    12     /// Contains extensions for the MailItem as well as utility methods specific for pEp.
    13     /// </summary>
    14     internal static class MailItemExtensions
    15     {
    16         public const string USER_PROPERTY_KEY_ORIG_ENTRY_ID             = "origEntryID";
    17         public const string USER_PROPERTY_KEY_IS_INCOMING               = "isIncoming";
    18         public const string USER_PROPERTY_KEY_IS_MIRROR                 = "isMirror";
    19         public const string UNKNOWN_SENDER                              = "unknown";
    20 
    21         private static object                     mutexCopiedItemsList  = new object();
    22         private static List<string>               copiedItemsList       = new List<string>();
    23 
    24         /// <summary>
    25         /// Enumeration defining the standard, setable pEp properties of an extended MailItem.
    26         /// These correspond with the properties in the PEPMessage.
    27         /// </summary>
    28         public enum PEPProperty
    29         {
    30             AutoConsume,
    31             ForceUnencrypted,
    32             KeyList,
    33             NeverUnsecure,
    34             PEPProtocolVersion,
    35             Rating,
    36             EnableProtection,
    37             ForceProtection
    38         }
    39 
    40         /**************************************************************
    41          * 
    42          * Extensions to Access a PEPProperty
    43          * 
    44          *************************************************************/
    45 
    46         /// <summary>
    47         /// Gets whether the given Outlook mail item is marked to never be unsecure.
    48         /// </summary>
    49         /// <param name="omi">The Outlook mail item to process with.</param>
    50         /// <returns>True if the Outlook mail item is marked to never be unsecure, otherwise false.</returns>
    51         public static bool GetNeverUnsecure(this Outlook.MailItem omi)
    52         {
    53             object propValue;
    54 
    55             if (omi != null)
    56             {
    57                 // Return status can be ignored here, using the auto default value is good enough
    58                 omi.GetPEPProperty(PEPProperty.NeverUnsecure, out propValue);
    59             }
    60             else
    61             {
    62                 propValue = GetPEPPropertyDefault(PEPProperty.NeverUnsecure);
    63             }
    64 
    65             return ((bool)propValue);
    66         }
    67 
    68         /// <summary>
    69         /// Gets whether the given Outlook mail item is marked to enable protection even if pEp is disabled.
    70         /// </summary>
    71         /// <param name="omi">The Outlook mail item to process with.</param>
    72         /// <returns>True if the Outlook mail item is marked to enable protection, otherwise false.</returns>
    73         public static bool GetEnableProtection(this Outlook.MailItem omi)
    74         {
    75             object propValue;
    76 
    77             if (omi != null)
    78             {
    79                 // Return status can be ignored here, using the auto default value is good enough
    80                 omi.GetPEPProperty(PEPProperty.EnableProtection, out propValue);
    81             }
    82             else
    83             {
    84                 propValue = GetPEPPropertyDefault(PEPProperty.EnableProtection);
    85             }
    86 
    87             return ((bool)propValue);
    88         }
    89 
    90         /// <summary>
    91         /// Gets whether the given Outlook mail item is marked with the autoconsume property.
    92         /// </summary>
    93         /// <param name="omi">The Outlook mail item to process with.</param>
    94         /// <returns>True if the Outlook mail item is marked with the autoconsume property, otherwise false.</returns>
    95         public static bool GetIsAutoConsume(this Outlook.MailItem omi)
    96         {
    97             object autoConsume = null;
    98             if (omi?.GetPEPProperty(MailItemExtensions.PEPProperty.AutoConsume, out autoConsume) == true)
    99             {
   100                 return (autoConsume != null);
   101             }
   102 
   103             return false;
   104         }
   105 
   106         /// <summary>
   107         /// Gets whether the given Outlook mail item is marked to force protection.
   108         /// </summary>
   109         /// <param name="omi">The Outlook mail item to process with.</param>
   110         /// <returns>True if the Outlook mail item is marked to force protection, otherwise false.</returns>
   111         public static bool GetIsForcefullyProtected(this Outlook.MailItem omi)
   112         {
   113             string forceProtectionId = omi.GetForceProtectionId();
   114 
   115             return (string.IsNullOrEmpty(forceProtectionId) == false);
   116         }
   117 
   118         /// <summary>
   119         /// Gets whether the given Outlook mail item is marked to send forcefully unencrypted.
   120         /// </summary>
   121         /// <param name="omi">The Outlook mail item to process with.</param>
   122         /// <returns>True if the Outlook mail item is marked to send forcefully unencrypted, otherwise false.</returns>
   123         public static bool GetIsForceUnencrypted(this Outlook.MailItem omi)
   124         {
   125             bool isForceUnencrypted = false;
   126             object value = null;
   127 
   128             if (omi?.GetPEPProperty(PEPProperty.ForceUnencrypted, out value) == true)
   129             {
   130                 isForceUnencrypted = (bool)value;
   131             }
   132 
   133             return isForceUnencrypted;
   134         }
   135 
   136         /// <summary>
   137         /// Gets the message GUID that is stored in the force protection MAPI property.
   138         /// </summary>
   139         /// <param name="omi">The Outlook mail item to process with.</param>
   140         /// <returns>The message GUID or null if not set.</returns>
   141         public static string GetForceProtectionId(this Outlook.MailItem omi)
   142         {
   143             object propValue = null;
   144             string guid = null;
   145 
   146             if (omi?.GetPEPProperty(PEPProperty.ForceProtection, out propValue) ?? false)
   147             {
   148                 guid = propValue as string;
   149             }
   150             else
   151             {
   152                 Log.Verbose("GetForceProtectionGuid: Could not get GUID. Mail item is null or property is not set.");
   153             }
   154 
   155             return guid;
   156         }
   157 
   158         /**************************************************************
   159          * 
   160          * Main Extensions
   161          * 
   162          *************************************************************/
   163 
   164         /// <summary>
   165         /// Adds a disclaimer text to this mail item's body if the
   166         /// respective setting is set for the item's account.
   167         /// </summary>
   168         /// <param name="omi">The Outlook mail item to process with.</param>
   169         public static void AddDisclaimer(this Outlook.MailItem omi)
   170         {
   171             try
   172             {
   173                 // Determine the disclaimer setting for the mail item's account
   174                 var accountSettings = Globals.ThisAddIn.Settings.GetAccountSettings(omi?.SenderEmailAddress ?? omi?.Sender.Address);
   175 
   176                 /* Add disclaimer if needed:
   177                  *      - if set to add to all messages
   178                  *      - if set to add to encrypted messages and outgoing rating is
   179                  *        at least reliable.
   180                  *        Special cases:
   181                  *          - ForceUnencrypted: checked for in outgoing rating
   182                  *          - ForceProtection: checked for in outgoing rating
   183                  *          - pEp disabled: unencrypted unless EnableProtection is set
   184                  */
   185                 if ((accountSettings.AddDisclaimer == PEPSettings.Disclaimer.AllMessages) ||
   186                     ((accountSettings.AddDisclaimer == PEPSettings.Disclaimer.OnlyEncryptedMessages) &&
   187                      (omi.GetOutgoingRating() >= pEpRating.pEpRatingReliable) &&
   188                      (omi.GetIsPEPEnabled())))
   189                 {
   190                     // We only add disclaimers to outgoing mails
   191                     if (omi.GetIsIncoming() == false)
   192                     {
   193                         if (string.IsNullOrWhiteSpace(omi.HTMLBody))
   194                         {
   195                             omi.Body += Environment.NewLine +
   196                                         Environment.NewLine +
   197                                         accountSettings.DisclaimerText;
   198                         }
   199                         else
   200                         {
   201                             omi.HTMLBody += "<br><br><p>" +
   202                                             accountSettings.DisclaimerText +
   203                                             "</p>";
   204                         }
   205                     }
   206                     else
   207                     {
   208                         Log.Verbose("AddDisclaimer: Skipped. Mail item is incoming.");
   209                     }
   210                 }
   211             }
   212             catch (Exception ex)
   213             {
   214                 Log.Error("AddDisclaimer: Error adding disclaimer to mail item. " + ex.ToString());
   215             }
   216         }
   217 
   218         /// <summary>
   219         /// Creates a new Outlook mail item in the unencrypted folder that is a mirror copy
   220         /// of the original mail item. This method will also update the mirror cache.
   221         /// Note: Normally, it should not be needed to pass a messageId, as we normally use the
   222         /// mail item's entryId. However, in cases where a mail item has no EntryId (e.g. an attached
   223         /// mail), we can store the messageId instead.
   224         /// </summary>
   225         /// <param name="omi">The Outlook mail item to process with.</param>
   226         /// <param name="messageId">Can be used instead of the mail item's entryId.</param>
   227         /// <returns>The newly created mirror Outlook mail item.</returns>
   228         public static Outlook.MailItem CreateMirrorOMI(this Outlook.MailItem omi, string messageId = null)
   229         {
   230             bool created = false;
   231             byte[] bytes;
   232             string str;
   233             Int32 messageFlags;
   234             Outlook.MailItem mirror = null;
   235             Outlook.Folder folder = omi.GetMirrorFolder();
   236 
   237             try
   238             {
   239                 Log.Verbose("CreateMirrorOMI: Creating by new mail item.");
   240 
   241                 mirror = (Outlook.MailItem)folder.Items.Add(Outlook.OlItemType.olMailItem);
   242                 mirror.UnRead = false;
   243 
   244                 // Set received and sent time
   245                 try
   246                 {
   247                     MapiHelper.SetProperty(mirror, MapiProperty.PidTagMessageDeliveryTime, omi.ReceivedTime.ToUniversalTime());
   248                     MapiHelper.SetProperty(mirror, MapiProperty.PidTagClientSubmitTime, omi.SentOn.ToUniversalTime());
   249                 }
   250                 catch (Exception ex)
   251                 {
   252                     Log.Verbose("CreateMirrorOMI: Failed to set received and sent time. " + ex.ToString());
   253                     throw;
   254                 }
   255 
   256                 // Attempt to set sender information
   257                 try
   258                 {
   259                     mirror.Sender = omi.Sender;
   260                 }
   261                 catch (Exception ex)
   262                 {
   263                     Log.Verbose("CreateMirrorOMI: Failed to set sender directly. " + ex.ToString());
   264                 }
   265 
   266                 try
   267                 {
   268                     MapiHelper.SetProperty(mirror, MapiProperty.PidTagSenderName, omi.SenderName);
   269                     MapiHelper.SetProperty(mirror, MapiProperty.PidTagSenderEmailAddress, omi.SenderEmailAddress);
   270 
   271                     // Entry ID is needed to resolve the sender so a reply is possible without having to re-enter address, this is last in case it fails
   272                     MapiHelper.SetProperty(mirror, MapiProperty.PidTagSenderEntryId, MapiHelper.GetProperty(omi, MapiProperty.PidTagSenderEntryId));
   273                 }
   274                 catch (Exception ex)
   275                 {
   276                     Log.Verbose("CreateMirrorOMI: Failed to set sender through MAPI properties. " + ex.ToString());
   277                     throw;
   278                 }
   279 
   280                 try
   281                 {
   282                     mirror.SendUsingAccount = omi.SendUsingAccount;
   283                 }
   284                 catch (Exception ex)
   285                 {
   286                     Log.Verbose("CreateMirrorOMI: Failed to set SendUsingAccount. " + ex.ToString());
   287                 }
   288 
   289                 // Set flags
   290                 messageFlags = (System.Int32)MapiHelper.GetProperty(mirror, MapiProperty.PidTagMessageFlags);
   291                 messageFlags &= ~((System.Int32)MapiPropertyValue.EnumPidTagMessageFlags.mfUnsent);  // Clear UNSENT flag -- must be done before save
   292                 messageFlags |= ((System.Int32)MapiPropertyValue.EnumPidTagMessageFlags.mfRead);     // Mark as read
   293                 try
   294                 {
   295                     MapiHelper.SetProperty(mirror, MapiProperty.PidTagMessageFlags, messageFlags);
   296                 }
   297                 catch (Exception ex)
   298                 {
   299                     Log.Verbose("CreateMirrorOMI: Failed to set message flags. " + ex.ToString());
   300                 }
   301 
   302                 // Conversation information
   303                 try
   304                 {
   305                     /* Note: PidTagConversationId cannot be set even through the MAPI accessor.
   306                      * This is by design since this property is computed automatically from other properties.
   307                      * See: https://msdn.microsoft.com/en-us/library/ee204279.aspx
   308                      */
   309 
   310                     str = omi.ConversationIndex;
   311                     if (str != null)
   312                     {
   313                         bytes = new byte[str.Length / 2];
   314                         for (int i = 0; i < str.Length; i += 2)
   315                         {
   316                             bytes[i / 2] = Convert.ToByte(str.Substring(i, 2), 16);
   317                         }
   318 
   319                         MapiHelper.SetProperty(mirror, MapiProperty.PidTagConversationIndex, bytes);
   320                     }
   321 
   322                     MapiHelper.SetProperty(mirror, MapiProperty.PidTagConversationIndexTracking, false);
   323                     MapiHelper.SetProperty(mirror, MapiProperty.PidTagConversationTopic, omi.ConversationTopic);
   324                 }
   325                 catch (Exception ex)
   326                 {
   327                     Log.Verbose("CreateMirrorOMI: Failed to set conversation information. " + ex.ToString());
   328                 }
   329 
   330                 mirror.To = omi.To;
   331                 mirror.CC = omi.CC;
   332                 mirror.BCC = omi.BCC;
   333                 mirror.Subject = "p≡p";
   334                 mirror.Body = string.Empty;
   335                 mirror.Save();
   336 
   337                 /* Set all received-by information
   338                  * 
   339                  * This must be stored as a user-property because settings MAPI properties such as "PR_RECEIVED_BY_EMAIL_ADDRESS"
   340                  * is not supported by Outlook. The user-properties are named the same as the MAPI property for simplicity.
   341                  * 
   342                  * In most situations the received-by information will not be needed.
   343                  * Decryption for example uses the recipients/identities in the original mail item itself.
   344                  * However, the GetMyIdentity(omi) method requires this information in some cases:
   345                  *  • The true 'myself' identity of a mail item is not an account registerd in Outlook. This can happen
   346                  *    using aliases or when forwarding emails from another account.
   347                  *  • This is primarily needed when displaying mirrors themselves then opening the handshake dialog.
   348                  */
   349                 try
   350                 {
   351                     /* Get the "received by" email address. In case this returns an internal X500 Exchange address ("/O=DOMAIN/OU=EXCHANGE ADMINISTRATIVE GROUP..."),
   352                      * compare it with the recipients' addresses and take the primary SMTP address.
   353                      */
   354                     string email = MapiHelper.GetProperty(omi, MapiProperty.PidTagReceivedByEmailAddress) as string;
   355                     if (email.StartsWith("/O"))
   356                     {
   357                         Outlook.Recipients recipients = null;
   358                         Outlook.Recipient recipient = null;
   359                         Outlook.AddressEntry addressEntry = null;
   360                         Outlook.ExchangeUser exchangeUser = null;
   361 
   362                         try
   363                         {
   364                             recipients = omi.Recipients;
   365 
   366                             for (int i = 1; i <= recipients.Count; i++)
   367                             {
   368                                 recipient = recipients[i];
   369                                 addressEntry = recipient?.AddressEntry;
   370                                 exchangeUser = addressEntry?.GetExchangeUser();
   371 
   372                                 if (email.ToUpperInvariant().Equals(exchangeUser?.Address?.ToUpperInvariant()))
   373                                 {
   374                                     email = exchangeUser?.PrimarySmtpAddress;
   375                                     break;
   376                                 }
   377 
   378                                 recipient = null;
   379                                 addressEntry = null;
   380                                 exchangeUser = null;
   381                             }
   382                         }
   383                         catch (Exception ex)
   384                         {
   385                             Log.Error("CreateMirrorOMI: Error getting ReceivedByEmailAddress. " + ex.ToString());
   386                         }
   387                         finally
   388                         {
   389                             recipients = null;
   390                             recipient = null;
   391                             addressEntry = null;
   392                             exchangeUser = null;
   393                         }
   394                     }
   395                     mirror.SetUserProperty(MapiProperty.PidTagReceivedByEmailAddress.DaslName, email,
   396                                         Outlook.OlUserPropertyType.olText);
   397                     mirror.SetUserProperty(MapiProperty.PidTagReceivedByName.DaslName,
   398                                         MapiHelper.GetProperty(omi, MapiProperty.PidTagReceivedByName),
   399                                         Outlook.OlUserPropertyType.olText);
   400                 }
   401                 catch (Exception ex)
   402                 {
   403                     Log.Verbose("CreateMirrorOMI: Failed to set received-by information. " + ex.ToString());
   404                 }
   405 
   406                 // Mark the mail item as a mirror
   407                 mirror.SetUserProperty(MailItemExtensions.USER_PROPERTY_KEY_IS_MIRROR, true, Outlook.OlUserPropertyType.olYesNo);
   408 
   409                 /* Set the incoming status of the new mail item.
   410                  * This is necessary because manually setting the PR_RECEIVED_BY_ENTRYID fails if called by user code.
   411                  * PR_RECEIVED_BY_ENTRYID is only set when copying the original mail item.
   412                  */
   413                 if (omi.GetIsIncoming())
   414                 {
   415                     mirror.SetUserProperty(MailItemExtensions.USER_PROPERTY_KEY_IS_INCOMING, true, Outlook.OlUserPropertyType.olYesNo);
   416                 }
   417                 else
   418                 {
   419                     mirror.SetUserProperty(MailItemExtensions.USER_PROPERTY_KEY_IS_INCOMING, false, Outlook.OlUserPropertyType.olYesNo);
   420                 }
   421 
   422                 // Add the original EntryID or the messageId if the latter has been passed
   423                 mirror.SetUserProperty(MailItemExtensions.USER_PROPERTY_KEY_ORIG_ENTRY_ID, messageId ?? omi.EntryID);
   424                 mirror.Save();
   425 
   426                 // Move the mirror copy to the correct folder
   427                 // This is needed as some account-types/office versions will still create a mail item in the default folder
   428                 // even if folder.Items.Add is used to specify a location.
   429                 // WARNING: this creates a new mail item that will no longer be referenced here!!
   430                 mirror.Move(folder);
   431                 created = true;
   432             }
   433             catch (Exception ex)
   434             {
   435                 Log.Verbose("CreateMirrorOMI: Creating new mail item failed. " + ex.ToString());
   436                 mirror.PermanentlyDelete();
   437                 mirror = null;
   438             }
   439 
   440             if (created == false)
   441             {
   442                 try
   443                 {
   444                     Log.Verbose("CreateMirrorOMI: Creating by copy.");
   445 
   446                     // Release any partially created omi from a failed copy
   447                     mirror = null;
   448 
   449                     /* Attempt to create the mirror by copying
   450                      * Depending on account types, anything after Office 2010 can have the following error:
   451                      *  System.Runtime.InteropServices.COMException (0x80040102): Sorry, Exchange ActiveSync doesn't
   452                      *  support what you're trying to do. 
   453                      *    at Microsoft.Office.Interop.Outlook._MailItem.Copy()
   454                      * When this happens a new mail item must be created.
   455                      */
   456                     lock (mutexCopiedItemsList)
   457                     {
   458                         mirror = (Outlook.MailItem)omi.Copy();
   459                         if (copiedItemsList.Contains(mirror?.EntryID) == false)
   460                         {
   461                             copiedItemsList.Add(mirror?.EntryID);
   462                         }
   463                     }
   464 
   465                     mirror.UnRead = false;
   466 
   467                     /* Set all received-by information
   468                      * 
   469                      * This must be stored as a user-property because settings MAPI properties such as "PR_RECEIVED_BY_EMAIL_ADDRESS"
   470                      * is not supported by Outlook. The user-properties are named the same as the MAPI property for simplicity.
   471                      * 
   472                      * In most situations the received-by information will not be needed.
   473                      * Decryption for example uses the recipients/identities in the original mail item itself.
   474                      * However, the GetMyIdentity(omi) method requires this information in some cases:
   475                      *  • The true 'myself' identity of a mail item is not an account registerd in Outlook. This can happen
   476                      *    using aliases or when forwarding emails from another account.
   477                      *  • This is primarily needed when displaying mirrors themselves then opening the privacy status manager.
   478                      */
   479                     try
   480                     {
   481                         mirror.SetUserProperty(MapiProperty.PidTagReceivedByEmailAddress.DaslName,
   482                                             MapiHelper.GetProperty(omi, MapiProperty.PidTagReceivedByEmailAddress),
   483                                             Outlook.OlUserPropertyType.olText);
   484                         mirror.SetUserProperty(MapiProperty.PidTagReceivedByName.DaslName,
   485                                             MapiHelper.GetProperty(omi, MapiProperty.PidTagReceivedByName),
   486                                             Outlook.OlUserPropertyType.olText);
   487                     }
   488                     catch { }
   489 
   490                     // Mark the mail item as a mirror
   491                     mirror.SetUserProperty(MailItemExtensions.USER_PROPERTY_KEY_IS_MIRROR, true, Outlook.OlUserPropertyType.olYesNo);
   492 
   493                     /* Set the incoming status of the new mail item.
   494                      * This is necessary because manually setting the PR_RECEIVED_BY_ENTRYID fails if called by user code.
   495                      * PR_RECEIVED_BY_ENTRYID is only set when copying the original mail item.
   496                      */
   497                     if (omi.GetIsIncoming())
   498                     {
   499                         mirror.SetUserProperty(MailItemExtensions.USER_PROPERTY_KEY_IS_INCOMING, true, Outlook.OlUserPropertyType.olYesNo);
   500                     }
   501                     else
   502                     {
   503                         mirror.SetUserProperty(MailItemExtensions.USER_PROPERTY_KEY_IS_INCOMING, false, Outlook.OlUserPropertyType.olYesNo);
   504                     }
   505 
   506                     // Add the original EntryID to the mirror copy
   507                     // This must be done before the move as the reference is then lost
   508                     mirror.SetUserProperty(MailItemExtensions.USER_PROPERTY_KEY_ORIG_ENTRY_ID, messageId ?? omi.EntryID);
   509                     mirror.Save();
   510 
   511                     // Move the mirror copy to the correct folder
   512                     // WARNING: this creates a new mail item that will no longer be referenced here!!
   513                     mirror.Move(folder);
   514                 }
   515                 catch (Exception ex)
   516                 {
   517                     Log.Error("CreateMirrorOMI: Creating by copy failed. " + ex.ToString());
   518                 }
   519                 finally
   520                 {
   521                     lock (mutexCopiedItemsList)
   522                     {
   523                         if (copiedItemsList.Contains(omi?.EntryID))
   524                         {
   525                             copiedItemsList.Remove(omi?.EntryID);
   526                         }
   527                     }
   528                 }
   529 
   530             }
   531 
   532             // Release objects
   533             if (mirror != null)
   534             {
   535                 // Marshal.ReleaseComObject(omi);
   536                 mirror = null;
   537             }
   538 
   539             if (folder != null)
   540             {
   541                 // Marshal.ReleaseComObject(folder);
   542                 folder = null;
   543             }
   544 
   545             /* Locate and return the mirror that was just created.
   546              * During this find method, the mirror cache will be updated when found.
   547              * It is important to do this here as any .Move methods create a new mail item.
   548              */
   549             return omi.GetMirror(messageId);
   550         }
   551 
   552         /// <summary>
   553         /// Searches for mirrored outlook mail item associated with the given outlook mail item.
   554         /// To improve performance, this method implements some caching. 
   555         /// Note: Normally, it should not be needed to pass a messageId, as we normally use the
   556         /// mail item's entryId. However, in cases where a mail item has no EntryId (e.g. an attached
   557         /// mail), we can store the messageId instead.
   558         /// </summary>
   559         /// <param name="omi">The outlook mail item to find the mirror for.</param>
   560         /// <param name="messageId">Can be used instead of the mail item's entryId.</param>
   561         /// <returns>The mirror mail item if successful. Otherwise null.</returns>
   562         public static Outlook.MailItem GetMirror(this Outlook.MailItem omi, string messageId = null)
   563         {
   564             Outlook.MailItem mirror = null;
   565             string userName = string.Empty;
   566 
   567             try
   568             {
   569                 if ((omi != null) &&
   570                     (PEPIdentity.GetFromUserName(omi, out userName) == Globals.ReturnStatus.Success))
   571                 {
   572                     mirror = PEPMessage.GetMirror(messageId ?? omi.EntryID, userName);
   573                 }
   574                 else
   575                 {
   576                     Log.Error("MsgProcessor.GetMirror: Original is null or From user name could not be retrieved.");
   577                 }
   578             }
   579             catch (Exception ex)
   580             {
   581                 Log.Error("MsgProcessor.GetMirror: Error getting mirror. " + ex.ToString());
   582             }
   583 
   584             return mirror;
   585         }
   586 
   587         /// <summary>
   588         /// Determines whether to enable the form region for this mail item.
   589         /// </summary>
   590         /// <param name="omi">The Outlook mail item to process with.</param>
   591         /// <returns>True if the mail item's form region is to enable, otherwise false.</returns>
   592         public static bool GetEnableFormRegion(this Outlook.MailItem omi)
   593         {
   594             bool enable = true;
   595 
   596             if (omi != null)
   597             {
   598                 try
   599                 {
   600                     if (omi.GetIsPEPEnabled() == false)
   601                     {
   602                         if (omi.GetIsIncoming())
   603                         {
   604                             enable = omi.GetIsDecryptAlwaysEnabled();
   605                         }
   606                         else
   607                         {
   608                             enable = omi.GetEnableProtection();
   609                         }
   610                     }
   611                 }
   612                 catch (Exception ex)
   613                 {
   614                     Log.Error("GetEnableFormRegion: Error occured. " + ex.ToString());
   615                 }
   616             }
   617 
   618             return enable;
   619         }
   620 
   621         /// <summary>
   622         /// Determines if the mail item's store is set to always decrypt.
   623         /// </summary>
   624         /// <param name="omi">The Outlook mail item to process with.</param>
   625         /// <returns>True if the mail item's store is set to always decrypt, otherwise false.</returns>
   626         public static bool GetIsDecryptAlwaysEnabled(this Outlook.MailItem omi)
   627         {
   628             bool isEnabled = PEPSettings.ACCOUNT_SETTING_IS_DECRYPT_ALWAYS_ENABLED_DEFAULT;
   629             Outlook.Folder folder = null;
   630             Outlook.Store store = null;
   631 
   632             try
   633             {
   634                 folder = omi.Parent as Outlook.Folder;
   635                 store = folder?.Store;
   636                 isEnabled = store?.GetIsDecryptAlwaysEnabled() ?? PEPSettings.ACCOUNT_SETTING_IS_DECRYPT_ALWAYS_ENABLED_DEFAULT;
   637             }
   638             catch
   639             {
   640                 Log.Error("GetDecryptAlwaysEnabled: failure occured, returning default.");
   641             }
   642             finally
   643             {
   644                 folder = null;
   645                 store = null;
   646             }
   647 
   648             return isEnabled;
   649         }
   650 
   651         /// <summary>
   652         /// Determines if the mail item is a draft (unsent) from its MAPI message flags.
   653         /// This specifically with check the 'mfUnsent' flag.
   654         /// </summary>
   655         /// <param name="omi">The Outlook mail item to process with.</param>
   656         /// <returns>True if the Outlook mail item is a draft (unsent), otherwise false.</returns>
   657         public static bool GetIsDraft(this Outlook.MailItem omi)
   658         {
   659             bool isDraft = false;
   660             Int32 messageFlags;
   661 
   662             if (omi != null)
   663             {
   664                 // Detect using MAPI message flags
   665                 messageFlags = (Int32)MapiHelper.GetProperty(omi, MapiProperty.PidTagMessageFlags, (Int32)0);
   666                 if ((messageFlags & ((Int32)MapiPropertyValue.EnumPidTagMessageFlags.mfUnsent)) > 0)
   667                 {
   668                     isDraft = true;
   669                 }
   670             }
   671 
   672             return (isDraft);
   673         }
   674 
   675         /// <summary>
   676         /// Determines if the mail item is submitted (user already pressed send) from its MAPI message flags.
   677         /// Submitted items should usually be located within the Outbox.
   678         /// This specifically will check the 'mfSubmitted' flag.
   679         /// </summary>
   680         /// <param name="omi">The Outlook mail item to process with.</param>
   681         /// <returns>True if the Outlook mail item is already submitted, otherwise false.</returns>
   682         public static bool GetIsSubmitted(this Outlook.MailItem omi)
   683         {
   684             bool isSubmitted = false;
   685             Int32 messageFlags;
   686 
   687             if (omi != null)
   688             {
   689                 // Detect using MAPI message flags
   690                 messageFlags = (Int32)MapiHelper.GetProperty(omi, MapiProperty.PidTagMessageFlags, (Int32)0);
   691                 if ((messageFlags & ((Int32)MapiPropertyValue.EnumPidTagMessageFlags.mfSubmitted)) > 0)
   692                 {
   693                     isSubmitted = true;
   694                 }
   695             }
   696 
   697             return (isSubmitted);
   698         }
   699 
   700         /// <summary>
   701         /// Gets the originally stored rating of the mail item.
   702         /// </summary>
   703         /// <param name="omi">The Outlook mail item to process with.</param>
   704         /// <returns>The stored rating or null if an error occured.</returns>
   705         public static pEpRating? GetStoredRating(this Outlook.MailItem omi)
   706         {
   707             pEpRating? rating = null;
   708 
   709             if (omi != null)
   710             {
   711                 try
   712                 {
   713                     object propValue = null;
   714                     if (omi.GetPEPProperty(PEPProperty.Rating, out propValue))
   715                     {
   716                         rating = (pEpRating)propValue;
   717                     }
   718                 }
   719                 catch (Exception ex)
   720                 {
   721                     Log.Error("GetStoredRating: Error occured. " + ex.ToString());
   722                 }
   723             }
   724 
   725             return rating;
   726         }
   727 
   728         /// <summary>
   729         /// Sets or removes a message flag.
   730         /// </summary>
   731         /// <param name="omi">The Outlook mail item to process with.</param>
   732         /// <param name="messageFlag">The message flag to set or remove.</param>
   733         /// <param name="value">Whether to set or remove the flag.</param>
   734         public static void SetMessageFlag(this Outlook.MailItem omi,
   735                                           MapiPropertyValue.EnumPidTagMessageFlags messageFlag,
   736                                           bool value)
   737         {
   738             Int32 existingFlags;
   739             Int32 newFlags;
   740 
   741             if (omi != null)
   742             {
   743                 existingFlags = (Int32)MapiHelper.GetProperty(omi, MapiProperty.PidTagMessageFlags, (Int32)0);
   744 
   745                 if (value)
   746                 {
   747                     newFlags = existingFlags | (Int32)messageFlag;
   748                 }
   749                 else
   750                 {
   751                     newFlags = existingFlags & ~(Int32)messageFlag;
   752                 }
   753 
   754                 if (existingFlags != newFlags)
   755                 {
   756                     try
   757                     {
   758                         MapiHelper.SetProperty(omi, MapiProperty.PidTagMessageFlags, newFlags);
   759                     }
   760                     catch { }
   761                 }
   762             }
   763 
   764             return;
   765         }
   766 
   767         /// <summary>
   768         /// Sets the user properties that indicate if a mail item has been encrypted
   769         /// and with which rating. 
   770         /// Note: The referenced mail item here is supposed to be a reply or replyAll
   771         /// item and this method is intended to be used when the respective events are
   772         /// being called and the reply item is being opened. This way, after the item
   773         /// is opened, we can access the rating of the original from the reply item.
   774         /// <param name="omi">The Outlook mail item to process with.</param>
   775         /// <param name="rating">The rating of the original mail item.</param>
   776         /// </summary>
   777         public static void SetOriginallyEncryptedStatus(this Outlook.MailItem omi, pEpRating rating)
   778         {
   779             if (rating >= pEpRating.pEpRatingUnencryptedForSome)
   780             {
   781                 omi?.SetUserProperty(CryptableMailItem.USER_PROPERTY_KEY_IS_ORIGINALLY_ENCRYPTED,
   782                                           true,
   783                                           Outlook.OlUserPropertyType.olYesNo);
   784 
   785                 omi?.SetUserProperty(CryptableMailItem.USER_PROPERTY_KEY_ORIGINAL_RATING,
   786                                           rating.ToEngineString(),
   787                                           Outlook.OlUserPropertyType.olText);
   788 
   789                 // If pEp is disabled, set the Enable Protection property
   790                 if (omi?.GetIsPEPEnabled() == false)
   791                 {
   792                     omi?.SetPEPProperty(MailItemExtensions.PEPProperty.EnableProtection, true);
   793                 }
   794             }
   795         }
   796 
   797         /// <summary>
   798         /// Gets whether this mail item is an attached mail (attachment of another mail).
   799         /// This is done by retrieving the mail item's message Id and checking if
   800         /// it is found in the attaached mails cache.
   801         /// </summary>
   802         /// <param name="omi">The Outlook mail item to process with.</param>
   803         /// <param name="messageId">The mail items's messageId</param>
   804         /// <returns>True if it is an attached mail, otherwise false.</returns>
   805         public static bool GetIsAttachedMail(this Outlook.MailItem omi, out string messageId)
   806         {
   807             messageId = null;
   808             HeaderList headers = omi.GetParsedTransportMessageHeaders();
   809 
   810             try
   811             {
   812                 messageId = headers[HeaderId.MessageId];
   813             }
   814             catch (Exception ex)
   815             {
   816                 messageId = null;
   817                 Log.Verbose("FormRegionPrivacyStatus_FormRegionShowing: Error getting MessageId from item. " + ex.ToString());
   818             }
   819 
   820             // If MessageId is found in cache, item is an attached mail
   821             return ((string.IsNullOrEmpty(messageId) == false) && (PEPAttachment.AttachedMailsCache.Contains(messageId)));
   822         }
   823 
   824         /// <summary>
   825         /// Gets whether the mail item is marked as incoming (received mail, not sent).
   826         /// </summary>
   827         /// <param name="omi">The Outlook mail item to process with.</param>
   828         /// <returns>True if the Outlook mail item is considered incoming, otherwise false.</returns>
   829         public static bool GetIsIncoming(this Outlook.MailItem omi)
   830         {
   831             object property = null;
   832             bool result = true; // Most emails are incoming/received
   833 
   834             if (omi != null)
   835             {
   836                 try
   837                 {
   838                     property = omi.GetUserProperty(MailItemExtensions.USER_PROPERTY_KEY_IS_INCOMING);
   839                 }
   840                 catch
   841                 {
   842                     property = null;
   843                     Log.Warning("GetIsIncoming: Failed to get IsIncoming user property.");
   844                 }
   845 
   846                 if (property != null)
   847                 {
   848                     /* Use the stored user property
   849                      * This property is set only during creation of a mirror OMI.
   850                      * This is necessary because manually setting the PR_RECEIVED_BY_ENTRYID fails if called by user code.
   851                      */
   852                     result = (bool)property;
   853                 }
   854                 else if (omi.GetIsInSentFolder())
   855                 {
   856                     /* Any messages in the sent folder are assumed to be outgoing.
   857                      * This is necessary because some account types (ActiveSync) save emails
   858                      * in the sent folder with the ReceivedByEntryID set.
   859                      * Without detecting the message is in the sent folder first, it would appear
   860                      * as incoming.
   861                      */
   862                     result = false;
   863                 }
   864                 else
   865                 {
   866                     // Check ReceivedByEntryID
   867                     result = (omi.ReceivedByEntryID != null);
   868 
   869                     // Doublecheck for attached items
   870                     if ((result == false) &&
   871                         (PEPAttachment.AttachedMailsCache.Count > 0))
   872                     {
   873                         string messageId;
   874                         result = omi.GetIsAttachedMail(out messageId);
   875                     }
   876                 }
   877             }
   878             else
   879             {
   880                 Log.Error("GetIsIncoming: Attempted to get for a null MailItem.");
   881             }
   882 
   883             return (result);
   884         }
   885 
   886         /// <summary>
   887         /// Determines if the mail item is an ActiveSync type store.
   888         /// </summary>
   889         /// <param name="omi">The Outlook mail item to process with.</param>
   890         /// <returns>True if the mail item is an ActiveSync type store. If this method fails, false is returned.</returns>
   891         public static bool GetIsInActiveSyncStore(this Outlook.MailItem omi)
   892         {
   893             bool isActiveSync = false;
   894             Outlook.Folder folder = null;
   895             Outlook.Store store = null;
   896 
   897             try
   898             {
   899                 folder = (Outlook.Folder)omi.Parent;
   900                 store = folder.Store;
   901                 isActiveSync = store.GetIsAccountType(Outlook.OlAccountType.olEas);
   902             }
   903             catch
   904             {
   905                 isActiveSync = false;
   906                 Log.Error("GetIsInActiveSyncStore: failure occured, returning default false.");
   907             }
   908             finally
   909             {
   910                 folder = null;
   911                 store = null;
   912             }
   913 
   914             return (isActiveSync);
   915         }
   916 
   917         /// <summary>
   918         /// Determines if the mail item is an Exchange type store.
   919         /// </summary>
   920         /// <param name="omi">The Outlook mail item to process with.</param>
   921         /// <returns>True if the mail item is an Exchange type store. If this method fails, false is returned.</returns>
   922         public static bool GetIsInExchangeStore(this Outlook.MailItem omi)
   923         {
   924             bool isExchangeStore = false;
   925             Outlook.Folder folder = null;
   926             Outlook.Store store = null;
   927 
   928             try
   929             {
   930                 folder = (Outlook.Folder)omi.Parent;
   931                 store = folder.Store;
   932                 isExchangeStore = store.GetIsAccountType(Outlook.OlAccountType.olExchange);
   933             }
   934             catch
   935             {
   936                 isExchangeStore = false;
   937                 Log.Error("GetIsInExchangeStore: failure occured, returning default false.");
   938             }
   939             finally
   940             {
   941                 folder = null;
   942                 store = null;
   943             }
   944 
   945             return (isExchangeStore);
   946         }
   947 
   948         /// <summary>
   949         /// Determines if the mail item is an IMAP type store.
   950         /// </summary>
   951         /// <param name="omi">The Outlook mail item to process with.</param>
   952         /// <returns>True if the mail item is an IMAP type store. If this method fails, false is returned.</returns>
   953         public static bool GetIsInIMAPStore(this Outlook.MailItem omi)
   954         {
   955             bool isIMAP = false;
   956             Outlook.Folder folder = null;
   957             Outlook.Store store = null;
   958 
   959             try
   960             {
   961                 folder = (Outlook.Folder)omi.Parent;
   962                 store = folder.Store;
   963                 isIMAP = store.GetIsAccountType(Outlook.OlAccountType.olImap);
   964             }
   965             catch
   966             {
   967                 Log.Error("GetIsInIMAPStore: failure occured, returning default false.");
   968             }
   969             finally
   970             {
   971                 // Release objects
   972                 if (folder != null)
   973                 {
   974                     // Marshal.ReleaseComObject(folder);
   975                     folder = null;
   976                 }
   977 
   978                 if (store != null)
   979                 {
   980                     // Marshal.ReleaseComObject(store);
   981                     store = null;
   982                 }
   983             }
   984 
   985             return (isIMAP);
   986         }
   987 
   988         /// <summary>
   989         /// Gets whether the mail item is a mirror.
   990         /// This is done by checking for the mirror indicating user property.
   991         /// </summary>
   992         /// <param name="omi">The Outlook mail item to process with.</param>
   993         /// <returns>True if the Outlook mail item is a mirror, otherwise false.</returns>
   994         public static bool GetIsMirror(this Outlook.MailItem omi)
   995         {
   996             bool isMirror = false;
   997             object propValue;
   998 
   999             if (omi != null)
  1000             {
  1001                 propValue = omi.GetUserProperty(MailItemExtensions.USER_PROPERTY_KEY_IS_MIRROR);
  1002 
  1003                 if ((propValue != null) &&
  1004                     (((bool)propValue) == true))
  1005                 {
  1006                     isMirror = true;
  1007                 }
  1008             }
  1009 
  1010             return (isMirror);
  1011         }
  1012 
  1013         /// <summary>
  1014         /// Determines if the mail item has pEp enabled for it.
  1015         /// This will handle drafts (using sending account) as well as saved mail items (using store).
  1016         /// </summary>
  1017         /// <param name="omi">The Outlook mail item to process with.</param>
  1018         /// <returns>True if the mail item has pEp enabled, otherwise false.</returns>
  1019         public static bool GetIsPEPEnabled(this Outlook.MailItem omi)
  1020         {
  1021             bool isEnabled = PEPSettings.ACCOUNT_SETTING_IS_PEP_ENABLED_DEFAULT;
  1022             bool success = false;
  1023             Outlook.Folder folder = null;
  1024             Outlook.Store store = null;
  1025             Outlook.Account acct = null;
  1026 
  1027             try
  1028             {
  1029                 // Try using the SendUsingAccount for drafts
  1030                 try
  1031                 {
  1032                     if (omi.GetIsDraft())
  1033                     {
  1034                         acct = omi.SendUsingAccount;
  1035                         isEnabled = acct.GetIsPEPEnabled();
  1036                         success = true;
  1037                     }
  1038                 }
  1039                 catch
  1040                 {
  1041                     success = false;
  1042                     Log.Warning("GetIsPEPEnabled: failed using SendUsingAccount.");
  1043                 }
  1044 
  1045                 // Try using the store
  1046                 if (success == false)
  1047                 {
  1048                     folder = (Outlook.Folder)omi.Parent;
  1049                     store = folder.Store;
  1050                     isEnabled = store.GetIsPEPEnabled();
  1051                     success = true;
  1052                 }
  1053             }
  1054             catch
  1055             {
  1056                 Log.Error("GetIsPEPEnabled: failure occured, returning default.");
  1057             }
  1058             finally
  1059             {
  1060                 // Release objects
  1061                 if (acct != null)
  1062                 {
  1063                     // Marshal.ReleaseComObject(acct);
  1064                     acct = null;
  1065                 }
  1066 
  1067                 if (folder != null)
  1068                 {
  1069                     // Marshal.ReleaseComObject(folder);
  1070                     folder = null;
  1071                 }
  1072 
  1073                 if (store != null)
  1074                 {
  1075                     // Marshal.ReleaseComObject(store);
  1076                     store = null;
  1077                 }
  1078             }
  1079 
  1080             return (isEnabled);
  1081         }
  1082 
  1083         /// <summary>
  1084         /// Determines if the mail item is to be stored securely (untrusted account/server).
  1085         /// </summary>
  1086         /// <param name="omi">The Outlook mail item to process with.</param>
  1087         /// <returns>True if the mail item is to be stored securely, otherwise false.</returns>
  1088         public static bool GetIsInSecureStore(this Outlook.MailItem omi)
  1089         {
  1090             bool isSecure = PEPSettings.ACCOUNT_SETTING_IS_SECURE_STORAGE_ENABLED_DEFAULT;
  1091             Outlook.Folder folder = null;
  1092             Outlook.Store store = null;
  1093 
  1094             try
  1095             {
  1096                 folder = (Outlook.Folder)omi.Parent;
  1097                 store = folder.Store;
  1098                 isSecure = store.GetIsSecureStorageEnabled();
  1099             }
  1100             catch
  1101             {
  1102                 Log.Error("GetIsInSecureStore: failure occured, returning default.");
  1103             }
  1104             finally
  1105             {
  1106                 // Release objects
  1107                 if (folder != null)
  1108                 {
  1109                     // Marshal.ReleaseComObject(folder);
  1110                     folder = null;
  1111                 }
  1112 
  1113                 if (store != null)
  1114                 {
  1115                     // Marshal.ReleaseComObject(store);
  1116                     store = null;
  1117                 }
  1118             }
  1119 
  1120             return (isSecure);
  1121         }
  1122 
  1123         /// <summary>
  1124         /// Determines if the mail item is in its store's default sent folder or a sent folder known
  1125         /// by pEp.
  1126         /// </summary>
  1127         /// <param name="omi">The Outlook mail item to process with.</param>
  1128         /// <returns>True if the mail item is in the default sent folder, otherwise false.</returns>
  1129         public static bool GetIsInSentFolder(this Outlook.MailItem omi)
  1130         {
  1131             bool result = false;
  1132             Outlook.Folder folder = null;
  1133             Outlook.Folder rootFolder = null;
  1134             Outlook.Folder sentFolder = null;
  1135             Outlook.Folders folders = null;
  1136             Outlook.Store store = null;
  1137 
  1138             try
  1139             {
  1140                 /* The check whether the mailitem is in the Sent folder or a subfolder of it is done in the following way:
  1141                  * 1. Get the full path of the containing folder and split it into an array containing all folders of this path
  1142                  * 2. Get the mailitem's store and all Level 1 folders of this store
  1143                  * 3. Check if the Level 1 folder of the mailitem's folder path (array index 3) matches the Sent folder. If the default method 
  1144                  *    doesn't return a Sent folder, check by all known accounts' custom Sent folder Entry IDs.
  1145                  */
  1146                 folder = (Outlook.Folder)omi.Parent;
  1147                 store = folder.Store;
  1148                 rootFolder = (Outlook.Folder)store.GetRootFolder();
  1149                 folders = rootFolder.Folders;
  1150                 var pathArray = folder.FolderPath.Split('\\');
  1151                 var folderLevelOneEntryId = folders[pathArray[3]].EntryID;
  1152 
  1153                 // Trying to get the Sent folder with the default method
  1154                 try
  1155                 {
  1156                     sentFolder = (Outlook.Folder)store.GetDefaultFolder(Outlook.OlDefaultFolders.olFolderSentMail);
  1157                 }
  1158                 catch { }
  1159 
  1160                 // Check for a match between the Sent folder and the Level 1 folder in the mailitem's path
  1161                 if (sentFolder != null)
  1162                 {
  1163                     if ((folder.EntryID == sentFolder.EntryID) ||
  1164                         (folderLevelOneEntryId == sentFolder.EntryID))
  1165                     {
  1166                         result = true;
  1167                     }
  1168                 }
  1169                 else
  1170                 {
  1171                     for (int i = 0; i < Globals.ThisAddIn.Settings.AccountSettingsList.Count; i++)
  1172                     {
  1173                         if ((Globals.ThisAddIn.Settings.AccountSettingsList[i].SentFolderEntryId != null) &&
  1174                             (folderLevelOneEntryId == Globals.ThisAddIn.Settings.AccountSettingsList[i].SentFolderEntryId))
  1175                         {
  1176                             result = true;
  1177                             break;
  1178                         }
  1179                     }
  1180                 }
  1181             }
  1182             catch { }
  1183             finally
  1184             {
  1185                 // Release objects
  1186                 if (folder != null)
  1187                 {
  1188                     // Marshal.ReleaseComObject(folder);
  1189                     folder = null;
  1190                 }
  1191 
  1192                 if (rootFolder != null)
  1193                 {
  1194                     // Marshal.ReleaseComObject(rootFolder);
  1195                     rootFolder = null;
  1196                 }
  1197 
  1198                 if (sentFolder != null)
  1199                 {
  1200                     // Marshal.ReleaseComObject(sentFolder);
  1201                     sentFolder = null;
  1202                 }
  1203 
  1204                 if (folders != null)
  1205                 {
  1206                     // Marshal.ReleaseComObject(folders);
  1207                     folders = null;
  1208                 }
  1209 
  1210                 if (store != null)
  1211                 {
  1212                     // Marshal.ReleaseComObject(store);
  1213                     store = null;
  1214                 }
  1215             }
  1216 
  1217             return (result);
  1218         }
  1219 
  1220         /// <summary>
  1221         /// Gets whether the mail item is considered secure.
  1222         /// </summary>
  1223         /// <param name="omi">The Outlook mail item to process with.</param>
  1224         /// <returns>True if the mail item is secure, otherwise false.</returns>
  1225         public static bool GetIsSecure(this Outlook.MailItem omi)
  1226         {
  1227             bool isSecure = false;
  1228 
  1229             try
  1230             {
  1231                 // PGP/MIME format
  1232                 if (omi?.GetIsPGPMIMEEncrypted() == true)
  1233                 {
  1234                     isSecure = true;
  1235                 }
  1236                 else if (omi?.EntryID == null)
  1237                 {
  1238                     // OUT-260 To prevent accesing omi.Body by newly composed mails
  1239                     Log.Verbose("GetIsSecure: EntryID is null. Returning false.");
  1240                     isSecure = false;
  1241                 }
  1242                 else if (omi?.Body != null && PEPMessage.IsPGPText(omi?.Body))
  1243                 {
  1244                     // Partitioned or inline PGP format
  1245                     isSecure = true;
  1246                 }
  1247             }
  1248             catch (Exception ex)
  1249             {
  1250                 isSecure = false;
  1251                 Log.Error("GetIsSecure: Error determining whether item is secure. Returning default (false). " + ex.ToString());
  1252             }
  1253 
  1254             return isSecure;
  1255         }
  1256 
  1257         /// <summary>
  1258         /// Determines if the given mail item is encrypted in PGP/MIME format.
  1259         /// </summary>
  1260         /// <param name="omi">The mail item to check encryption for.</param>
  1261         /// <returns>True if the given mail item is PGP/MIME encrypted, otherwise false.</returns>
  1262         public static bool GetIsPGPMIMEEncrypted(this Outlook.MailItem omi)
  1263         {
  1264             bool result = false;
  1265             bool versionInfoFound = false;
  1266             bool contentFound = false;
  1267             PEPAttachment attach;
  1268             Outlook.Attachment attachment = null;
  1269             Outlook.Attachments attachments = null;
  1270 
  1271             try
  1272             {
  1273                 if (omi != null)
  1274                 {
  1275                     attachments = omi.Attachments;
  1276 
  1277                     // Require only two attachments (version identification & encrypted content)
  1278                     // However, allow the attachments to be in any order
  1279                     if (attachments.Count == 2)
  1280                     {
  1281                         // Note: attachment index starts at 1
  1282                         for (int i = 1; i <= attachments.Count; i++)
  1283                         {
  1284                             attachment = attachments[i];
  1285                             attach = new PEPAttachment(attachment);
  1286 
  1287                             if (attach.IsPGPMIMEVersionInfoFormat)
  1288                             {
  1289                                 versionInfoFound = true;
  1290                             }
  1291                             else if (attach.IsPGPMIMEContentFormat)
  1292                             {
  1293                                 contentFound = true;
  1294                             }
  1295 
  1296                             attachment = null;
  1297                         }
  1298 
  1299                         if (versionInfoFound && contentFound)
  1300                         {
  1301                             result = true;
  1302                         }
  1303                     }
  1304                 }
  1305             }
  1306             catch (Exception ex)
  1307             {
  1308                 Log.Error("GetIsPGPMIMEEncrypted: Error. " + ex.ToString());
  1309             }
  1310             finally
  1311             {
  1312                 attachment = null;
  1313                 attachments = null;
  1314             }
  1315 
  1316             return (result);
  1317         }
  1318 
  1319         /// <summary>
  1320         /// Determines if the given Outlook mail item should be securely stored (use a mirror).
  1321         /// This will check: IsInSecureStore, IsSecure, IsMirror &amp; NeverUnsecure.
  1322         /// </summary>
  1323         /// <param name="omi">The Outlook mail item to process with.</param>
  1324         /// <returns>True if the Outlook mail item should be stored securely, otherwise false.</returns>
  1325         public static bool GetIsSecurelyStored(this Outlook.MailItem omi)
  1326         {
  1327             if (omi != null)
  1328             {
  1329                 if ((omi.GetNeverUnsecure() && (omi.GetIsMirror() == false)) ||
  1330                     (omi.GetIsInSecureStore() && (omi.GetIsSecure() || omi.GetIsForcefullyProtected())))
  1331                 {
  1332                     return (true);
  1333                 }
  1334             }
  1335 
  1336             return (false);
  1337         }
  1338 
  1339         /// <summary>
  1340         /// Determines whether this mail item should be processed during sending. This checks
  1341         /// if pEp is enabled and whether the force unencrypted, force protection or enable protection 
  1342         /// properties are being set.
  1343         /// </summary>
  1344         /// <param name="omi">The Outlook mail item to process with.</param>
  1345         /// <returns>True if the mail item should be processed, otherwise false.</returns>
  1346         public static bool GetIsSendProcessingEnabled(this Outlook.MailItem omi)
  1347         {
  1348             bool isEnabled = true;
  1349 
  1350             if (omi != null)
  1351             {
  1352                 try
  1353                 {
  1354                     // If pEp is disabled, process in case of ForceProtected or EnableProtection
  1355                     if (omi.GetIsPEPEnabled() == false)
  1356                     {
  1357                         isEnabled = (omi.GetIsForcefullyProtected() || omi.GetEnableProtection());
  1358                     }
  1359                 }
  1360                 catch (Exception ex)
  1361                 {
  1362                     Log.Error("IsProcessingEnabled: Error occured. " + ex.ToString());
  1363                 }
  1364             }
  1365 
  1366             return isEnabled;
  1367         }
  1368 
  1369         /// <summary>
  1370         /// Determines a mail item's maximum allowable size in kB for sending through the mail item's current store.
  1371         /// </summary>
  1372         /// <param name="omi">The Outlook mail item to process with.</param>
  1373         /// <returns>The maximum allowed size in kB.</returns>
  1374         public static int GetMaxMailSize(this Outlook.MailItem omi)
  1375         {
  1376             int maxFileSize = 0;
  1377             Outlook.Folder folder = null;
  1378             Outlook.Store store = null;
  1379 
  1380             try
  1381             {
  1382                 folder = (Outlook.Folder)omi.Parent;
  1383                 store = folder.Store;
  1384                 maxFileSize = store.GetMaxSubmitSize();
  1385             }
  1386             catch (Exception ex)
  1387             {
  1388                 maxFileSize = 0;
  1389                 Log.Error("GetMaxMailSize: failure occured, returning default. " + ex.ToString());
  1390             }
  1391             finally
  1392             {
  1393                 folder = null;
  1394                 store = null;
  1395             }
  1396 
  1397             return maxFileSize;
  1398         }
  1399 
  1400         /// <summary>
  1401         /// Gets the pEp store folder used to store the mirror for the given Outlook mail item.
  1402         /// This will be determined based on the from user name.
  1403         /// The UNKNOWN_SENDER folder is used if a failure occurs in finding the from user name.
  1404         /// </summary>
  1405         /// <param name="omi">The Outlook mail item to process with.</param>
  1406         /// <returns>The Outlook folder to store the mirror of the mail item.</returns>
  1407         public static Outlook.Folder GetMirrorFolder(this Outlook.MailItem omi)
  1408         {
  1409             string userName;
  1410             string[] specialChars;
  1411             Outlook.Folder folder;
  1412             Outlook.Folders folders = Globals.ThisAddIn.PEPStoreRootFolder.Folders;
  1413             Globals.ReturnStatus sts;
  1414             StringBuilder strBuilder = new StringBuilder();
  1415 
  1416             sts = PEPIdentity.GetFromUserName(omi, out userName);
  1417 
  1418             if (sts == Globals.ReturnStatus.Success)
  1419             {
  1420                 /* Remove special characters from folder name.
  1421                  * See: https://msdn.microsoft.com/en-us/library/aa493942(v=exchg.80).aspx
  1422                  */
  1423                 specialChars = new string[] { "[", "]", "/", "\\", "&", "~", "?", "*", "|", "<", ">", "\"", ";", ":", "+" };
  1424                 if (userName != null)
  1425                 {
  1426                     strBuilder.Append(userName);
  1427                     for (int i = 0; i < specialChars.Length; i++)
  1428                     {
  1429                         strBuilder.Replace(specialChars[i], "");
  1430                     }
  1431                     userName = strBuilder.ToString();
  1432                 }
  1433 
  1434                 // Use unknown if invalid
  1435                 if (string.IsNullOrWhiteSpace(userName))
  1436                 {
  1437                     userName = MailItemExtensions.UNKNOWN_SENDER;
  1438                 }
  1439             }
  1440             else
  1441             {
  1442                 userName = MailItemExtensions.UNKNOWN_SENDER;
  1443             }
  1444 
  1445             try
  1446             {
  1447                 folder = (Outlook.Folder)folders[userName];
  1448                 Log.Verbose("GetUnencryptedFolder: Using existing folder.");
  1449             }
  1450             catch
  1451             {
  1452                 folder = (Outlook.Folder)folders.Add(userName);
  1453                 Log.Verbose("GetUnencryptedFolder: Creating new folder.");
  1454             }
  1455 
  1456             // Release objects
  1457             if (folders != null)
  1458             {
  1459                 // Marshal.ReleaseComObject(folders);
  1460                 folders = null;
  1461             }
  1462 
  1463             return (folder);
  1464         }
  1465 
  1466         /// <summary>
  1467         /// Gets whether a given EntryID is in the list of copied items.
  1468         /// </summary>
  1469         /// <param name="entryId">The EntryID to check for.</param>
  1470         /// <returns>True, if in the list. Otherwise false.</returns>
  1471         public static bool IsInCopiedItemsList(string entryId)
  1472         {
  1473             bool isInList = false;
  1474 
  1475             lock (mutexCopiedItemsList)
  1476             {
  1477                 if (copiedItemsList.Contains(entryId))
  1478                 {
  1479                     isInList = true;
  1480                 }
  1481             }
  1482 
  1483             return isInList;
  1484         }
  1485 
  1486         /// <summary>
  1487         /// Permanently deletes the mail item from Outlook.
  1488         /// This will also remove any mail item data before save.
  1489         /// Therefore, it is safe for handling sensitive data.
  1490         /// </summary>
  1491         /// <param name="mailItem">The Outlook mail item to process with.</param>
  1492         /// <param name="mailStore">The known store containing the mail item being deleted.
  1493         /// This is used to locate the deleted folder. Leaving empty or setting to null will
  1494         /// automatically try to find the store using the mail item itself.</param>
  1495         public static void PermanentlyDelete(this Outlook.MailItem mailItem,
  1496                                              Outlook.Store mailStore = null)
  1497         {
  1498             bool deleted = false;
  1499             string deleteValue;
  1500             Outlook.Folder folder = null;
  1501             Outlook.Folder deletedFolder = null;
  1502             Outlook.MailItem deletedMailItem = null;
  1503             Outlook.Attachments attachments = null;
  1504             Outlook.Recipients recipients = null;
  1505             Outlook.Store store = null;
  1506             Outlook.Items items = null;
  1507 
  1508             try
  1509             {
  1510                 /* Remove all relevant data from the mail item
  1511                  * This is done so it is not saved later where it could be unencrypted
  1512                  */
  1513                 recipients = mailItem.Recipients;
  1514                 while (recipients.Count > 0)
  1515                 {
  1516                     recipients.Remove(1);
  1517                 }
  1518 
  1519                 mailItem.Subject = "";
  1520                 mailItem.BodyFormat = Outlook.OlBodyFormat.olFormatPlain;
  1521                 mailItem.Body = "";
  1522                 mailItem.HTMLBody = "";
  1523 
  1524                 attachments = mailItem.Attachments;
  1525                 while (attachments.Count > 0)
  1526                 {
  1527                     attachments.Remove(1);
  1528                 }
  1529 
  1530                 // Add mail
  1531                 try
  1532                 {
  1533                     mailItem.Save();
  1534                 }
  1535                 catch (Exception ex)
  1536                 {
  1537                     Log.Error("PermanentlyDelete: Error saving mail item. " + ex.ToString());
  1538                 }
  1539 
  1540                 // Locate the store for the mailitem if none was given
  1541                 if (mailStore == null)
  1542                 {
  1543                     try
  1544                     {
  1545                         folder = (Outlook.Folder)mailItem.Parent;
  1546                         store = folder.Store;
  1547                     }
  1548                     catch (Exception ex)
  1549                     {
  1550                         Log.Error("PermanentlyDelete: unable to locate parent store, " + ex.ToString());
  1551                     }
  1552                     finally
  1553                     {
  1554                         // Release objects
  1555                         if (folder != null)
  1556                         {
  1557                             // Marshal.ReleaseComObject(folder);
  1558                             folder = null;
  1559                         }
  1560                     }
  1561                 }
  1562 
  1563                 /* Move the mail item to the deleted folder then delete (otherwise calling .Delete() will only move to deleted items)
  1564                  * For ActiveSync accounts, the .Move() call will fail with the error:
  1565                  * 'Sorry, Exchange ActiveSync doesn't support what you're trying to do.'
  1566                  */
  1567                 try
  1568                 {
  1569                     if (mailStore != null)
  1570                     {
  1571                         // Use given store
  1572                         deletedFolder = (Outlook.Folder)mailStore.GetDefaultFolder(Outlook.OlDefaultFolders.olFolderDeletedItems);
  1573                     }
  1574                     else
  1575                     {
  1576                         // Use calculated store
  1577                         deletedFolder = (Outlook.Folder)store.GetDefaultFolder(Outlook.OlDefaultFolders.olFolderDeletedItems);
  1578                     }
  1579                     deletedMailItem = (Outlook.MailItem)mailItem.Move(deletedFolder);
  1580 
  1581                     /* Change the subject and Save
  1582                      * If the subject (or another property) is not changed the .Save() call will be ignored
  1583                      * Calling .Save() is for some reason necessary before .Delete() to work properly
  1584                      */
  1585                     deletedMailItem.Subject = "pEp";
  1586                     try
  1587                     {
  1588                         deletedMailItem.Save();
  1589                     }
  1590                     catch (Exception ex)
  1591                     {
  1592                         Log.Error("PermanentlyDelete: Error saving deleted mail item. " + ex.ToString());
  1593                     }
  1594 
  1595                     // Now that the mail item is in the deleted folder, calling .Delete() will delete permanently
  1596                     deletedMailItem.Delete();
  1597                     deleted = true;
  1598                 }
  1599                 catch (Exception ex)
  1600                 {
  1601                     Log.Error("PermanentlyDelete: Error deleting. " + ex.ToString());
  1602                 }
  1603                 finally
  1604                 {
  1605                     if (deletedFolder != null)
  1606                     {
  1607                         // Marshal.ReleaseComObject(deletedFolder);
  1608                         deletedFolder = null;
  1609                     }
  1610 
  1611                     if (deletedMailItem != null)
  1612                     {
  1613                         // Marshal.ReleaseComObject(deletedMailItem);
  1614                         deletedMailItem = null;
  1615                     }
  1616                 }
  1617 
  1618                 // Try secondary method supporting ActiveSync accounts
  1619                 if (deleted == false)
  1620                 {
  1621                     if (mailStore != null)
  1622                     {
  1623                         // Use given store
  1624                         deletedFolder = (Outlook.Folder)mailStore.GetDefaultFolder(Outlook.OlDefaultFolders.olFolderDeletedItems);
  1625                     }
  1626                     else
  1627                     {
  1628                         // Use calculated store
  1629                         deletedFolder = (Outlook.Folder)store.GetDefaultFolder(Outlook.OlDefaultFolders.olFolderDeletedItems);
  1630                     }
  1631 
  1632                     // Tag the item with a user property so it can be located in the deleted folder
  1633                     deleteValue = "DELETE_" + mailItem.EntryID;
  1634                     mailItem.SetUserProperty("DELETE", deleteValue, Outlook.OlUserPropertyType.olText);
  1635                     try
  1636                     {
  1637                         mailItem.Save();
  1638                     }
  1639                     catch (Exception ex)
  1640                     {
  1641                         Log.Error("PermanentlyDelete: Error saving mail item. " + ex.ToString());
  1642                     }
  1643 
  1644                     // Delete the mail item moving to deleted items folder
  1645                     mailItem.Delete();
  1646 
  1647                     // Permanently delete item by locating in the delete items folder
  1648                     items = deletedFolder.Items;
  1649                     for (int i = 1; i <= items.Count; i++)
  1650                     {
  1651                         deletedMailItem = items[i];
  1652 
  1653                         // Check if the property exists
  1654                         if ((string)deletedMailItem.GetUserProperty("DELETE", "") == deleteValue)
  1655                         {
  1656                             deletedMailItem.Delete();
  1657                             deleted = true;
  1658                             break;
  1659                         }
  1660 
  1661                         // Marshal.ReleaseComObject(deletedMailItem);
  1662                         deletedMailItem = null;
  1663                     }
  1664                 }
  1665             }
  1666             catch (Exception ex)
  1667             {
  1668                 Log.Error("PermanentlyDelete: Failed, " + ex.ToString());
  1669             }
  1670             finally
  1671             {
  1672                 if (folder != null)
  1673                 {
  1674                     // Marshal.ReleaseComObject(folder);
  1675                     folder = null;
  1676                 }
  1677 
  1678                 if (deletedFolder != null)
  1679                 {
  1680                     // Marshal.ReleaseComObject(deletedFolder);
  1681                     deletedFolder = null;
  1682                 }
  1683 
  1684                 if (deletedMailItem != null)
  1685                 {
  1686                     // Marshal.ReleaseComObject(deletedMailItem);
  1687                     deletedMailItem = null;
  1688                 }
  1689 
  1690                 if (attachments != null)
  1691                 {
  1692                     // Marshal.ReleaseComObject(attachments);
  1693                     attachments = null;
  1694                 }
  1695 
  1696                 if (recipients != null)
  1697                 {
  1698                     // Marshal.ReleaseComObject(recipients);
  1699                     recipients = null;
  1700                 }
  1701 
  1702                 if (store != null)
  1703                 {
  1704                     // Marshal.ReleaseComObject(store);
  1705                     store = null;
  1706                 }
  1707 
  1708                 if (items != null)
  1709                 {
  1710                     // Marshal.ReleaseComObject(items);
  1711                     items = null;
  1712                 }
  1713             }
  1714 
  1715             return;
  1716         }
  1717 
  1718         /// <summary>
  1719         /// Gets the own pEp identity for the SendUsingAccount of the MailItem.
  1720         /// Warning: This can return null.
  1721         /// </summary>
  1722         /// <param name="omi">The Outlook mail item to process with.</param>
  1723         /// <returns>The own identity for the send using account, otherwise null.</returns>
  1724         public static PEPIdentity GetSendUsingAccountIdentity(this Outlook.MailItem omi)
  1725         {
  1726             PEPIdentity ident = null;
  1727             Outlook.Account account = null;
  1728             Globals.ReturnStatus sts;
  1729 
  1730             try
  1731             {
  1732                 account = omi.SendUsingAccount;
  1733 
  1734                 // Ignore any failures
  1735                 sts = PEPIdentity.GetOwnIdentity(account, out ident);
  1736             }
  1737             catch (Exception ex)
  1738             {
  1739                 Log.Error("GetSendUsingAccountIdentity: Failure occured, " + ex.ToString());
  1740             }
  1741             finally
  1742             {
  1743                 if (account != null)
  1744                 {
  1745                     // Marshal.ReleaseComObject(account);
  1746                     account = null;
  1747                 }
  1748             }
  1749 
  1750             return (ident);
  1751         }
  1752 
  1753         /// <summary>
  1754         /// Creates a message container for the given Outlook mail item that represents its data
  1755         /// and how it is stored/located within Outlook.
  1756         /// Warning: This method can take some time (especially if a mirror is being located).
  1757         /// </summary>
  1758         /// <param name="omi">The Outlook mail item to process with.</param>
  1759         /// <returns>The new message container, otherwise null.</returns>
  1760         public static MsgContainer ToMsgContainer(this Outlook.MailItem omi)
  1761         {
  1762             PEPMessage newMessage;
  1763             MsgContainer msgCont = new MsgContainer();
  1764             Globals.ReturnStatus sts;
  1765             Outlook.Folder folder = null;
  1766             Outlook.MailItem mirrorMailItem = null;
  1767             Outlook.Store store = null;
  1768 
  1769             Log.Verbose("ToMsgContainer: Started");
  1770 
  1771             try
  1772             {
  1773                 // Create Message
  1774                 sts = PEPMessage.Create(omi, out newMessage);
  1775                 msgCont.Message = newMessage;
  1776                 msgCont.MessageCreationStatus = sts;
  1777 
  1778                 // Create Mirror
  1779                 if (omi.GetIsSecurelyStored())
  1780                 {
  1781                     mirrorMailItem = omi.GetMirror();
  1782                     if (mirrorMailItem != null)
  1783                     {
  1784                         sts = PEPMessage.Create(mirrorMailItem, out newMessage);
  1785                         msgCont.Mirror = newMessage;
  1786                         msgCont.MirrorCreationStatus = sts;
  1787                     }
  1788                 }
  1789 
  1790                 // Get the store ID
  1791                 try
  1792                 {
  1793                     folder = (Outlook.Folder)omi.Parent;
  1794                     store = folder.Store;
  1795                     msgCont.StoreId = store.StoreID;
  1796                 }
  1797                 catch
  1798                 {
  1799                     msgCont.StoreId = null;
  1800                 }
  1801 
  1802                 // Get other Outlook store/location information
  1803                 msgCont.EntryId = omi.EntryID;
  1804                 msgCont.IsDraft = omi.GetIsDraft();
  1805                 msgCont.IsInSecureStore = omi.GetIsInSecureStore();
  1806                 msgCont.IsInSentFolder = omi.GetIsInSentFolder();
  1807                 msgCont.IsMirror = omi.GetIsMirror();
  1808             }
  1809             catch (Exception ex)
  1810             {
  1811                 Log.Error("ToMsgContainer: Error occured, " + ex.ToString());
  1812             }
  1813             finally
  1814             {
  1815                 if (folder != null)
  1816                 {
  1817                     // Marshal.ReleaseComObject(folder);
  1818                     folder = null;
  1819                 }
  1820 
  1821                 if (mirrorMailItem != null)
  1822                 {
  1823                     // Marshal.ReleaseComObject(mirrorMailItem);
  1824                     mirrorMailItem = null;
  1825                 }
  1826 
  1827                 if (store != null)
  1828                 {
  1829                     // Marshal.ReleaseComObject(store);
  1830                     store = null;
  1831                 }
  1832             }
  1833 
  1834             Log.Verbose("ToMsgContainer: Completed");
  1835 
  1836             return (msgCont);
  1837         }
  1838 
  1839         /// <summary>
  1840         /// Saves this Outlook mail item as plain-text (eml) message file.
  1841         /// If no file path is given, the message is stored with a randomized
  1842         /// file name in the Temp directory.
  1843         /// </summary>
  1844         /// <param name="omi">The Outlook mail item to process with.</param>
  1845         /// <param name="fileName">The file path where to store the item.</param>
  1846         /// <returns>The file path where the message was saved or null if an error occured.</returns>
  1847         public static string SaveAsEml(this Outlook.MailItem omi, string fileName = null)
  1848         {
  1849             MimeMessage message;
  1850 
  1851             // If no file name was given, create one. Else, validate file name.
  1852             if (fileName == null)
  1853             {
  1854                 // Create a randomized file name for the attachment
  1855                 Random random = new Random();
  1856                 string tempPath = Path.GetTempPath();
  1857                 fileName = tempPath + "message" + random.Next(10000) + ".eml";
  1858                 while (File.Exists(fileName))
  1859                 {
  1860                     fileName = tempPath + "message" + random.Next(10000) + ".eml";
  1861                 }
  1862             }
  1863             else if (fileName.EndsWith(".eml") == false)
  1864             {
  1865                 fileName = null;
  1866                 Log.Error("SaveAsEml: File name has to have EML file ending.");
  1867             }
  1868             else
  1869             {
  1870                 FileInfo fi = null;
  1871                 try
  1872                 {
  1873                     fi = new FileInfo(fileName);
  1874                 }
  1875                 catch (Exception ex)
  1876                 {
  1877                     fileName = null;
  1878                     Log.Error("SaveAsEml: Error in file name. " + ex.ToString());
  1879                 }
  1880             }
  1881 
  1882             // If file path is valid, save mail item and return file path
  1883             if (string.IsNullOrEmpty(fileName) == false)
  1884             {
  1885                 try
  1886                 {
  1887                     message = omi.ToMIMEMessage();
  1888                     message.WriteTo(fileName);
  1889                 }
  1890                 catch (Exception ex)
  1891                 {
  1892                     fileName = null;
  1893                     Log.Error("SaveAsEml: Error saving mail item to EML file. " + ex.ToString());
  1894                 }
  1895             }
  1896 
  1897             return fileName;
  1898         }
  1899 
  1900         /// <summary>
  1901         /// Converts this Outlook mail item into a MimeMessage.
  1902         /// </summary>
  1903         /// <param name="omi">The Outlook mail item to process with.</param>
  1904         /// <returns>The created MimeMessage or null if an error occured.</returns>
  1905         public static MimeMessage ToMIMEMessage(this Outlook.MailItem omi)
  1906         {
  1907             MimeMessage mimeMessage = null;
  1908             BodyBuilder bodyBuilder = new BodyBuilder();
  1909             PEPMessage pEpMessage = null;
  1910 
  1911             // Create a PEPMessage
  1912             if (PEPMessage.Create(omi, out pEpMessage) == Globals.ReturnStatus.Success)
  1913             {
  1914                 MimeMessage message;
  1915                 if (PEPMessage.ToMIMEMessage(pEpMessage, out message) == Globals.ReturnStatus.Success)
  1916                 {
  1917                     mimeMessage = message;
  1918                 }
  1919                 else
  1920                 {
  1921                     Log.Error("MailItemExtensions.ToMIMEMessage: Error creating MimeMessage.");
  1922                 }
  1923             }
  1924             else
  1925             {
  1926                 Log.Error("MailItemExtensions.ToMIMEMessage: Error creating PEPMessage.");
  1927             }
  1928 
  1929             return mimeMessage;
  1930         }
  1931 
  1932         /**************************************************************
  1933          * 
  1934          * User/MAPI Property Extensions
  1935          * 
  1936          *************************************************************/
  1937 
  1938         /// <summary>
  1939         /// Sets the user property value of the mail item.
  1940         /// This will create the user property of the given type if it doesn't already exist.
  1941         /// Warning: this will not save the mail item.
  1942         /// </summary>
  1943         /// <param name="omi">The Outlook mail item to process with.</param>
  1944         /// <param name="name">The name of the user property.</param>
  1945         /// <param name="value">The new value of the user property.</param>
  1946         /// <param name="type">The type of the user property.</param>
  1947         public static void SetUserProperty(this Outlook.MailItem omi,
  1948                                            string name,
  1949                                            object value,
  1950                                            Outlook.OlUserPropertyType type = Outlook.OlUserPropertyType.olText)
  1951         {
  1952             Outlook.UserProperties properties;
  1953             Outlook.UserProperty up;
  1954 
  1955             if ((omi != null) &&
  1956                 (string.IsNullOrEmpty(name) == false))
  1957             {
  1958                 properties = omi.UserProperties;
  1959                 up = properties.Find(name);
  1960 
  1961                 if (up == null)
  1962                 {
  1963                     /* The .Add function has the following parameters:
  1964                      * 
  1965                      *              Name : The name of the property. 
  1966                      *                     The maximum length is 64 characters. 
  1967                      *                     The characters, '[', ']', '_' and '#', are not permitted in the name.
  1968                      *              Type : The type of the new property.
  1969                      * [Optional]
  1970                      * AddToFolderFields : True if the property will be added as a custom field to the 
  1971                      *                     folder that the item is in. This field can be displayed in 
  1972                      *                     the folder's view. False if the property will be added as a 
  1973                      *                     custom field to the item but not to the folder. 
  1974                      *                     The default value is True.
  1975                      * [Optional]
  1976                      *     DisplayFormat : Specifies how the property will be displayed in the Outlook user interface. 
  1977                      * 
  1978                      * It is very important that the AddToFolderFields parameter is FALSE.
  1979                      * This is opposite of the default.
  1980                      * Setting to false will attach the user property to the mail item itself.
  1981                      * See: https://msdn.microsoft.com/en-us/library/office/ff867389.aspx
  1982                      */
  1983                     up = properties.Add(name, type, false);
  1984                 }
  1985 
  1986                 up.Value = value;
  1987 
  1988                 // Release objects
  1989                 if (properties != null)
  1990                 {
  1991                     // Marshal.ReleaseComObject(properties);
  1992                     properties = null;
  1993                 }
  1994 
  1995                 if (up != null)
  1996                 {
  1997                     // Marshal.ReleaseComObject(up);
  1998                     up = null;
  1999                 }
  2000             }
  2001 
  2002             return;
  2003         }
  2004 
  2005         /// <summary>
  2006         /// Gets the user property value in the mail item.
  2007         /// </summary>
  2008         /// <param name="omi">The Outlook mail item to process with.</param>
  2009         /// <param name="name">The name of the user property.</param>
  2010         /// <param name="defaultValue">The default value of the user property to return if not found.</param>
  2011         /// <returns>The value of the user property if found, otherwise defaultValue.</returns>
  2012         public static object GetUserProperty(this Outlook.MailItem omi,
  2013                                              string name,
  2014                                              object defaultValue = null)
  2015         {
  2016             object value = null;
  2017             Outlook.UserProperties properties = null;
  2018             Outlook.UserProperty up = null;
  2019 
  2020             if ((omi != null) &&
  2021                 (string.IsNullOrEmpty(name) == false))
  2022             {
  2023                 /* This try/catch block is necessary, as the following lines showed to raise an exception of type
  2024                  * "The item has been moved or deleted" (even after the null check). See OUT-256
  2025                  */
  2026                 try
  2027                 {
  2028                     properties = omi.UserProperties;
  2029                     up = properties.Find(name);
  2030                 }
  2031                 catch (Exception ex)
  2032                 {
  2033                     Log.Verbose("MailItemExtensions.GetUserProperty: Error getting user properties. " + ex.ToString());
  2034                     up = null;
  2035                 }
  2036 
  2037                 if (up == null)
  2038                 {
  2039                     value = defaultValue;
  2040                 }
  2041                 else
  2042                 {
  2043                     value = up.Value;
  2044                 }
  2045 
  2046                 // Release objects
  2047                 if (properties != null)
  2048                 {
  2049                     // Marshal.ReleaseComObject(properties);
  2050                     properties = null;
  2051                 }
  2052 
  2053                 if (up != null)
  2054                 {
  2055                     // Marshal.ReleaseComObject(up);
  2056                     up = null;
  2057                 }
  2058             }
  2059 
  2060             return (value);
  2061         }
  2062 
  2063         /// <summary>
  2064         /// Deletes the user property from the mail item (if it exists).
  2065         /// Warning: this will not save the mail item.
  2066         /// </summary>
  2067         /// <param name="omi">The Outlook mail item to process with.</param>
  2068         /// <param name="name">The name of the user property to delete.</param>
  2069         public static void DeleteUserProperty(this Outlook.MailItem omi,
  2070                                               string name)
  2071         {
  2072             Outlook.UserProperties properties;
  2073             Outlook.UserProperty up;
  2074 
  2075             if ((omi != null) &&
  2076                 (string.IsNullOrEmpty(name) == false))
  2077             {
  2078                 properties = omi.UserProperties;
  2079                 up = properties.Find(name);
  2080 
  2081                 if (up != null)
  2082                 {
  2083                     up.Delete();
  2084                 }
  2085 
  2086                 // Release objects
  2087                 if (properties != null)
  2088                 {
  2089                     // Marshal.ReleaseComObject(properties);
  2090                     properties = null;
  2091                 }
  2092 
  2093                 if (up != null)
  2094                 {
  2095                     // Marshal.ReleaseComObject(up);
  2096                     up = null;
  2097                 }
  2098             }
  2099 
  2100             return;
  2101         }
  2102 
  2103         /// <summary>
  2104         /// Gets the outgoing rating for this mail item.
  2105         /// </summary>
  2106         /// <param name="omi">The Outlook mail item to process with.</param>
  2107         /// <returns>The outgoing rating for the mail item.</returns>
  2108         public static pEpRating GetOutgoingRating(this Outlook.MailItem omi)
  2109         {
  2110             pEpRating rating = pEpRating.pEpRatingUndefined;
  2111 
  2112 #if READER_RELEASE_MODE
  2113             // If reader mode, always unencrypted
  2114             return (pEpRating.pEpRatingUnencrypted);
  2115 #else
  2116             // If mail has no recipients, return undefined
  2117             if (omi?.Recipients?.Count == 0)
  2118             {
  2119                 return pEpRating.pEpRatingUndefined;
  2120             }
  2121 
  2122             // If mail is forcefully protected, return reliable
  2123             if (omi?.GetIsForcefullyProtected() == true)
  2124             {
  2125                 return pEpRating.pEpRatingReliable;
  2126             }
  2127 
  2128             // If mail is forcefully unencrypted, return unencrypted
  2129             if (omi?.GetIsForceUnencrypted() == true)
  2130             {
  2131                 return pEpRating.pEpRatingUnencrypted;
  2132             }
  2133 
  2134             // If mail has BCC recipients, always unencrypted
  2135             if (string.IsNullOrEmpty(omi?.BCC) == false)
  2136             {
  2137                 return (pEpRating.pEpRatingUnencrypted);
  2138             }
  2139 
  2140             // Else calculate it
  2141             PEPMessage message;
  2142             if (PEPMessage.Create(omi, out message, true, false) == Globals.ReturnStatus.Success)
  2143             {
  2144                 rating = message?.OutgoingRating ?? pEpRating.pEpRatingUndefined;
  2145             }
  2146             else
  2147             {
  2148                 rating = pEpRating.pEpRatingUndefined;
  2149                 Log.Error("MailItemExtensions.GetOutgoingRating: Error creating PEPMessage. Returning default rating.");
  2150             }
  2151 #endif
  2152             return rating;
  2153         }
  2154 
  2155         /// <summary>
  2156         /// Gets the parsed transport message headers (MIME headers) of the given Outlook mail item.
  2157         /// This will internally use the MAPI property PidTagTransportMessageHeaders.
  2158         /// Warning: This can return null.
  2159         /// </summary>
  2160         /// <param name="omi">The Outlook mail item to process with.</param>
  2161         /// <returns>The parsed transport message headers, otherwise null.</returns>
  2162         public static HeaderList GetParsedTransportMessageHeaders(this Outlook.MailItem omi)
  2163         {
  2164             string messageHeaders = null;
  2165             MemoryStream stream = null;
  2166             MimeParser mimeParser;
  2167             HeaderList headers = null;
  2168 
  2169             if (omi != null)
  2170             {
  2171                 messageHeaders = MapiHelper.GetProperty(omi, MapiProperty.PidTagTransportMessageHeaders, string.Empty) as string;
  2172 
  2173                 if (string.IsNullOrEmpty(messageHeaders) == false)
  2174                 {
  2175                     // Parse the headers using MimeKit
  2176                     try
  2177                     {
  2178                         stream = new MemoryStream(Encoding.UTF8.GetBytes(messageHeaders));
  2179                         mimeParser = new MimeParser(stream);
  2180                         headers = mimeParser.ParseHeaders();
  2181                     }
  2182                     catch (Exception ex)
  2183                     {
  2184                         Log.Error("GetParsedTransportMessageHeaders: Failed to parse MIME headers, " + ex.ToString());
  2185                     }
  2186                     finally
  2187                     {
  2188                         if (stream != null)
  2189                         {
  2190                             stream.Close();
  2191                             stream = null;
  2192                         }
  2193                     }
  2194                 }
  2195             }
  2196 
  2197             return (headers);
  2198         }
  2199 
  2200         /// <summary>
  2201         /// Gets the default value for the given UserProperty or MAPI property.
  2202         /// Warning: The propertyName for a UserProperty or MAPI property must never be the same.
  2203         /// </summary>
  2204         /// <param name="property">The pEp defined property to get the default for.</param>
  2205         /// <param name="defaultValue">The default value to return if failure.</param>
  2206         /// <returns>The default value of the given property.</returns>
  2207         public static object GetPEPPropertyDefault(PEPProperty property)
  2208         {
  2209             object newValue = null;
  2210 
  2211             switch (property)
  2212             {
  2213                 case PEPProperty.AutoConsume:
  2214                     newValue = null;
  2215                     break;
  2216                 case PEPProperty.ForceProtection:
  2217                     newValue = null;
  2218                     break;
  2219                 case PEPProperty.ForceUnencrypted:
  2220                     newValue = false;
  2221                     break;
  2222                 case PEPProperty.KeyList:
  2223                     newValue = null;
  2224                     break;
  2225                 case PEPProperty.NeverUnsecure:
  2226                     newValue = false;
  2227                     break;
  2228                 case PEPProperty.PEPProtocolVersion:
  2229                     newValue = null;
  2230                     break;
  2231                 case PEPProperty.Rating:
  2232                     newValue = pEpRating.pEpRatingUndefined;
  2233                     break;
  2234                 case PEPProperty.EnableProtection:
  2235                     newValue = false;
  2236                     break;
  2237             }
  2238 
  2239             return (newValue);
  2240         }
  2241 
  2242         /// <summary>
  2243         /// Gets the value of either a UserProperty or MAPI property from the given Outlook mail item.
  2244         /// The value will be processed (interpreted) for immediate use elsewhere.
  2245         /// This method will automatically calculate a default value based on the given propertyName.
  2246         /// Warning: The propertyName for a UserProperty or MAPI property must never be the same.
  2247         /// </summary>
  2248         /// <param name="omi">The Outlook mail item to process with.</param>
  2249         /// <param name="property">The pEp defined property to get.</param>
  2250         /// <param name="value">The output value of the given property (will return calculated default if unsuccessful).</param>
  2251         /// <returns>True if successful, otherwise false.</returns>
  2252         public static bool GetPEPProperty(this Outlook.MailItem omi,
  2253                                           PEPProperty property,
  2254                                           out object value)
  2255         {
  2256             bool success;
  2257             object outValue;
  2258             object defaultValue;
  2259 
  2260             defaultValue = MailItemExtensions.GetPEPPropertyDefault(property);
  2261             success = omi.GetPEPProperty(property, out outValue, defaultValue, null);
  2262 
  2263             value = outValue;
  2264             return (success);
  2265         }
  2266 
  2267         /// <summary>
  2268         /// Gets the value of either a UserProperty or MAPI property from the given Outlook mail item.
  2269         /// The value will be processed (interpreted) for immediate use elsewhere.
  2270         /// Warning: The propertyName for a UserProperty or MAPI property must never be the same.
  2271         /// </summary>
  2272         /// <param name="omi">The Outlook mail item to process with.</param>
  2273         /// <param name="property">The pEp defined property to get.</param>
  2274         /// <param name="value">The output value of the given property (will return defaultValue if unsuccessful).</param>
  2275         /// <param name="defaultValue">The default value to return if failure.</param>
  2276         /// <param name="messageHeaders">The message headers of this message.</param>
  2277         /// <returns>True if successful, otherwise false.</returns>
  2278         public static bool GetPEPProperty(this Outlook.MailItem omi,
  2279                                           PEPProperty property,
  2280                                           out object value,
  2281                                           object defaultValue,
  2282                                           HeaderList messageHeaders)
  2283         {
  2284             bool success = false;
  2285             object propertyValue;
  2286             object outValue = defaultValue;
  2287 
  2288             try
  2289             {
  2290                 if (omi != null)
  2291                 {
  2292                     switch (property)
  2293                     {
  2294                         // Auto consume MAPI property
  2295                         case PEPProperty.AutoConsume:
  2296                             {
  2297                                 // Note: this value is passed through completely as a string
  2298                                 outValue = MailItemExtensions.GetPEPPropertyDefault(property);
  2299                                 propertyValue = MapiHelper.GetProperty(omi, PEPMessage.PidNamePEPAutoConsume, null);
  2300 
  2301                                 if (propertyValue != null)
  2302                                 {
  2303                                     outValue = propertyValue;
  2304                                 }
  2305                                 else
  2306                                 {
  2307                                     // As a backup search the original internet message headers   
  2308                                     if (messageHeaders == null)
  2309                                     {
  2310                                         messageHeaders = omi.GetParsedTransportMessageHeaders();
  2311                                     }
  2312                                     if (messageHeaders != null)
  2313                                     {
  2314                                         foreach (Header header in messageHeaders)
  2315                                         {
  2316                                             if ((header.Field != null) &&
  2317                                                 (header.Value != null) &&
  2318                                                 (string.Equals(header.Field.Trim(), PEPMessage.PR_PEP_AUTO_CONSUME_NAME, StringComparison.OrdinalIgnoreCase)))
  2319                                             {
  2320                                                 outValue = header.Value.Trim();
  2321                                             }
  2322                                         }
  2323                                     }
  2324                                 }
  2325 
  2326                                 success = true;
  2327                                 break;
  2328                             }
  2329                         // Force protection MAPI property
  2330                         case PEPProperty.ForceProtection:
  2331                             {
  2332                                 /* Note that ForceProtection is defined as:
  2333                                  *   ON : ForceProtection property exists with a value of a message GUID
  2334                                  *  OFF : ForceProtection property does not exist or has a value of null
  2335                                  */
  2336                                 outValue = MailItemExtensions.GetPEPPropertyDefault(property);
  2337                                 propertyValue = MapiHelper.GetProperty(omi, PEPMessage.PidNamePEPForceProtection, null);
  2338 
  2339                                 if (propertyValue != null)
  2340                                 {
  2341                                     outValue = propertyValue as string;
  2342                                 }
  2343                                 else
  2344                                 {
  2345                                     // As a backup search the original internet message headers
  2346                                     if (messageHeaders == null)
  2347                                     {
  2348                                         messageHeaders = omi.GetParsedTransportMessageHeaders();
  2349                                     }
  2350                                     if (messageHeaders != null)
  2351                                     {
  2352                                         foreach (Header header in messageHeaders)
  2353                                         {
  2354                                             if ((header.Field != null) &&
  2355                                                 (header.Value != null) &&
  2356                                                 (string.Equals(header.Field.Trim(), PEPMessage.PR_PEP_FORCE_PROTECTION_NAME, StringComparison.OrdinalIgnoreCase)))
  2357                                             {
  2358                                                 outValue = header.Value.ToString();
  2359                                             }
  2360                                         }
  2361                                     }
  2362                                 }
  2363 
  2364                                 success = true;
  2365                                 break;
  2366                             }
  2367                         // Force unencrypted user property
  2368                         case PEPProperty.ForceUnencrypted:
  2369                             {
  2370                                 /* Note that ForceUnencrypted is defined as:
  2371                                  *   ON : ForceUnencrypted property exists with a value of 'True'
  2372                                  *  OFF : ForceUnencrypted property does not exist or has a value of 'False' or null
  2373                                  */
  2374 
  2375                                 outValue = MailItemExtensions.GetPEPPropertyDefault(property);
  2376                                 propertyValue = omi.GetUserProperty(CryptableMailItem.USER_PROPERTY_KEY_FORCE_UNENCRYPTED);
  2377 
  2378                                 if ((propertyValue != null) &&
  2379                                     (propertyValue is bool) &&
  2380                                     ((bool)propertyValue == true))
  2381                                 {
  2382                                     outValue = true;
  2383                                 }
  2384 
  2385                                 success = true;
  2386                                 break;
  2387                             }
  2388                         // Key list MAPI property
  2389                         case PEPProperty.KeyList:
  2390                             {
  2391                                 // Note: this value is passed through completely as a string
  2392                                 outValue = MapiHelper.GetProperty(omi, PEPMessage.PidNameKeyList, MailItemExtensions.GetPEPPropertyDefault(property));
  2393                                 success = true;
  2394                                 break;
  2395                             }
  2396                         // Never unsecure MAPI property
  2397                         case PEPProperty.NeverUnsecure:
  2398                             {
  2399                                 /* Note that NeverUnsecure is defined as:
  2400                                  *   ON : NeverUnsecure property exists with ANY value ('1' is the default)
  2401                                  *  OFF : NeverUnsecure property does not exist or has a null value
  2402                                  */
  2403 
  2404                                 outValue = MailItemExtensions.GetPEPPropertyDefault(property);
  2405                                 propertyValue = MapiHelper.GetProperty(omi, PEPMessage.PidNamePEPNeverUnsecure, null);
  2406 
  2407                                 // Only check that the NeverUnsecure property exists, the value is ignored
  2408                                 if (propertyValue != null)
  2409                                 {
  2410                                     outValue = true;
  2411                                 }
  2412                                 else
  2413                                 {
  2414                                     // Fallback solution for when the property doesn't get set correctly as MAPI property in an Exchange
  2415                                     // server environment and therefore has to be retrieved directly from the message's internet headers.
  2416                                     // This is not ideal, as the fallback will only be needed for some edge cases, but the logic is run 
  2417                                     // several times for each mail item.
  2418                                     if (messageHeaders == null)
  2419                                     {
  2420                                         messageHeaders = omi.GetParsedTransportMessageHeaders();
  2421                                     }
  2422                                     if (messageHeaders != null)
  2423                                     {
  2424                                         foreach (Header header in messageHeaders)
  2425                                         {
  2426                                             if ((header.Field != null) &&
  2427                                                 (string.Equals(header.Field.Trim(), PEPMessage.PR_PEP_NEVER_UNSECURE_NAME, StringComparison.OrdinalIgnoreCase)))
  2428                                             {
  2429                                                 Log.Verbose("MailItemExtensions.GetPEPProperty: NeverUnsecure property retrieved using fallback solution.");
  2430                                                 outValue = true;
  2431                                             }
  2432                                         }
  2433                                     }
  2434                                 }
  2435 
  2436                                 success = true;
  2437                                 break;
  2438                             }
  2439                         // pEp protocol version MAPI property
  2440                         case PEPProperty.PEPProtocolVersion:
  2441                             {
  2442                                 // Note: this value is passed through completely as a string
  2443                                 outValue = MapiHelper.GetProperty(omi, PEPMessage.PidNamePEPProtocolVersion, MailItemExtensions.GetPEPPropertyDefault(property));
  2444 
  2445                                 // OUT-259. In case of clean account sometimes can not get PidNamePEPProtocolVersion property("The property is unknown or cannot be found" exception)
  2446                                 // workaround: getting all Transport Message Headers and taking needed PR_PEP_PROTOCOL_VERSION_NAME from there.    
  2447                                 if (outValue == null)
  2448                                 {
  2449                                     // As a backup search the original internet message headers
  2450                                     if (messageHeaders == null)
  2451                                     {
  2452                                         messageHeaders = omi.GetParsedTransportMessageHeaders();
  2453                                     }
  2454                                     if (messageHeaders != null)
  2455                                     {
  2456                                         foreach (Header header in messageHeaders)
  2457                                         {
  2458                                             if ((header.Field != null) &&
  2459                                                 (header.Value != null) &&
  2460                                                 (string.Equals(header.Field.Trim(), PEPMessage.PR_PEP_PROTOCOL_VERSION_NAME, StringComparison.OrdinalIgnoreCase)))
  2461                                             {
  2462                                                 outValue = header.Value.Trim();
  2463                                             }
  2464                                         }
  2465                                     }
  2466                                 }
  2467                                 success = true;
  2468                                 break;
  2469                             }
  2470                         // pEp rating MAPI property
  2471                         case PEPProperty.Rating:
  2472                             {
  2473                                 /* Note that Rating is defined as:
  2474                                  *          pEpRatingX : Rating property exists with a valid string value (parsable to pEpRating)
  2475                                  *  pEpRatingUndefined : Rating property does not exist or has an invalid value (including null)
  2476                                  */
  2477                                 outValue = MailItemExtensions.GetPEPPropertyDefault(property);
  2478                                 propertyValue = MapiHelper.GetProperty(omi, PEPMessage.PidNameEncStatus, null);
  2479 
  2480                                 if ((propertyValue != null) &&
  2481                                     (propertyValue is string))
  2482                                 {
  2483                                     try
  2484                                     {
  2485                                         outValue = AdapterExtensions.ParseRatingString((string)propertyValue);
  2486                                     }
  2487                                     catch
  2488                                     {
  2489                                         outValue = pEpRating.pEpRatingUndefined;
  2490                                     }
  2491                                 }
  2492 
  2493                                 success = true;
  2494                                 break;
  2495                             }
  2496                         // EnableProtection user property
  2497                         case PEPProperty.EnableProtection:
  2498                             {
  2499                                 /* Note that EnableProtection is defined as:
  2500                                  *   ON : EnableProtection property exists with a value of 'True'
  2501                                  *  OFF : EnableProtection property does not exist or has a value of 'False' or null
  2502                                  */
  2503                                 outValue = MailItemExtensions.GetPEPPropertyDefault(property);
  2504                                 propertyValue = omi.GetUserProperty(CryptableMailItem.USER_PROPERTY_KEY_ENABLE_PROTECTION);
  2505 
  2506                                 if ((propertyValue != null) &&
  2507                                     (propertyValue is bool) &&
  2508                                     ((bool)propertyValue == true))
  2509                                 {
  2510                                     outValue = true;
  2511                                 }
  2512 
  2513                                 success = true;
  2514                                 break;
  2515                             }
  2516                     }
  2517                 }
  2518                 else
  2519                 {
  2520                     Log.Error("GetPEPProperty: Can't set for null MailItem or propertyName.");
  2521                 }
  2522             }
  2523             catch (Exception ex)
  2524             {
  2525                 success = false;
  2526                 Log.Error("GetPEPProperty: An error occured with " + property.ToString() + ", " + ex.ToString());
  2527             }
  2528 
  2529             value = outValue;
  2530             return (success);
  2531         }
  2532 
  2533         /// <summary>
  2534         /// Sets a value to either a UserProperty or MAPI property in the given Outlook mail item.
  2535         /// NOTE: The MailItem will NOT be automatically saved, this MUST be done externally for the property to be persistent.
  2536         /// Warning: The propertyName for a UserProperty or MAPI property must never be the same.
  2537         /// </summary>
  2538         /// <param name="omi">The Outlook mail item to process with.</param>
  2539         /// <param name="property">The pEp defined property to set.</param>
  2540         /// <param name="value">The value of the property to set (null is not supported).</param>
  2541         /// <returns>True if successful, otherwise false.</returns>
  2542         public static bool SetPEPProperty(this Outlook.MailItem omi,
  2543                                           PEPProperty property,
  2544                                           object value)
  2545         {
  2546             bool success = false;
  2547 
  2548             /* Note: Never call .Save() for a MailItem in this method.
  2549              * This method is used in other methods where the MailItem draft flag needs to be changed.
  2550              */
  2551 
  2552             try
  2553             {
  2554                 if (omi != null)
  2555                 {
  2556                     switch (property)
  2557                     {
  2558                         // Auto consume MAPI property
  2559                         case PEPProperty.AutoConsume:
  2560                             {
  2561                                 // Note: cannot set the property value to null in PropertyAccessor.SetProperty()
  2562                                 if (value != null)
  2563                                 {
  2564                                     MapiHelper.SetProperty(omi, PEPMessage.PidNamePEPAutoConsume, value.ToString());
  2565                                 }
  2566                                 else
  2567                                 {
  2568                                     MapiHelper.DeleteProperty(omi, PEPMessage.PidNamePEPAutoConsume);
  2569                                 }
  2570 
  2571                                 success = true;
  2572                                 break;
  2573                             }
  2574                         // Force unencrypted user property
  2575                         case PEPProperty.ForceProtection:
  2576                             {
  2577                                 // Note: cannot set the property value to null in PropertyAccessor.SetProperty()
  2578                                 if (value != null)
  2579                                 {
  2580                                     MapiHelper.SetProperty(omi, PEPMessage.PidNamePEPForceProtection, value.ToString());
  2581                                 }
  2582                                 else
  2583                                 {
  2584                                     MapiHelper.DeleteProperty(omi, PEPMessage.PidNamePEPForceProtection);
  2585                                 }
  2586 
  2587                                 success = true;
  2588                                 break;
  2589                             }
  2590                         // Force unencrypted user property
  2591                         case PEPProperty.ForceUnencrypted:
  2592                             {
  2593                                 if (value is bool)
  2594                                 {
  2595                                     if ((bool)value)
  2596                                     {
  2597                                         omi.SetUserProperty(CryptableMailItem.USER_PROPERTY_KEY_FORCE_UNENCRYPTED,
  2598                                                             true,
  2599                                                             Outlook.OlUserPropertyType.olYesNo);
  2600                                     }
  2601                                     else
  2602                                     {
  2603                                         omi.DeleteUserProperty(CryptableMailItem.USER_PROPERTY_KEY_FORCE_UNENCRYPTED);
  2604                                     }
  2605 
  2606                                     success = true;
  2607                                 }
  2608 
  2609                                 break;
  2610                             }
  2611                         // Key list MAPI property
  2612                         case PEPProperty.KeyList:
  2613                             {
  2614                                 // Note: cannot set the property value to null in PropertyAccessor.SetProperty()
  2615                                 if (value != null)
  2616                                 {
  2617                                     MapiHelper.SetProperty(omi, PEPMessage.PidNameKeyList, value.ToString());
  2618                                 }
  2619                                 else
  2620                                 {
  2621                                     MapiHelper.DeleteProperty(omi, PEPMessage.PidNameKeyList);
  2622                                 }
  2623 
  2624                                 success = true;
  2625                                 break;
  2626                             }
  2627                         // Never unsecure MAPI property
  2628                         case PEPProperty.NeverUnsecure:
  2629                             {
  2630                                 if (value is bool)
  2631                                 {
  2632                                     if ((bool)value)
  2633                                     {
  2634                                         MapiHelper.SetProperty(omi, PEPMessage.PidNamePEPNeverUnsecure, PEPMessage.PR_PEP_NEVER_UNSECURE_VALUE);
  2635                                     }
  2636                                     else
  2637                                     {
  2638                                         // The property MUST be deleted when the MailItem should not be marked never unsecure
  2639                                         MapiHelper.DeleteProperty(omi, PEPMessage.PidNamePEPNeverUnsecure);
  2640                                     }
  2641 
  2642                                     success = true;
  2643                                 }
  2644 
  2645                                 break;
  2646                             }
  2647                         // pEp protocol version MAPI property
  2648                         case PEPProperty.PEPProtocolVersion:
  2649                             {
  2650                                 // Note: cannot set the property value to null in PropertyAccessor.SetProperty()
  2651                                 if (value != null)
  2652                                 {
  2653                                     MapiHelper.SetProperty(omi, PEPMessage.PidNamePEPProtocolVersion, value.ToString());
  2654                                 }
  2655                                 else
  2656                                 {
  2657                                     MapiHelper.DeleteProperty(omi, PEPMessage.PidNamePEPProtocolVersion);
  2658                                 }
  2659 
  2660                                 success = true;
  2661                                 break;
  2662                             }
  2663                         // pEp rating MAPI property
  2664                         case PEPProperty.Rating:
  2665                             {
  2666                                 if (value is pEpRating)
  2667                                 {
  2668                                     if (((pEpRating)value) != pEpRating.pEpRatingUndefined)
  2669                                     {
  2670                                         MapiHelper.SetProperty(omi, PEPMessage.PidNameEncStatus, ((pEpRating)value).ToEngineString());
  2671                                     }
  2672                                     else
  2673                                     {
  2674                                         MapiHelper.DeleteProperty(omi, PEPMessage.PidNameEncStatus);
  2675                                     }
  2676 
  2677                                     success = true;
  2678                                 }
  2679 
  2680                                 break;
  2681                             }
  2682                         // EnableProtection user property
  2683                         case PEPProperty.EnableProtection:
  2684                             {
  2685                                 if (value is bool)
  2686                                 {
  2687                                     if ((bool)value)
  2688                                     {
  2689                                         omi.SetUserProperty(CryptableMailItem.USER_PROPERTY_KEY_ENABLE_PROTECTION,
  2690                                                             true,
  2691                                                             Outlook.OlUserPropertyType.olYesNo);
  2692                                     }
  2693                                     else
  2694                                     {
  2695                                         omi.DeleteUserProperty(CryptableMailItem.USER_PROPERTY_KEY_ENABLE_PROTECTION);
  2696                                     }
  2697 
  2698                                     success = true;
  2699                                 }
  2700 
  2701                                 break;
  2702                             }
  2703                     }
  2704                 }
  2705                 else
  2706                 {
  2707                     Log.Error("SetPEPProperty: Can't set for null MailItem or propertyName.");
  2708                 }
  2709             }
  2710             catch (Exception ex)
  2711             {
  2712                 success = false;
  2713                 Log.Error("SetPEPProperty: An error occured with " + property.ToString() + ", " + ex.ToString());
  2714             }
  2715 
  2716             return (success);
  2717         }
  2718     }
  2719 }