Merge with default sync
authorThomas
Mon, 25 Mar 2019 13:27:16 +0100
branchsync
changeset 261009fde2338362
parent 2594 b4351ec5c330
parent 2607 8a9eddf5347a
child 2613 9b7775c64b39
Merge with default
ThisAddIn.cs
     1.1 --- a/Extensions/MailItemExtensions.cs	Tue Mar 05 14:45:53 2019 +0100
     1.2 +++ b/Extensions/MailItemExtensions.cs	Mon Mar 25 13:27:16 2019 +0100
     1.3 @@ -367,214 +367,228 @@
     1.4              Outlook.MailItem mirror = null;
     1.5              Outlook.Folder folder = omi.GetMirrorFolder();
     1.6  
     1.7 -            /* If S/MIME is enabled by default, the mirror creation by new mail item fails during the
     1.8 -             * Save() method. 
     1.9 -             */
    1.10 -            if ((Globals.ThisAddIn.OutlookOptions.IsSMIMEEnabled == false) &&
    1.11 -                (omi.GetIsSMIMEEnabled() == false))
    1.12 +            try
    1.13              {
    1.14 +                Log.Verbose("CreateMirrorOMI: Creating by new mail item.");
    1.15 +
    1.16 +                // Try to create the new mail item directly in the pEp store
    1.17 +                Outlook.Application application = null;
    1.18                  try
    1.19                  {
    1.20 -                    Log.Verbose("CreateMirrorOMI: Creating by new mail item.");
    1.21 -
    1.22                      mirror = (Outlook.MailItem)folder.Items.Add(Outlook.OlItemType.olMailItem);
    1.23 -                    mirror.UnRead = false;
    1.24 -
    1.25 -                    // Set received and sent time
    1.26 -                    try
    1.27 +                }
    1.28 +                catch (Exception ex)
    1.29 +                {
    1.30 +                    // If this fails, log the error and try to create the item with backup method
    1.31 +                    Log.Warning("CreateMirrorOMI: Error creating mirror via Items.ItemAdd. " + ex.ToString());
    1.32 +
    1.33 +                    // If backup fails, let error get thrown out to outer catch
    1.34 +                    application = Globals.ThisAddIn.Application;
    1.35 +                    mirror = application.CreateItem(Outlook.OlItemType.olMailItem);
    1.36 +                    Log.Verbose("CreateMirrorOMI: mail item successfully created via backup method 1.");
    1.37 +                }
    1.38 +                finally
    1.39 +                {
    1.40 +                    application = null;
    1.41 +                }
    1.42 +
    1.43 +                // Set the mirror as read
    1.44 +                mirror.UnRead = false;
    1.45 +
    1.46 +                try
    1.47 +                {
    1.48 +                    MapiHelper.SetProperty(mirror, MapiProperty.PidTagMessageDeliveryTime, omi.ReceivedTime.ToUniversalTime());
    1.49 +                    MapiHelper.SetProperty(mirror, MapiProperty.PidTagClientSubmitTime, omi.SentOn.ToUniversalTime());
    1.50 +                }
    1.51 +                catch (Exception ex)
    1.52 +                {
    1.53 +                    Log.Verbose("CreateMirrorOMI: Failed to set received and sent time. " + ex.ToString());
    1.54 +                    throw;
    1.55 +                }
    1.56 +
    1.57 +                // Attempt to set sender information
    1.58 +                try
    1.59 +                {
    1.60 +                    mirror.Sender = omi.Sender;
    1.61 +                }
    1.62 +                catch (Exception ex)
    1.63 +                {
    1.64 +                    Log.Verbose("CreateMirrorOMI: Failed to set sender directly. " + ex.ToString());
    1.65 +                }
    1.66 +
    1.67 +                try
    1.68 +                {
    1.69 +                    MapiHelper.SetProperty(mirror, MapiProperty.PidTagSenderName, omi.SenderName);
    1.70 +                    MapiHelper.SetProperty(mirror, MapiProperty.PidTagSenderEmailAddress, omi.SenderEmailAddress);
    1.71 +
    1.72 +                    // 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
    1.73 +                    MapiHelper.SetProperty(mirror, MapiProperty.PidTagSenderEntryId, MapiHelper.GetProperty(omi, MapiProperty.PidTagSenderEntryId));
    1.74 +                }
    1.75 +                catch (Exception ex)
    1.76 +                {
    1.77 +                    Log.Verbose("CreateMirrorOMI: Failed to set sender through MAPI properties. " + ex.ToString());
    1.78 +                    throw;
    1.79 +                }
    1.80 +
    1.81 +                try
    1.82 +                {
    1.83 +                    mirror.SendUsingAccount = omi.SendUsingAccount;
    1.84 +                }
    1.85 +                catch (Exception ex)
    1.86 +                {
    1.87 +                    Log.Verbose("CreateMirrorOMI: Failed to set SendUsingAccount. " + ex.ToString());
    1.88 +                }
    1.89 +
    1.90 +                // Set flags
    1.91 +                messageFlags = (System.Int32)MapiHelper.GetProperty(mirror, MapiProperty.PidTagMessageFlags);
    1.92 +                messageFlags &= ~((System.Int32)MapiPropertyValue.EnumPidTagMessageFlags.mfUnsent);  // Clear UNSENT flag -- must be done before save
    1.93 +                messageFlags |= ((System.Int32)MapiPropertyValue.EnumPidTagMessageFlags.mfRead);     // Mark as read
    1.94 +                try
    1.95 +                {
    1.96 +                    MapiHelper.SetProperty(mirror, MapiProperty.PidTagMessageFlags, messageFlags);
    1.97 +                }
    1.98 +                catch (Exception ex)
    1.99 +                {
   1.100 +                    Log.Verbose("CreateMirrorOMI: Failed to set message flags. " + ex.ToString());
   1.101 +                }
   1.102 +
   1.103 +                // Conversation information
   1.104 +                try
   1.105 +                {
   1.106 +                    /* Note: PidTagConversationId cannot be set even through the MAPI accessor.
   1.107 +                     * This is by design since this property is computed automatically from other properties.
   1.108 +                     * See: https://msdn.microsoft.com/en-us/library/ee204279.aspx
   1.109 +                     */
   1.110 +
   1.111 +                    str = omi.ConversationIndex;
   1.112 +                    bytes = MapiHelper.StringToPtypBinary(str);
   1.113 +                    if (bytes?.Length > 0)
   1.114                      {
   1.115 -                        MapiHelper.SetProperty(mirror, MapiProperty.PidTagMessageDeliveryTime, omi.ReceivedTime.ToUniversalTime());
   1.116 -                        MapiHelper.SetProperty(mirror, MapiProperty.PidTagClientSubmitTime, omi.SentOn.ToUniversalTime());
   1.117 +                        MapiHelper.SetProperty(mirror, MapiProperty.PidTagConversationIndex, bytes);
   1.118                      }
   1.119 -                    catch (Exception ex)
   1.120 +
   1.121 +                    MapiHelper.SetProperty(mirror, MapiProperty.PidTagConversationIndexTracking, true);
   1.122 +                    MapiHelper.SetProperty(mirror, MapiProperty.PidTagConversationTopic, omi.ConversationTopic);
   1.123 +                }
   1.124 +                catch (Exception ex)
   1.125 +                {
   1.126 +                    Log.Verbose("CreateMirrorOMI: Failed to set conversation information. " + ex.ToString());
   1.127 +                }
   1.128 +
   1.129 +                // Remove S/MIME flags if necessary
   1.130 +                mirror.RemoveSMIMEFlags();
   1.131 +
   1.132 +                mirror.To = omi.To;
   1.133 +                mirror.CC = omi.CC;
   1.134 +                mirror.BCC = omi.BCC;
   1.135 +                mirror.Subject = omi.Subject;
   1.136 +                mirror.Body = string.Empty;
   1.137 +                mirror.Save();
   1.138 +
   1.139 +                /* Set all received-by information
   1.140 +                 * 
   1.141 +                 * This must be stored as a user-property because settings MAPI properties such as "PR_RECEIVED_BY_EMAIL_ADDRESS"
   1.142 +                 * is not supported by Outlook. The user-properties are named the same as the MAPI property for simplicity.
   1.143 +                 * 
   1.144 +                 * In most situations the received-by information will not be needed.
   1.145 +                 * Decryption for example uses the recipients/identities in the original mail item itself.
   1.146 +                 * However, the GetMyIdentity(omi) method requires this information in some cases:
   1.147 +                 *  • The true 'myself' identity of a mail item is not an account registerd in Outlook. This can happen
   1.148 +                 *    using aliases or when forwarding emails from another account.
   1.149 +                 *  • This is primarily needed when displaying mirrors themselves then opening the handshake dialog.
   1.150 +                 */
   1.151 +                try
   1.152 +                {
   1.153 +                    /* Get the "received by" email address. In case this returns an internal X500 Exchange address ("/O=DOMAIN/OU=EXCHANGE ADMINISTRATIVE GROUP..."),
   1.154 +                     * compare it with the recipients' addresses and take the primary SMTP address.
   1.155 +                     */
   1.156 +                    string email = MapiHelper.GetProperty(omi, MapiProperty.PidTagReceivedByEmailAddress) as string;
   1.157 +                    if (email.StartsWith("/O"))
   1.158                      {
   1.159 -                        Log.Verbose("CreateMirrorOMI: Failed to set received and sent time. " + ex.ToString());
   1.160 -                        throw;
   1.161 -                    }
   1.162 -
   1.163 -                    // Attempt to set sender information
   1.164 -                    try
   1.165 -                    {
   1.166 -                        mirror.Sender = omi.Sender;
   1.167 -                    }
   1.168 -                    catch (Exception ex)
   1.169 -                    {
   1.170 -                        Log.Verbose("CreateMirrorOMI: Failed to set sender directly. " + ex.ToString());
   1.171 -                    }
   1.172 -
   1.173 -                    try
   1.174 -                    {
   1.175 -                        MapiHelper.SetProperty(mirror, MapiProperty.PidTagSenderName, omi.SenderName);
   1.176 -                        MapiHelper.SetProperty(mirror, MapiProperty.PidTagSenderEmailAddress, omi.SenderEmailAddress);
   1.177 -
   1.178 -                        // 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
   1.179 -                        MapiHelper.SetProperty(mirror, MapiProperty.PidTagSenderEntryId, MapiHelper.GetProperty(omi, MapiProperty.PidTagSenderEntryId));
   1.180 -                    }
   1.181 -                    catch (Exception ex)
   1.182 -                    {
   1.183 -                        Log.Verbose("CreateMirrorOMI: Failed to set sender through MAPI properties. " + ex.ToString());
   1.184 -                        throw;
   1.185 -                    }
   1.186 -
   1.187 -                    try
   1.188 -                    {
   1.189 -                        mirror.SendUsingAccount = omi.SendUsingAccount;
   1.190 -                    }
   1.191 -                    catch (Exception ex)
   1.192 -                    {
   1.193 -                        Log.Verbose("CreateMirrorOMI: Failed to set SendUsingAccount. " + ex.ToString());
   1.194 -                    }
   1.195 -
   1.196 -                    // Set flags
   1.197 -                    messageFlags = (System.Int32)MapiHelper.GetProperty(mirror, MapiProperty.PidTagMessageFlags);
   1.198 -                    messageFlags &= ~((System.Int32)MapiPropertyValue.EnumPidTagMessageFlags.mfUnsent);  // Clear UNSENT flag -- must be done before save
   1.199 -                    messageFlags |= ((System.Int32)MapiPropertyValue.EnumPidTagMessageFlags.mfRead);     // Mark as read
   1.200 -                    try
   1.201 -                    {
   1.202 -                        MapiHelper.SetProperty(mirror, MapiProperty.PidTagMessageFlags, messageFlags);
   1.203 -                    }
   1.204 -                    catch (Exception ex)
   1.205 -                    {
   1.206 -                        Log.Verbose("CreateMirrorOMI: Failed to set message flags. " + ex.ToString());
   1.207 -                    }
   1.208 -
   1.209 -                    // Conversation information
   1.210 -                    try
   1.211 -                    {
   1.212 -                        /* Note: PidTagConversationId cannot be set even through the MAPI accessor.
   1.213 -                         * This is by design since this property is computed automatically from other properties.
   1.214 -                         * See: https://msdn.microsoft.com/en-us/library/ee204279.aspx
   1.215 -                         */
   1.216 -
   1.217 -                        str = omi.ConversationIndex;
   1.218 -                        bytes = MapiHelper.StringToPtypBinary(str);
   1.219 -                        if (bytes?.Length > 0)
   1.220 +                        Outlook.Recipients recipients = null;
   1.221 +                        Outlook.Recipient recipient = null;
   1.222 +                        Outlook.AddressEntry addressEntry = null;
   1.223 +                        Outlook.ExchangeUser exchangeUser = null;
   1.224 +
   1.225 +                        try
   1.226                          {
   1.227 -                            MapiHelper.SetProperty(mirror, MapiProperty.PidTagConversationIndex, bytes);
   1.228 -                        }
   1.229 -
   1.230 -                        MapiHelper.SetProperty(mirror, MapiProperty.PidTagConversationIndexTracking, true);
   1.231 -                        MapiHelper.SetProperty(mirror, MapiProperty.PidTagConversationTopic, omi.ConversationTopic);
   1.232 -                    }
   1.233 -                    catch (Exception ex)
   1.234 -                    {
   1.235 -                        Log.Verbose("CreateMirrorOMI: Failed to set conversation information. " + ex.ToString());
   1.236 -                    }
   1.237 -
   1.238 -                    // Remove S/MIME flags if necessary
   1.239 -                    mirror.RemoveSMIMEFlags();
   1.240 -
   1.241 -                    mirror.To = omi.To;
   1.242 -                    mirror.CC = omi.CC;
   1.243 -                    mirror.BCC = omi.BCC;
   1.244 -                    mirror.Subject = omi.Subject;
   1.245 -                    mirror.Body = string.Empty;
   1.246 -                    mirror.Save();
   1.247 -
   1.248 -                    /* Set all received-by information
   1.249 -                     * 
   1.250 -                     * This must be stored as a user-property because settings MAPI properties such as "PR_RECEIVED_BY_EMAIL_ADDRESS"
   1.251 -                     * is not supported by Outlook. The user-properties are named the same as the MAPI property for simplicity.
   1.252 -                     * 
   1.253 -                     * In most situations the received-by information will not be needed.
   1.254 -                     * Decryption for example uses the recipients/identities in the original mail item itself.
   1.255 -                     * However, the GetMyIdentity(omi) method requires this information in some cases:
   1.256 -                     *  • The true 'myself' identity of a mail item is not an account registerd in Outlook. This can happen
   1.257 -                     *    using aliases or when forwarding emails from another account.
   1.258 -                     *  • This is primarily needed when displaying mirrors themselves then opening the handshake dialog.
   1.259 -                     */
   1.260 -                    try
   1.261 -                    {
   1.262 -                        /* Get the "received by" email address. In case this returns an internal X500 Exchange address ("/O=DOMAIN/OU=EXCHANGE ADMINISTRATIVE GROUP..."),
   1.263 -                         * compare it with the recipients' addresses and take the primary SMTP address.
   1.264 -                         */
   1.265 -                        string email = MapiHelper.GetProperty(omi, MapiProperty.PidTagReceivedByEmailAddress) as string;
   1.266 -                        if (email.StartsWith("/O"))
   1.267 -                        {
   1.268 -                            Outlook.Recipients recipients = null;
   1.269 -                            Outlook.Recipient recipient = null;
   1.270 -                            Outlook.AddressEntry addressEntry = null;
   1.271 -                            Outlook.ExchangeUser exchangeUser = null;
   1.272 -
   1.273 -                            try
   1.274 +                            recipients = omi.Recipients;
   1.275 +
   1.276 +                            for (int i = 1; i <= recipients.Count; i++)
   1.277                              {
   1.278 -                                recipients = omi.Recipients;
   1.279 -
   1.280 -                                for (int i = 1; i <= recipients.Count; i++)
   1.281 +                                recipient = recipients[i];
   1.282 +                                addressEntry = recipient?.AddressEntry;
   1.283 +                                exchangeUser = addressEntry?.GetExchangeUser();
   1.284 +
   1.285 +                                if (email.ToUpperInvariant().Equals(exchangeUser?.Address?.ToUpperInvariant()))
   1.286                                  {
   1.287 -                                    recipient = recipients[i];
   1.288 -                                    addressEntry = recipient?.AddressEntry;
   1.289 -                                    exchangeUser = addressEntry?.GetExchangeUser();
   1.290 -
   1.291 -                                    if (email.ToUpperInvariant().Equals(exchangeUser?.Address?.ToUpperInvariant()))
   1.292 -                                    {
   1.293 -                                        email = exchangeUser?.PrimarySmtpAddress;
   1.294 -                                        break;
   1.295 -                                    }
   1.296 -
   1.297 -                                    recipient = null;
   1.298 -                                    addressEntry = null;
   1.299 -                                    exchangeUser = null;
   1.300 +                                    email = exchangeUser?.PrimarySmtpAddress;
   1.301 +                                    break;
   1.302                                  }
   1.303 -                            }
   1.304 -                            catch (Exception ex)
   1.305 -                            {
   1.306 -                                Log.Error("CreateMirrorOMI: Error getting ReceivedByEmailAddress. " + ex.ToString());
   1.307 -                            }
   1.308 -                            finally
   1.309 -                            {
   1.310 -                                recipients = null;
   1.311 +
   1.312                                  recipient = null;
   1.313                                  addressEntry = null;
   1.314                                  exchangeUser = null;
   1.315                              }
   1.316                          }
   1.317 -                        mirror.SetUserProperty(MapiProperty.PidTagReceivedByEmailAddress.DaslName, email,
   1.318 -                                            Outlook.OlUserPropertyType.olText);
   1.319 -                        mirror.SetUserProperty(MapiProperty.PidTagReceivedByName.DaslName,
   1.320 -                                            MapiHelper.GetProperty(omi, MapiProperty.PidTagReceivedByName),
   1.321 -                                            Outlook.OlUserPropertyType.olText);
   1.322 +                        catch (Exception ex)
   1.323 +                        {
   1.324 +                            Log.Error("CreateMirrorOMI: Error getting ReceivedByEmailAddress. " + ex.ToString());
   1.325 +                        }
   1.326 +                        finally
   1.327 +                        {
   1.328 +                            recipients = null;
   1.329 +                            recipient = null;
   1.330 +                            addressEntry = null;
   1.331 +                            exchangeUser = null;
   1.332 +                        }
   1.333                      }
   1.334 -                    catch (Exception ex)
   1.335 -                    {
   1.336 -                        Log.Verbose("CreateMirrorOMI: Failed to set received-by information. " + ex.ToString());
   1.337 -                    }
   1.338 -
   1.339 -                    // Mark the mail item as a mirror
   1.340 -                    mirror.SetUserProperty(MailItemExtensions.USER_PROPERTY_KEY_IS_MIRROR, true, Outlook.OlUserPropertyType.olYesNo);
   1.341 -
   1.342 -                    /* Set the incoming status of the new mail item.
   1.343 -                     * This is necessary because manually setting the PR_RECEIVED_BY_ENTRYID fails if called by user code.
   1.344 -                     * PR_RECEIVED_BY_ENTRYID is only set when copying the original mail item.
   1.345 -                     */
   1.346 -                    if (omi.GetIsIncoming())
   1.347 -                    {
   1.348 -                        mirror.SetUserProperty(MailItemExtensions.USER_PROPERTY_KEY_IS_INCOMING, true, Outlook.OlUserPropertyType.olYesNo);
   1.349 -                    }
   1.350 -                    else
   1.351 -                    {
   1.352 -                        mirror.SetUserProperty(MailItemExtensions.USER_PROPERTY_KEY_IS_INCOMING, false, Outlook.OlUserPropertyType.olYesNo);
   1.353 -                    }
   1.354 -
   1.355 -                    // Add the original EntryID or the messageId if the latter has been passed
   1.356 -                    mirror.SetUserProperty(MailItemExtensions.USER_PROPERTY_KEY_ORIG_ENTRY_ID, messageId ?? omi.EntryID);
   1.357 -                    mirror.Save();
   1.358 -
   1.359 -                    // Move the mirror copy to the correct folder
   1.360 -                    // This is needed as some account-types/office versions will still create a mail item in the default folder
   1.361 -                    // even if folder.Items.Add is used to specify a location.
   1.362 -                    // WARNING: this creates a new mail item that will no longer be referenced here!!
   1.363 -                    mirror.Move(folder);
   1.364 -                    created = true;
   1.365 +                    mirror.SetUserProperty(MapiProperty.PidTagReceivedByEmailAddress.DaslName, email,
   1.366 +                                        Outlook.OlUserPropertyType.olText);
   1.367 +                    mirror.SetUserProperty(MapiProperty.PidTagReceivedByName.DaslName,
   1.368 +                                        MapiHelper.GetProperty(omi, MapiProperty.PidTagReceivedByName),
   1.369 +                                        Outlook.OlUserPropertyType.olText);
   1.370                  }
   1.371                  catch (Exception ex)
   1.372                  {
   1.373 -                    Log.Verbose("CreateMirrorOMI: Creating new mail item failed. " + ex.ToString());
   1.374 -                    mirror.PermanentlyDelete();
   1.375 -                    mirror = null;
   1.376 +                    Log.Verbose("CreateMirrorOMI: Failed to set received-by information. " + ex.ToString());
   1.377                  }
   1.378 +
   1.379 +                // Mark the mail item as a mirror
   1.380 +                mirror.SetUserProperty(MailItemExtensions.USER_PROPERTY_KEY_IS_MIRROR, true, Outlook.OlUserPropertyType.olYesNo);
   1.381 +
   1.382 +                /* Set the incoming status of the new mail item.
   1.383 +                 * This is necessary because manually setting the PR_RECEIVED_BY_ENTRYID fails if called by user code.
   1.384 +                 * PR_RECEIVED_BY_ENTRYID is only set when copying the original mail item.
   1.385 +                 */
   1.386 +                if (omi.GetIsIncoming())
   1.387 +                {
   1.388 +                    mirror.SetUserProperty(MailItemExtensions.USER_PROPERTY_KEY_IS_INCOMING, true, Outlook.OlUserPropertyType.olYesNo);
   1.389 +                }
   1.390 +                else
   1.391 +                {
   1.392 +                    mirror.SetUserProperty(MailItemExtensions.USER_PROPERTY_KEY_IS_INCOMING, false, Outlook.OlUserPropertyType.olYesNo);
   1.393 +                }
   1.394 +
   1.395 +                // Add the original EntryID or the messageId if the latter has been passed
   1.396 +                mirror.SetUserProperty(MailItemExtensions.USER_PROPERTY_KEY_ORIG_ENTRY_ID, messageId ?? omi.EntryID);
   1.397 +                mirror.Save();
   1.398 +
   1.399 +                // Move the mirror copy to the correct folder
   1.400 +                // This is needed as some account-types/office versions will still create a mail item in the default folder
   1.401 +                // even if folder.Items.Add is used to specify a location.
   1.402 +                // WARNING: this creates a new mail item that will no longer be referenced here!!
   1.403 +                mirror.Move(folder);
   1.404 +                created = true;
   1.405              }
   1.406 -
   1.407 +            catch (Exception ex)
   1.408 +            {
   1.409 +                Log.Verbose("CreateMirrorOMI: Creating new mail item failed. " + ex.ToString());
   1.410 +                mirror.PermanentlyDelete();
   1.411 +                mirror = null;
   1.412 +            }
   1.413 +
   1.414 +            // Backup method
   1.415              if (created == false)
   1.416              {
   1.417                  try
     2.1 --- a/Mapi.cs	Tue Mar 05 14:45:53 2019 +0100
     2.2 +++ b/Mapi.cs	Mon Mar 25 13:27:16 2019 +0100
     2.3 @@ -135,7 +135,35 @@
     2.4          #region Interfaces
     2.5  
     2.6          /// <summary>
     2.7 -        /// Performs operations on the messages and subfolders in a folder..
     2.8 +        /// Manages high-level operations on container objects such as address books, distribution lists, and folders.
     2.9 +        /// </summary>
    2.10 +        [
    2.11 +            ComImport,
    2.12 +            ComVisible(false),
    2.13 +            InterfaceType(ComInterfaceType.InterfaceIsIUnknown),
    2.14 +            Guid(Mapi.MAPIInterfaceIds.IMAPIContainer)
    2.15 +        ]
    2.16 +        internal interface IMAPIContainer : IMAPIProp
    2.17 +        {
    2.18 +            [return: MarshalAs(UnmanagedType.I4)]
    2.19 +            [PreserveSig]
    2.20 +            int GetContentsTable();
    2.21 +            [return: MarshalAs(UnmanagedType.I4)]
    2.22 +            [PreserveSig]
    2.23 +            int GetHierarchyTable();
    2.24 +            [return: MarshalAs(UnmanagedType.I4)]
    2.25 +            [PreserveSig]
    2.26 +            int OpenEntry(uint cbEntryId, IntPtr entryId, ref Guid iid, uint flags, out IntPtr type, out IntPtr iUnk);
    2.27 +            [return: MarshalAs(UnmanagedType.I4)]
    2.28 +            [PreserveSig]
    2.29 +            int SetSearchCriteria();
    2.30 +            [return: MarshalAs(UnmanagedType.I4)]
    2.31 +            [PreserveSig]
    2.32 +            int GetSearchCriteria();
    2.33 +        }
    2.34 +
    2.35 +        /// <summary>
    2.36 +        /// Performs operations on the messages and subfolders in a folder.
    2.37          /// </summary>
    2.38          [
    2.39              ComImport,
    2.40 @@ -143,7 +171,7 @@
    2.41              InterfaceType(ComInterfaceType.InterfaceIsIUnknown),
    2.42              Guid(Mapi.MAPIInterfaceIds.IMAPIFolder)
    2.43          ]
    2.44 -        internal interface IMAPIFolder
    2.45 +        internal interface IMAPIFolder : IMAPIContainer
    2.46          {
    2.47              /// <summary>
    2.48              /// Creates a new message.
    2.49 @@ -158,7 +186,7 @@
    2.50              /// <returns>The status of this method.</returns>
    2.51              [return: MarshalAs(UnmanagedType.I4)]
    2.52              [PreserveSig]
    2.53 -            int CreateMessage(IntPtr _interface, uint flags, [MarshalAs(UnmanagedType.Interface)] ref IMessage message);
    2.54 +            int CreateMessage(ref Guid lpiid, uint flags, [MarshalAs(UnmanagedType.Interface)] out IMessage message);
    2.55  
    2.56              /// <summary>
    2.57              /// Copies or moves one or more messages. 
    2.58 @@ -469,7 +497,7 @@
    2.59          /// The IMessage interface defines methods and properties used to manage messages.
    2.60          /// </summary>
    2.61          [ComImport()]
    2.62 -        [Guid("00020307-0000-0000-C000-000000000046")]
    2.63 +        [Guid(Mapi.MAPIInterfaceIds.IMessage)]        
    2.64          [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    2.65          public interface IMessage
    2.66          {
    2.67 @@ -487,7 +515,7 @@
    2.68              int GetNamesFromIDs(out IntPtr lppPropTags, ref Guid lpPropSetGuid, uint ulFlags, out uint lpcPropNames, out IntPtr lpppPropNames);
    2.69              int GetIDsFromNames(uint cPropNames, ref IntPtr lppPropNames, uint ulFlags, out IntPtr lppPropTags);
    2.70              int GetAttachmentTable(uint ulFlags, out IMAPITable lppTable);
    2.71 -            //int OpenAttach(uint ulAttachmentNum, ref Guid lpInterface, uint ulFlags, out IAttach lppAttach);
    2.72 +            int OpenAttach(uint ulAttachmentNum, ref Guid lpInterface, uint ulFlags, out IAttach lppAttach);
    2.73              int CreateAttach();
    2.74              int DeleteAttach();
    2.75              int GetRecipientTable(uint ulFlags, out IMAPITable lppTable);
    2.76 @@ -496,6 +524,12 @@
    2.77              int SetReadFlag(uint ulFlags);
    2.78          }
    2.79  
    2.80 +        [ComImport()]
    2.81 +        [Guid(Mapi.MAPIInterfaceIds.IAttachment)]
    2.82 +        [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    2.83 +        public interface IAttach : IMAPIProp
    2.84 +        { }
    2.85 +
    2.86          #endregion
    2.87  
    2.88          #region Structures
    2.89 @@ -692,6 +726,95 @@
    2.90          }
    2.91  
    2.92          /// <summary>
    2.93 +        /// Gets an attachment's properties.
    2.94 +        /// </summary>
    2.95 +        /// <param name="omi">The Outlook mail item that contains the attachment.</param>
    2.96 +        /// <param name="index">The attachment's index. 0-based.</param>
    2.97 +        /// <param name="mapiProperties">The MAPI properties to get its values for.</param>
    2.98 +        /// <param name="propertyValues">The property values that have been returned.</param>
    2.99 +        /// <returns>The status (Mapi.HResult) of this method.</returns>
   2.100 +        public static int GetAttachmentProperties(Outlook.MailItem omi,
   2.101 +                                                  int index,
   2.102 +                                                  List<MapiProperty.MapiProp> mapiProperties,
   2.103 +                                                  out MAPIProperties propertyValues)
   2.104 +        {
   2.105 +            int result = (int)Mapi.HResult.S_FALSE;
   2.106 +            propertyValues = null;
   2.107 +
   2.108 +            try
   2.109 +            {
   2.110 +                // Get the IMessage interface
   2.111 +                IMessage iMessage = (IMessage)omi?.MAPIOBJECT;
   2.112 +                if (iMessage == null)
   2.113 +                {
   2.114 +                    throw new Exception("Could not get IMessage interface.");
   2.115 +                }
   2.116 +
   2.117 +                // Open the attachment
   2.118 +                Guid guid = (typeof(Mapi.IAttach)).GUID;
   2.119 +                result = iMessage.OpenAttach((uint)index, ref guid, 0, out IAttach attachment);
   2.120 +
   2.121 +                // If we can't open the attachment, throw error
   2.122 +                if (result != 0)
   2.123 +                {
   2.124 +                    throw new Exception("Error opening attachment. " + Mapi.GetHResultError(result));
   2.125 +                }
   2.126 +
   2.127 +                // Create the SPropValue structure
   2.128 +                Mapi.SPropValue sPropValue = new Mapi.SPropValue();
   2.129 +                int propValueSize = Marshal.SizeOf(sPropValue);
   2.130 +
   2.131 +                // Allocate memory for the array of SPropValues to set
   2.132 +                int propertiesCount = mapiProperties.Count;
   2.133 +                IntPtr propTagArray = Marshal.AllocHGlobal(propValueSize * propertiesCount);
   2.134 +
   2.135 +                // Create the property values array
   2.136 +                uint[] propertyTags = new uint[propertiesCount + 1];
   2.137 +                propertyTags[0] = (uint)propertiesCount;
   2.138 +                for (int i = 0; i < propertiesCount; i++)
   2.139 +                {
   2.140 +                    propertyTags[i + 1] = (uint)mapiProperties[i].Tag;
   2.141 +                }
   2.142 +
   2.143 +                // Get properties
   2.144 +                result = attachment.GetProps(propertyTags, Mapi.MAPI_UNICODE, out uint valuesCount, out IntPtr propArray);
   2.145 +
   2.146 +                // If an error occured, just log at this point.
   2.147 +                if (result != 0)
   2.148 +                {
   2.149 +                    Log.Error("OpenAttachment: Error getting attachment properties. " + Mapi.GetHResultError(result));
   2.150 +                }
   2.151 +
   2.152 +                // Convert the retrieved values
   2.153 +                object[] values = new object[valuesCount];
   2.154 +                for (int i = 0; i < valuesCount; i++)
   2.155 +                {
   2.156 +                    sPropValue = (SPropValue)Marshal.PtrToStructure((propArray + (i * propValueSize)), typeof(SPropValue));
   2.157 +                    values[i] = Mapi.ConvertSPropValueToObject(mapiProperties[i], sPropValue);
   2.158 +                }
   2.159 +
   2.160 +                // Check if returned values match properties count
   2.161 +                if (propertiesCount != valuesCount)
   2.162 +                {
   2.163 +                    throw new Exception("Properties count doesn't match values count.");
   2.164 +                }
   2.165 +
   2.166 +                // Create return dictionary
   2.167 +                propertyValues = new MAPIProperties();
   2.168 +                for (int i = 0; i < valuesCount; i++)
   2.169 +                {
   2.170 +                    propertyValues.Add(mapiProperties[i], values[i]);
   2.171 +                }
   2.172 +            }
   2.173 +            catch (Exception ex)
   2.174 +            {
   2.175 +                Log.Error("OpenAttachment: Error getting attachment.  " + ex.ToString());
   2.176 +            }
   2.177 +
   2.178 +            return result;
   2.179 +        }
   2.180 +
   2.181 +        /// <summary>
   2.182          /// Gets the attachment table of an Outlook mail item.
   2.183          /// </summary>
   2.184          /// <param name="omi">The Outlook mail item to get its attachment table for.</param>
     3.1 --- a/Properties/AssemblyInfo.cs	Tue Mar 05 14:45:53 2019 +0100
     3.2 +++ b/Properties/AssemblyInfo.cs	Mon Mar 25 13:27:16 2019 +0100
     3.3 @@ -46,5 +46,5 @@
     3.4  // You can specify all the values or you can default the Build and Revision Numbers 
     3.5  // by using the '*' as shown below:
     3.6  // [assembly: AssemblyVersion("1.0.*")]
     3.7 -[assembly: AssemblyVersion("1.0.209.0")]
     3.8 -[assembly: AssemblyFileVersion("1.0.209.0")]
     3.9 +[assembly: AssemblyVersion("1.0.210.0")]
    3.10 +[assembly: AssemblyFileVersion("1.0.210.0")]
     4.1 --- a/ThisAddIn.cs	Tue Mar 05 14:45:53 2019 +0100
     4.2 +++ b/ThisAddIn.cs	Mon Mar 25 13:27:16 2019 +0100
     4.3 @@ -1547,7 +1547,6 @@
     4.4          /// </summary>
     4.5          private void OpenPEPStoreRootFolder()
     4.6          {
     4.7 -            string path;
     4.8              Outlook.Store pEpStore = null;
     4.9              Outlook.Store store = null;
    4.10              Outlook.Stores stores = null;
    4.11 @@ -1555,9 +1554,18 @@
    4.12  
    4.13              try
    4.14              {
    4.15 +                // The path to store the pEp store to
    4.16 +                string path = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "pEp", (Globals.PEP_DATA_FILE_NAME + ".pst"));
    4.17 +                
    4.18 +                /* For Outlook installations from the Windows Trusted Store the AddStoreEx function to add a store seems to
    4.19 +                 * convert "%LOCALAPPDATA%" to "%LOCALAPPDATA\Packages\Microsoft.Office.Desktop_8wekyb3d8bbwe\LocalCache\Local\".
    4.20 +                 * This is also confirmed on several sites/blogs. No official documentation has been found so far, though.
    4.21 +                 * In any case, we also use the above mentioned path to search for the pEp store.
    4.22 +                 */
    4.23 +                string winStorePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "Packages", "Microsoft.Office.Desktop_8wekyb3d8bbwe", "LocalCache", "Local", "pEp", (Globals.PEP_DATA_FILE_NAME + ".pst"));
    4.24 +
    4.25                  ns = this.Application.Session;
    4.26                  stores = ns.Stores;
    4.27 -                path = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "pEp", (Globals.PEP_DATA_FILE_NAME + ".pst"));
    4.28  
    4.29                  // Try to find an existing store based on file name
    4.30                  for (int i = 1; i <= stores.Count; i++)
    4.31 @@ -1576,7 +1584,8 @@
    4.32                          Log.Warning("OpenPEPStoreRootFolder: Failed to get store, " + ex.ToString());
    4.33                      }
    4.34  
    4.35 -                    if (store?.FilePath == path)
    4.36 +                    if ((store?.FilePath?.Equals(path) == true) ||
    4.37 +                        (store?.FilePath?.Equals(winStorePath) == true))
    4.38                      {
    4.39                          pEpStore = store;
    4.40                          break;
    4.41 @@ -1588,7 +1597,7 @@
    4.42                  // If no store was found, create new 
    4.43                  if (pEpStore == null)
    4.44                  {
    4.45 -                    /* Will create the file and add it as a store, otherwise it will use the existing one.
    4.46 +                    /* Create the file and add it as a store, otherwise use the existing one.
    4.47                       * This can throw an error if a user either modified the list of Outlook data stores
    4.48                       * or the pEp.pst file itself.
    4.49                       */
    4.50 @@ -1616,12 +1625,15 @@
    4.51                              Log.Warning("OpenPEPStoreRootFolder: Failed to get store, " + ex.ToString());
    4.52                          }
    4.53  
    4.54 -                        if (store?.FilePath == path)
    4.55 +                        if ((store?.FilePath?.Equals(path) == true) ||
    4.56 +                            (store?.FilePath?.Equals(winStorePath) == true))
    4.57                          {
    4.58                              pEpStore = store;
    4.59                              pEpStore.GetRootFolder().Name = Globals.PEP_DATA_FILE_NAME;
    4.60                              break; // Break before releasing
    4.61                          }
    4.62 +
    4.63 +                        store = null;
    4.64                      }
    4.65                  }
    4.66  
    4.67 @@ -1632,14 +1644,14 @@
    4.68                  else
    4.69                  {
    4.70                      // Try again using fallback solution for file paths with prefixes
    4.71 -                    Log.Warning("OpenPEPStoreRootFolder: No pEp store found. Trying fallback. \npEp path is " + path);
    4.72 +                    Log.Warning("OpenPEPStoreRootFolder: No pEp store found. Trying fallback. pEp path is " + path);
    4.73  
    4.74                      for (int i = 1; i <= stores.Count; i++)
    4.75                      {
    4.76                          try
    4.77                          {
    4.78                              store = stores[i];
    4.79 -                            Log.Warning("OpenPEPStoreRootFolder: file path of store " + i + ": " + store?.FilePath ?? "<null>");
    4.80 +                            Log.Info("OpenPEPStoreRootFolder: file path of store " + i + " (" + (store?.DisplayName ?? "<null>") + "): " + store?.FilePath ?? "<null>");
    4.81                          }
    4.82                          catch (Exception ex)
    4.83                          {
    4.84 @@ -1647,18 +1659,21 @@
    4.85                          }
    4.86  
    4.87                          // Custom comparison of file paths independently of their (known) prefixes
    4.88 -                        if (Comparisons.FilePathsAreEqual(store?.FilePath, path))
    4.89 +                        if (Comparisons.FilePathsAreEqual(store?.FilePath, path) ||
    4.90 +                            Comparisons.FilePathsAreEqual(store?.FilePath, winStorePath))
    4.91                          {
    4.92                              pEpStore = store;
    4.93                              pEpStore.GetRootFolder().Name = Globals.PEP_DATA_FILE_NAME;
    4.94                              break;
    4.95                          }
    4.96 +
    4.97 +                        store = null;
    4.98                      }
    4.99  
   4.100                      // If pEp store was found, use it. Else throw error.
   4.101                      if (pEpStore != null)
   4.102                      {
   4.103 -                        this._PEPStoreRootFolder = (Outlook.Folder)pEpStore.GetRootFolder();
   4.104 +                        this._PEPStoreRootFolder = pEpStore.GetRootFolder() as Outlook.Folder;
   4.105                      }
   4.106                      else
   4.107                      {
     5.1 --- a/UI/FormRegionPreviewUnencrypted.cs	Tue Mar 05 14:45:53 2019 +0100
     5.2 +++ b/UI/FormRegionPreviewUnencrypted.cs	Mon Mar 25 13:27:16 2019 +0100
     5.3 @@ -21,44 +21,41 @@
     5.4              // Use e.OutlookItem to get a reference to the current Outlook item.
     5.5              private void FormRegionPreviewUnencryptedFactory_FormRegionInitializing(object sender, Microsoft.Office.Tools.Outlook.FormRegionInitializingEventArgs e)
     5.6              {
     5.7 -                Outlook.MailItem omi;
     5.8 +                if (e?.OutlookItem is Outlook.MailItem omi)
     5.9 +                {
    5.10 +                    // Do not load if the mail item itself is not stored encrypted or pEp is disabled
    5.11 +                    if ((omi.GetIsSecurelyStored() == false) ||
    5.12 +                        (omi.GetIsPEPEnabled() == false) && (omi.GetIsDecryptAlwaysEnabled() == false))
    5.13 +                    {
    5.14 +                        e.Cancel = true;
    5.15 +                    }
    5.16 +                    else if (string.IsNullOrEmpty(CryptableMailItem.PreviewAttachedMailId) == false)
    5.17 +                    {
    5.18 +                        HeaderList headers = omi.GetParsedTransportMessageHeaders();
    5.19 +                        string messageId = null;
    5.20  
    5.21 -                try
    5.22 -                {
    5.23 -                    omi = (Outlook.MailItem)e.OutlookItem;
    5.24 +                        try
    5.25 +                        {
    5.26 +                            messageId = headers[HeaderId.MessageId];
    5.27 +                        }
    5.28 +                        catch (Exception ex)
    5.29 +                        {
    5.30 +                            Log.Verbose("FormRegionPreviewUnencryptedFactory_FormRegionInitializing: Error getting MessageId from item. " + ex.ToString());
    5.31 +                        }
    5.32 +
    5.33 +                        e.Cancel = (messageId?.Equals(CryptableMailItem.PreviewAttachedMailId) != true);
    5.34 +                    }
    5.35 +                    else
    5.36 +                    {
    5.37 +                        e.Cancel = false;
    5.38 +                    }
    5.39 +
    5.40 +                    omi = null;
    5.41                  }
    5.42 -                catch
    5.43 +                else
    5.44                  {
    5.45                      // Never load if it's not a MailItem
    5.46                      e.Cancel = true;
    5.47 -                    return;
    5.48 -                }
    5.49 -
    5.50 -                // Do not load if the mail item itself is not stored encrypted or pEp is disabled
    5.51 -                if ((omi.GetIsSecurelyStored() == false) ||
    5.52 -                    (omi.GetIsPEPEnabled() == false) && (omi.GetIsDecryptAlwaysEnabled() == false))
    5.53 -                {
    5.54 -                    e.Cancel = true;
    5.55 -                }
    5.56 -                else if (string.IsNullOrEmpty(CryptableMailItem.PreviewAttachedMailId) == false)
    5.57 -                {
    5.58 -                    HeaderList headers = omi.GetParsedTransportMessageHeaders();
    5.59 -                    string messageId = null;
    5.60 -
    5.61 -                    try
    5.62 -                    {
    5.63 -                        messageId = headers[HeaderId.MessageId];
    5.64 -                    }
    5.65 -                    catch (Exception ex)
    5.66 -                    {
    5.67 -                        Log.Verbose("FormRegionPreviewUnencryptedFactory_FormRegionInitializing: Error getting MessageId from item. " + ex.ToString());
    5.68 -                    }
    5.69 -
    5.70 -                    e.Cancel = (messageId?.Equals(CryptableMailItem.PreviewAttachedMailId) != true);
    5.71 -                }
    5.72 -                else
    5.73 -                {
    5.74 -                    e.Cancel = false;
    5.75                  }
    5.76  
    5.77                  Log.Verbose("FormRegionPreviewUnencryptedFactory_FormRegionInitializing: e.Cancel = " + e.Cancel.ToString());
     6.1 --- a/pEpForOutlook.csproj	Tue Mar 05 14:45:53 2019 +0100
     6.2 +++ b/pEpForOutlook.csproj	Mon Mar 25 13:27:16 2019 +0100
     6.3 @@ -44,7 +44,7 @@
     6.4      <PublishUrl>publish\</PublishUrl>
     6.5      <InstallUrl>https://pep-project.org/</InstallUrl>
     6.6      <TargetCulture>en</TargetCulture>
     6.7 -    <ApplicationVersion>1.0.209.0</ApplicationVersion>
     6.8 +    <ApplicationVersion>1.0.210.0</ApplicationVersion>
     6.9      <AutoIncrementApplicationRevision>true</AutoIncrementApplicationRevision>
    6.10      <UpdateEnabled>true</UpdateEnabled>
    6.11      <UpdateInterval>0</UpdateInterval>