Merge with default OUT-189-UI-Automation
authorMarkus Schaber <markus@pep-security.net>
Sun, 16 Jul 2017 12:39:49 +0200
branchOUT-189-UI-Automation
changeset 174457e628624360
parent 1743 4c3a7686e6b0
parent 1739 32612407d28c
Merge with default
ThisAddIn.cs
pEpForOutlook.csproj
     1.1 --- a/CryptableMailItem.cs	Sun Jul 16 11:56:42 2017 +0200
     1.2 +++ b/CryptableMailItem.cs	Sun Jul 16 12:39:49 2017 +0200
     1.3 @@ -96,18 +96,21 @@
     1.4          private Globals.ReturnStatus _LastProcessedStatus;
     1.5  
     1.6          private bool                              disposeAfterProcessing;
     1.7 +        private bool                              decrementCounter;
     1.8          private Outlook.MailItem                  internalMailItem;
     1.9          private BackgroundWorker                  mirrorLocator;
    1.10          private BackgroundWorker                  processor;
    1.11  
    1.12 -        private string                            draftFileName       = null;
    1.13 -        private object                            mutexMailItem       = new object();
    1.14 -        private static object                     mutexMirror         = new object();
    1.15 -        private static object                     mutexConversation   = new object();
    1.16 -        private static object                     mutexDecryptionList = new object();
    1.17 -        private static Dictionary<string, string> mirrorCache         = new Dictionary<string, string>();
    1.18 -        private static List<OriginalMailItem>     conversationCache   = new List<OriginalMailItem>();
    1.19 -        private static Dictionary<string, string> decryptionList      = new Dictionary<string, string>();
    1.20 +        private string                            draftFileName         = null;
    1.21 +        private object                            mutexMailItem         = new object();
    1.22 +        private static object                     mutexMirror           = new object();
    1.23 +        private static object                     mutexConversation     = new object();
    1.24 +        private static object                     mutexDecryptionList   = new object();
    1.25 +        private static object                     mutexCopiedItemsList  = new object();
    1.26 +        private static Dictionary<string, string> mirrorCache           = new Dictionary<string, string>();
    1.27 +        private static List<OriginalMailItem>     conversationCache     = new List<OriginalMailItem>();
    1.28 +        private static Dictionary<string, string> decryptionList        = new Dictionary<string, string>();
    1.29 +        private static List<string>               copiedItemsList       = new List<string>();
    1.30  
    1.31          /**************************************************************
    1.32           * 
    1.33 @@ -119,12 +122,14 @@
    1.34          /// Primary constructor creating the cryptable mail item from an existing outlook mail item.
    1.35          /// </summary>
    1.36          /// <param name="mailItem">The mail item to wrap and make cryptable.</param>
    1.37 -        public CryptableMailItem(Outlook.MailItem mailItem)
    1.38 +        public CryptableMailItem(Outlook.MailItem mailItem,
    1.39 +                                 bool decrementCounter = false)
    1.40          {
    1.41              this.internalMailItem = mailItem;
    1.42              this.ConnectInternalMailItemEvents();
    1.43  
    1.44              this.disposeAfterProcessing = false;
    1.45 +            this.decrementCounter = decrementCounter;
    1.46              this._LastProcessedRating = pEpRating.pEpRatingUndefined;
    1.47              this._LastProcessedStatus = Globals.ReturnStatus.Success;
    1.48  
    1.49 @@ -639,6 +644,26 @@
    1.50          }
    1.51  
    1.52          /// <summary>
    1.53 +        /// Gets whether a given EntryID is in the list of copied items.
    1.54 +        /// </summary>
    1.55 +        /// <param name="entryId">The EntryID to check for.</param>
    1.56 +        /// <returns>True, if in the list. Otherwise false.</returns>
    1.57 +        public static bool IsInCopiedItemsList(string entryId)
    1.58 +        {
    1.59 +            bool isInList = false;
    1.60 +
    1.61 +            lock (mutexCopiedItemsList)
    1.62 +            {
    1.63 +                if (copiedItemsList.Contains(entryId))
    1.64 +                {
    1.65 +                    isInList = true;
    1.66 +                }
    1.67 +            }
    1.68 +
    1.69 +            return isInList;
    1.70 +        }
    1.71 +
    1.72 +        /// <summary>
    1.73          /// Gets whether the wrapped mail item was marked as being originally encrypted.
    1.74          /// This should have been set by calling SetIsOriginallyEncryptedByCache() during the 
    1.75          /// EncryptedConversationCacheUpdated event.
    1.76 @@ -991,9 +1016,9 @@
    1.77                  bool isAlreadyBeingDecrypted = false;
    1.78                  bool isDraft;
    1.79                  bool isPEPInternal = false;
    1.80 -                bool mirrorFound = false;
    1.81 +                bool isSecurelyStored = false;
    1.82                  bool isSubmitted;
    1.83 -                bool specialCaseFound = false;
    1.84 +                bool fullCalculation = true;
    1.85                  bool entryIDWasAdded = false;
    1.86                  bool mirrorCreated = false;
    1.87                  bool preProcessingPropertySet = false;
    1.88 @@ -1114,28 +1139,38 @@
    1.89                  catch { }
    1.90  
    1.91                  // Do not processes deleted messages on IMAP
    1.92 -                if ((specialCaseFound == false) &&
    1.93 +                if ((fullCalculation) &&
    1.94                      (this.internalMailItem.GetIsInIMAPStore()) &&
    1.95                      (MapiHelper.GetProperty(this.internalMailItem, MapiProperty.PidLidImapMarkedForDeletion, "0").ToString() == "1"))
    1.96                  {
    1.97                      Log.Verbose("ProcessAndGetRating: Deleted IMAP message detected, skipping processing");
    1.98                      result = pEpRating.pEpRatingUndefined;
    1.99 -                    specialCaseFound = true;
   1.100 +                    fullCalculation = false;
   1.101                  }
   1.102  
   1.103                  // Check if the mail item is a mirror and use stored rating
   1.104 -                if ((specialCaseFound == false) &&
   1.105 +                if ((fullCalculation) &&
   1.106                      (this.internalMailItem.GetIsMirror()))
   1.107                  {
   1.108 -                    this.internalMailItem.GetPEPProperty(MailItemExtensions.PEPProperty.Rating,
   1.109 -                                                         out propValue);
   1.110 -                    result = (pEpRating)propValue;
   1.111 +                    // Get UI rating from mirror
   1.112 +                    try
   1.113 +                    {
   1.114 +                        if (PEPMessage.Create(this.internalMailItem, out mirror) == Globals.ReturnStatus.Success)
   1.115 +                        {
   1.116 +                            result = AdapterExtensions.ReevaluateMessageRating(mirror, mirror.Rating);
   1.117 +                        }
   1.118 +                    }
   1.119 +                    catch (Exception e)
   1.120 +                    {
   1.121 +                        Log.Error("ProcessAndGetRating: Error reevaluating mirror rating. " + e.Message);
   1.122 +                        result = mirror.Rating;
   1.123 +                    }
   1.124  
   1.125                      // If the value was undefined, don't use it. Recalculation is needed.
   1.126                      if (result != pEpRating.pEpRatingUndefined)
   1.127                      {
   1.128                          Log.Verbose("ProcessAndGetRating: Mirror detected, using stored rating.");
   1.129 -                        specialCaseFound = true;
   1.130 +                        fullCalculation = false;
   1.131                      }
   1.132                  }
   1.133  
   1.134 @@ -1145,18 +1180,42 @@
   1.135                   * However, it's potentially quicker (and more robust) to handle this special case.
   1.136                   * Note: It's important that the message is not submitted (since it could already be encrypted).
   1.137                   */
   1.138 -                if ((specialCaseFound == false) &&
   1.139 +                if ((fullCalculation) &&
   1.140                      (isDraft) &&
   1.141                      (isSubmitted == false))
   1.142                  {
   1.143                      Log.Verbose("ProcessAndGetRating: Draft detected, using outgoing rating.");
   1.144  
   1.145                      result = this.OutgoingRating;
   1.146 -                    specialCaseFound = true;
   1.147 +                    fullCalculation = false;
   1.148 +                }
   1.149 +
   1.150 +                // Check if untrusted server and search for mirror. Use mirror rating if available
   1.151 +                isSecurelyStored = this.internalMailItem.GetIsSecurelyStored();
   1.152 +                if (fullCalculation &&
   1.153 +                    isSecurelyStored)
   1.154 +                {
   1.155 +                    mirrorMailItem = CryptableMailItem.FindMirrorOMI(this.internalMailItem);
   1.156 +                    if ((mirrorMailItem != null) &&
   1.157 +                        (PEPMessage.Create(mirrorMailItem, out mirror) == Globals.ReturnStatus.Success))
   1.158 +                    {
   1.159 +                        fullCalculation = false;
   1.160 +
   1.161 +                        // Get UI rating from mirror
   1.162 +                        try
   1.163 +                        {
   1.164 +                            result = AdapterExtensions.ReevaluateMessageRating(mirror, mirror.Rating);
   1.165 +                        }
   1.166 +                        catch (Exception e)
   1.167 +                        {
   1.168 +                            Log.Error("ProcessAndGetRating: Error reevaluating mirror rating. " + e.Message);
   1.169 +                            result = mirror.Rating;
   1.170 +                        }
   1.171 +                    }
   1.172                  }
   1.173  
   1.174                  // Full calculation if no special cases were found
   1.175 -                if (specialCaseFound == false)
   1.176 +                if (fullCalculation)
   1.177                  {
   1.178                      // Check if message has autoconsume header and set pEp internal category if necessary
   1.179                      try
   1.180 @@ -1194,9 +1253,9 @@
   1.181                       * which then leads to the form region not being properly dispayed at the first click (OUT-68).
   1.182                       */
   1.183                      if ((isPEPInternal == false) &&
   1.184 -                        ((this.internalMailItem.GetNeverUnsecure()) ||
   1.185 -                         (this.internalMailItem.GetIsInSecureStore() && this.internalMailItem.GetIsSecure()) ||
   1.186 +                         (isSecurelyStored ||
   1.187                           (Globals.GetOutlookVersion() == Globals.Version.Outlook2010 && this.internalMailItem.GetIsInSecureStore() && this.internalMailItem.DownloadState == Outlook.OlDownloadState.olHeaderOnly)))
   1.188 +
   1.189                      {
   1.190                          try
   1.191                          {
   1.192 @@ -1222,142 +1281,117 @@
   1.193                          }
   1.194                      }
   1.195  
   1.196 -                    // Check if untrusted server and search for mirror. Use mirror rating if available
   1.197 -                    if (this.internalMailItem.GetIsSecurelyStored())
   1.198 +                    // Process mail item
   1.199 +                    sts1 = PEPMessage.Create(this.internalMailItem, out message);
   1.200 +
   1.201 +                    // Process
   1.202 +                    status = msgProcessor.ProcessMessage(message,
   1.203 +                                                         sts1,
   1.204 +                                                         this.internalMailItem.GetIsInSecureStore(),
   1.205 +                                                         this.internalMailItem.GetIsInSentFolder(),
   1.206 +                                                         isDraft,
   1.207 +                                                         out mirror,
   1.208 +                                                         out processedMessage,
   1.209 +                                                         out processedRating,
   1.210 +                                                         out decryptionFlags);
   1.211 +
   1.212 +                    if (status == Globals.ReturnStatus.Success)
   1.213                      {
   1.214 -                        mirrorMailItem = CryptableMailItem.FindMirrorOMI(this.internalMailItem);
   1.215 -                        if ((mirrorMailItem != null) &&
   1.216 -                            (PEPMessage.Create(mirrorMailItem, out mirror) == Globals.ReturnStatus.Success))
   1.217 +                        // Handle the consumed flag marking for deletion
   1.218 +                        if (decryptionFlags.HasFlag(pEpDecryptFlags.pEpDecryptFlagConsume))
   1.219                          {
   1.220 -                            mirrorFound = true;
   1.221 -
   1.222 -                            // Get UI rating from mirror
   1.223 +                            // Delete the mail item from Outlook
   1.224 +#warning Delete subject logging in release versions
   1.225                              try
   1.226                              {
   1.227 -                                result = AdapterExtensions.ReevaluateMessageRating(mirror, mirror.Rating);
   1.228 +                                Log.Verbose("ProcessAndGetRating: Processed msg with subject " + this.internalMailItem.Subject + " received on " + this.internalMailItem.ReceivedTime.ToString());
   1.229                              }
   1.230 -                            catch (Exception e)
   1.231 +                            catch { }
   1.232 +                            this.internalMailItem.PermanentlyDelete();
   1.233 +                            this.disposeAfterProcessing = true;
   1.234 +
   1.235 +                            Log.Verbose("ProcessAndGetRating: Processed message consumed");
   1.236 +                        }
   1.237 +                        else if (decryptionFlags.HasFlag(pEpDecryptFlags.pEpDecryptFlagIgnore))
   1.238 +                        {
   1.239 +#warning Delete subject logging in release versions
   1.240 +                            try
   1.241                              {
   1.242 -                                Log.Error("ProcessAndGetRating: Error reevaluating mirror rating. " + e.Message);
   1.243 -                                result = mirror.Rating;
   1.244 +                                Log.Verbose("ProcessAndGetRating: Processed msg with subject " + this.internalMailItem.Subject + " received on " + this.internalMailItem.ReceivedTime.ToString());
   1.245                              }
   1.246 +                            catch { }
   1.247 +                            Log.Verbose("ProcessAndGetRating: Processed message ignored.");
   1.248                          }
   1.249 -                    }
   1.250 +                        else
   1.251 +                        {
   1.252 +                            // Save processed message data to Outlook
   1.253 +                            if (processedMessage != null)
   1.254 +                            {
   1.255 +                                // Set the mail item
   1.256 +                                mirrorCreated = false;
   1.257 +                                if (mirror != null)
   1.258 +                                {
   1.259 +                                    if (mirrorMailItem == null)
   1.260 +                                    {
   1.261 +                                        mirrorMailItem = this.CreateMirrorOMI();
   1.262 +                                        mirrorCreated = true;
   1.263 +                                    }
   1.264  
   1.265 -                    // If not untrusted or no mirror found, process mail item
   1.266 -                    if (mirrorFound == false)
   1.267 -                    {
   1.268 -                        sts1 = PEPMessage.Create(this.internalMailItem, out message);
   1.269 +                                    mailItem = mirrorMailItem;
   1.270 +                                }
   1.271 +                                else
   1.272 +                                {
   1.273 +                                    mailItem = this.internalMailItem;
   1.274 +                                }
   1.275  
   1.276 -                        // Process
   1.277 -                        status = msgProcessor.ProcessMessage(message,
   1.278 -                                                             sts1,
   1.279 -                                                             this.internalMailItem.GetIsInSecureStore(),
   1.280 -                                                             this.internalMailItem.GetIsInSentFolder(),
   1.281 -                                                             isDraft,
   1.282 -                                                             out mirror,
   1.283 -                                                             out processedMessage,
   1.284 -                                                             out processedRating,
   1.285 -                                                             out decryptionFlags);
   1.286 +                                // Apply updated data
   1.287 +                                processedMessage.ApplyTo(mailItem, true, false);
   1.288  
   1.289 -                        if (status == Globals.ReturnStatus.Success)
   1.290 -                        {
   1.291 -                            // Handle the consumed flag marking for deletion
   1.292 -                            if (decryptionFlags.HasFlag(pEpDecryptFlags.pEpDecryptFlagConsume))
   1.293 -                            {
   1.294 -                                // Delete the mail item from Outlook
   1.295 -#warning Delete subject logging in release versions
   1.296 -                                try
   1.297 +                                // Save
   1.298 +                                curCount = 1;
   1.299 +                                maxRetryCount = 6;
   1.300 +                                while (true)
   1.301                                  {
   1.302 -                                    Log.Verbose("ProcessAndGetRating: Processed msg with subject " + this.internalMailItem.Subject + " received on " + this.internalMailItem.ReceivedTime.ToString());
   1.303 +                                    try
   1.304 +                                    {
   1.305 +                                        mailItem.Save();
   1.306 +                                        break;
   1.307 +                                    }
   1.308 +                                    catch (Exception e)
   1.309 +                                    {
   1.310 +                                        Log.Warning("ProcessAndGetRating: Failed to save MailItem, try " +
   1.311 +                                                    curCount.ToString() + "/" + maxRetryCount.ToString() + ". Error: " + e.Message);
   1.312 +                                    }
   1.313 +
   1.314 +                                    curCount++;
   1.315 +
   1.316 +                                    if (curCount > maxRetryCount)
   1.317 +                                    {
   1.318 +                                        Log.Error("ProcessAndGetRating: Failed to save MailItem");
   1.319 +
   1.320 +                                        // Delete the mirror if one was created, will try again next time
   1.321 +                                        try
   1.322 +                                        {
   1.323 +                                            if (mirrorCreated)
   1.324 +                                            {
   1.325 +                                                mirrorMailItem.PermanentlyDelete(Globals.ThisAddIn.PEPStoreRootFolder.Store);
   1.326 +                                            }
   1.327 +                                        }
   1.328 +                                        catch
   1.329 +                                        {
   1.330 +                                            Log.Error("ProcessAndGetRating: Failed to delete mirror after decrypt error.");
   1.331 +                                        }
   1.332 +
   1.333 +                                        break;
   1.334 +                                    }
   1.335 +
   1.336 +                                    Thread.Sleep(100); // In MS
   1.337                                  }
   1.338 -                                catch { }
   1.339 -                                this.internalMailItem.PermanentlyDelete();
   1.340 -                                this.disposeAfterProcessing = true;
   1.341 -
   1.342 -                                Log.Verbose("ProcessAndGetRating: Processed message consumed");
   1.343                              }
   1.344 -                            else if (decryptionFlags.HasFlag(pEpDecryptFlags.pEpDecryptFlagIgnore))
   1.345 -                            {
   1.346 -#warning Delete subject logging in release versions
   1.347 -                                try
   1.348 -                                {
   1.349 -                                    Log.Verbose("ProcessAndGetRating: Processed msg with subject " + this.internalMailItem.Subject + " received on " + this.internalMailItem.ReceivedTime.ToString());
   1.350 -                                }
   1.351 -                                catch { }
   1.352 -                                Log.Verbose("ProcessAndGetRating: Processed message ignored.");
   1.353 -                            }
   1.354 +                            // If processed message is null (= didn't run through decryption (again)), update UI rating
   1.355                              else
   1.356                              {
   1.357 -                                // Save processed message data to Outlook
   1.358 -                                if (processedMessage != null)
   1.359 -                                {
   1.360 -                                    // Set the mail item
   1.361 -                                    mirrorCreated = false;
   1.362 -                                    if (mirror != null)
   1.363 -                                    {
   1.364 -                                        if (mirrorMailItem == null)
   1.365 -                                        {
   1.366 -                                            mirrorMailItem = this.CreateMirrorOMI();
   1.367 -                                            mirrorCreated = true;
   1.368 -                                        }
   1.369 -
   1.370 -                                        mailItem = mirrorMailItem;
   1.371 -                                    }
   1.372 -                                    else
   1.373 -                                    {
   1.374 -                                        mailItem = this.internalMailItem;
   1.375 -                                    }
   1.376 -
   1.377 -                                    // Apply updated data
   1.378 -                                    processedMessage.ApplyTo(mailItem, true, false);
   1.379 -
   1.380 -                                    // Save
   1.381 -                                    curCount = 1;
   1.382 -                                    maxRetryCount = 6;
   1.383 -                                    while (true)
   1.384 -                                    {
   1.385 -                                        try
   1.386 -                                        {
   1.387 -                                            mailItem.Save();
   1.388 -                                            break;
   1.389 -                                        }
   1.390 -                                        catch (Exception e)
   1.391 -                                        {
   1.392 -                                            Log.Warning("ProcessAndGetRating: Failed to save MailItem, try " +
   1.393 -                                                        curCount.ToString() + "/" + maxRetryCount.ToString() + ". Error: " + e.Message);
   1.394 -                                        }
   1.395 -
   1.396 -                                        curCount++;
   1.397 -
   1.398 -                                        if (curCount > maxRetryCount)
   1.399 -                                        {
   1.400 -                                            Log.Error("ProcessAndGetRating: Failed to save MailItem");
   1.401 -
   1.402 -                                            // Delete the mirror if one was created, will try again next time
   1.403 -                                            try
   1.404 -                                            {
   1.405 -                                                if (mirrorCreated)
   1.406 -                                                {
   1.407 -                                                    mirrorMailItem.PermanentlyDelete(Globals.ThisAddIn.PEPStoreRootFolder.Store);
   1.408 -                                                }
   1.409 -                                            }
   1.410 -                                            catch
   1.411 -                                            {
   1.412 -                                                Log.Error("ProcessAndGetRating: Failed to delete mirror after decrypt error.");
   1.413 -                                            }
   1.414 -
   1.415 -                                            break;
   1.416 -                                        }
   1.417 -
   1.418 -                                        Thread.Sleep(100); // In MS
   1.419 -                                    }
   1.420 -                                }
   1.421 -                                // If message is null (= didn't run through decryption (again)), update UI rating
   1.422 -                                else
   1.423 -                                {
   1.424 -                                    processedRating = AdapterExtensions.ReevaluateMessageRating(message, processedRating);
   1.425 -                                }
   1.426 +                                processedRating = AdapterExtensions.ReevaluateMessageRating(message, processedRating);
   1.427                              }
   1.428                          }
   1.429  
   1.430 @@ -1365,24 +1399,6 @@
   1.431                      }
   1.432                  }
   1.433  
   1.434 -                /* Apply the custom message class again to secure mail items in secure stores. This is a failsafe method in case
   1.435 -                 * it didn't work the first time above. Again, don't apply to pEp internal messages.
   1.436 -                 */
   1.437 -                if ((isPEPInternal == false) &&
   1.438 -                    (this.disposeAfterProcessing == false) &&
   1.439 -                    (this.internalMailItem.GetIsSecure()))
   1.440 -                {
   1.441 -                    try
   1.442 -                    {
   1.443 -                        MapiHelper.SetProperty(this.internalMailItem, MapiProperty.PidTagMessageClass, MapiPropertyValue.PidTagMessageClassSecurePEP);
   1.444 -                        this.internalMailItem.Save();
   1.445 -                    }
   1.446 -                    catch (Exception e)
   1.447 -                    {
   1.448 -                        Log.Error("ProcessAndGetRating: Could not set custom message class, " + e.ToString());
   1.449 -                    }
   1.450 -                }
   1.451 -
   1.452                  // Save processing status
   1.453                  this._LastProcessedRating = result;
   1.454                  this._LastProcessedStatus = status;
   1.455 @@ -1455,13 +1471,26 @@
   1.456          {
   1.457              MsgProcessor.ProcessingCompletedEventArgs args;
   1.458  
   1.459 +            // Decrement decryption counter after decryption has finished (if necessary)
   1.460 +            if (this.decrementCounter)
   1.461 +            {
   1.462 +                Globals.ThisAddIn.DecrementDecryptionCounter();
   1.463 +            }
   1.464 +
   1.465              // Raise complete event (only if dispose after decryption is not true since .this reference is returned)
   1.466 -            if ((this.disposeAfterProcessing == false) &&
   1.467 -                (e.Error == null) &&
   1.468 -                (e.Cancelled == false))
   1.469 +            if (this.disposeAfterProcessing == false)
   1.470              {
   1.471                  args = new MsgProcessor.ProcessingCompletedEventArgs();
   1.472 -                args.ProcessedRating = (pEpRating)e.Result;
   1.473 +
   1.474 +                // Check for errors during processing but invoke anyhow to complete process (needed to look up mirror).
   1.475 +                if (e.Error == null)
   1.476 +                {
   1.477 +                    args.ProcessedRating = (pEpRating)e.Result;
   1.478 +                }
   1.479 +                else
   1.480 +                {
   1.481 +                    args.ProcessedRating = pEpRating.pEpRatingUndefined;
   1.482 +                }
   1.483  
   1.484                  this.ProcessingCompleted?.Invoke(this, args);
   1.485              }
   1.486 @@ -1922,12 +1951,55 @@
   1.487                   * However, the GetMyIdentity(omi) method requires this information in some cases:
   1.488                   *  • The true 'myself' identity of a mail item is not an account registerd in Outlook. This can happen
   1.489                   *    using aliases or when forwarding emails from another account.
   1.490 -                 *  • This is primarily needed when displaying mirrors themselves then opening the privacy status manager.
   1.491 +                 *  • This is primarily needed when displaying mirrors themselves then opening the handshake dialog.
   1.492                   */
   1.493                  try
   1.494                  {
   1.495 -                    omi.SetUserProperty(MapiProperty.PidTagReceivedByEmailAddress.DaslName,
   1.496 -                                        MapiHelper.GetProperty(this.internalMailItem, MapiProperty.PidTagReceivedByEmailAddress),
   1.497 +                    /* Get the "received by" email address. In case this returns an internal X500 Exchange address ("/O=DOMAIN/OU=EXCHANGE ADMINISTRATIVE GROUP..."),
   1.498 +                     * compare it with the recipients' addresses and take the primary SMTP address.
   1.499 +                     */
   1.500 +                    string email = MapiHelper.GetProperty(this.internalMailItem, MapiProperty.PidTagReceivedByEmailAddress) as string;
   1.501 +                    if (email.StartsWith("/O"))
   1.502 +                    {
   1.503 +                        Outlook.Recipients recipients = null;
   1.504 +                        Outlook.Recipient recipient = null;
   1.505 +                        Outlook.AddressEntry addressEntry = null;
   1.506 +                        Outlook.ExchangeUser exchangeUser = null;
   1.507 +
   1.508 +                        try
   1.509 +                        {
   1.510 +                            recipients = this.internalMailItem.Recipients;
   1.511 +
   1.512 +                            for (int i = 1; i <= recipients.Count; i++)
   1.513 +                            {
   1.514 +                                recipient = recipients[i];
   1.515 +                                addressEntry = recipient?.AddressEntry;
   1.516 +                                exchangeUser = addressEntry?.GetExchangeUser();
   1.517 +
   1.518 +                                if (email.ToUpperInvariant().Equals(exchangeUser?.Address?.ToUpperInvariant()))
   1.519 +                                {
   1.520 +                                    email = exchangeUser?.PrimarySmtpAddress;
   1.521 +                                    break;
   1.522 +                                }
   1.523 +
   1.524 +                                recipient = null;
   1.525 +                                addressEntry = null;
   1.526 +                                exchangeUser = null;
   1.527 +                            }
   1.528 +                        }
   1.529 +                        catch (Exception ex)
   1.530 +                        {
   1.531 +                            Log.Error("CreateMirrorOMI: Error getting ReceivedByEmailAddress. " + ex.ToString());
   1.532 +                        }
   1.533 +                        finally
   1.534 +                        {
   1.535 +                            recipients = null;
   1.536 +                            recipient = null;
   1.537 +                            addressEntry = null;
   1.538 +                            exchangeUser = null;
   1.539 +                        }
   1.540 +                    }
   1.541 +                    omi.SetUserProperty(MapiProperty.PidTagReceivedByEmailAddress.DaslName, email,
   1.542                                          Outlook.OlUserPropertyType.olText);
   1.543                      omi.SetUserProperty(MapiProperty.PidTagReceivedByName.DaslName,
   1.544                                          MapiHelper.GetProperty(this.internalMailItem, MapiProperty.PidTagReceivedByName),
   1.545 @@ -1968,6 +2040,8 @@
   1.546              catch (Exception e)
   1.547              {
   1.548                  Log.Verbose("CreateMirrorOMI: Creating new mail item failed. " + e.Message);
   1.549 +                omi.PermanentlyDelete();
   1.550 +                omi = null;
   1.551              }
   1.552  
   1.553              if (created == false)
   1.554 @@ -1986,7 +2060,14 @@
   1.555                       *    at Microsoft.Office.Interop.Outlook._MailItem.Copy()
   1.556                       * When this happens a new mail item must be created.
   1.557                       */
   1.558 -                    omi = (Outlook.MailItem)this.internalMailItem.Copy();
   1.559 +                    lock (mutexCopiedItemsList)
   1.560 +                    {
   1.561 +                        omi = (Outlook.MailItem)this.internalMailItem.Copy();
   1.562 +                        if (copiedItemsList.Contains(omi.EntryID) == false)
   1.563 +                        {
   1.564 +                            copiedItemsList.Add(omi.EntryID);
   1.565 +                        }
   1.566 +                    }
   1.567                      omi.UnRead = false;
   1.568  
   1.569                      /* Set all received-by information
   1.570 @@ -2041,6 +2122,16 @@
   1.571                  {
   1.572                      Log.Error("CreateMirrorOMI: Creating by copy failed. " + e.Message);
   1.573                  }
   1.574 +                finally
   1.575 +                {
   1.576 +                    lock (mutexCopiedItemsList)
   1.577 +                    {
   1.578 +                        if (copiedItemsList.Contains(omi?.EntryID))
   1.579 +                        {
   1.580 +                            copiedItemsList.Remove(omi?.EntryID);
   1.581 +                        }
   1.582 +                    }
   1.583 +                }
   1.584              }
   1.585  
   1.586              // Release objects
     2.1 --- a/Extensions/MailItemExtensions.cs	Sun Jul 16 11:56:42 2017 +0200
     2.2 +++ b/Extensions/MailItemExtensions.cs	Sun Jul 16 12:39:49 2017 +0200
     2.3 @@ -522,7 +522,84 @@
     2.4          /// <returns>True if the mail item is secure, otherwise false.</returns>
     2.5          public static bool GetIsSecure(this Outlook.MailItem omi)
     2.6          {
     2.7 -            return (PEPMessage.GetIsSecure(omi));
     2.8 +            if (omi != null)
     2.9 +            {
    2.10 +                // PGP/MIME format
    2.11 +                if (omi.GetIsPGPMIMEEncrypted())
    2.12 +                {
    2.13 +                    return (true);
    2.14 +                }
    2.15 +
    2.16 +                // Partitioned or inline PGP format
    2.17 +                if (omi.Body != null && PEPMessage.IsPGPText(omi.Body))
    2.18 +                {
    2.19 +                    return (true);
    2.20 +                }
    2.21 +            }
    2.22 +
    2.23 +            return false;
    2.24 +        }
    2.25 +
    2.26 +        /// <summary>
    2.27 +        /// Determines if the given mail item is encrypted in PGP/MIME format.
    2.28 +        /// </summary>
    2.29 +        /// <param name="omi">The mail item to check encryption for.</param>
    2.30 +        /// <returns>True if the given mail item is PGP/MIME encrypted, otherwise false.</returns>
    2.31 +        public static bool GetIsPGPMIMEEncrypted(this Outlook.MailItem omi)
    2.32 +        {
    2.33 +            bool result = false;
    2.34 +            bool versionInfoFound = false;
    2.35 +            bool contentFound = false;
    2.36 +            PEPAttachment attach;
    2.37 +            Outlook.Attachment attachment = null;
    2.38 +            Outlook.Attachments attachments = null;
    2.39 +
    2.40 +            try
    2.41 +            {
    2.42 +                if (omi != null)
    2.43 +                {
    2.44 +                    attachments = omi.Attachments;
    2.45 +
    2.46 +                    // Require only two attachments (version identification & encrypted content)
    2.47 +                    // However, allow the attachments to be in any order
    2.48 +                    if (attachments.Count == 2)
    2.49 +                    {
    2.50 +                        // Note: attachment index starts at 1
    2.51 +                        for (int i = 1; i <= attachments.Count; i++)
    2.52 +                        {
    2.53 +                            attachment = attachments[i];
    2.54 +                            attach = new PEPAttachment(attachment);
    2.55 +
    2.56 +                            if (attach.IsPGPMIMEVersionInfoFormat)
    2.57 +                            {
    2.58 +                                versionInfoFound = true;
    2.59 +                            }
    2.60 +                            else if (attach.IsPGPMIMEContentFormat)
    2.61 +                            {
    2.62 +                                contentFound = true;
    2.63 +                            }
    2.64 +
    2.65 +                            attachment = null;
    2.66 +                        }
    2.67 +
    2.68 +                        if (versionInfoFound && contentFound)
    2.69 +                        {
    2.70 +                            result = true;
    2.71 +                        }
    2.72 +                    }
    2.73 +                }
    2.74 +            }
    2.75 +            catch (Exception ex)
    2.76 +            {
    2.77 +                Log.Error("GetIsPGPMIMEEncrypted: Error. " + ex.ToString());
    2.78 +            }
    2.79 +            finally
    2.80 +            {
    2.81 +                attachment = null;
    2.82 +                attachments = null;
    2.83 +            }
    2.84 +
    2.85 +            return (result);
    2.86          }
    2.87  
    2.88          /// <summary>
     3.1 --- a/MsgProcessor.cs	Sun Jul 16 11:56:42 2017 +0200
     3.2 +++ b/MsgProcessor.cs	Sun Jul 16 12:39:49 2017 +0200
     3.3 @@ -319,16 +319,38 @@
     3.4                              if ((isInSentFolder == false) &&
     3.5                                  (sentItem == false))
     3.6                              {
     3.7 -                                /* Decrypt message. This is mainly done to check if it's a 
     3.8 -                                 * beacon message. The "decrypted" message and the key list
     3.9 -                                 * can be ignored.
    3.10 +                                /* Decrypt message. This is done to check if it's a beacon
    3.11 +                                 * message and to remove public keys attached to the message.
    3.12 +                                 * The processed message itself can be ignored.
    3.13                                  */
    3.14 -                                PEPMessage dummyMessage;
    3.15 +                                PEPMessage outMessage;
    3.16                                  this.Decrypt(message,
    3.17 -                                             out dummyMessage,
    3.18 +                                             out outMessage,
    3.19                                               out decryptionKeyList,
    3.20                                               out decryptionFlags);
    3.21  
    3.22 +                                /* Check for attached keys and remove them.
    3.23 +                                 * Although this is also done in the engine during decryption,
    3.24 +                                 * it seems safer to only apply the round-trip Outlook mail item
    3.25 +                                 * => PEPMessage => Outlook mail item if really needed.
    3.26 +                                 */ 
    3.27 +                                bool keyFound = false;
    3.28 +
    3.29 +                                for (int i = 0; i < message.Attachments?.Count; i++)
    3.30 +                                {
    3.31 +                                    if (message.Attachments[i].IsKey)
    3.32 +                                    {
    3.33 +                                        message.Attachments.Remove(message.Attachments[i]);
    3.34 +                                        keyFound = true;
    3.35 +                                        i--;
    3.36 +                                    }
    3.37 +                                }
    3.38 +
    3.39 +                                // If a key was found and detached, return updated message
    3.40 +                                if (keyFound)
    3.41 +                                {
    3.42 +                                    processedMessage = message;
    3.43 +                                }
    3.44                              }
    3.45                          }
    3.46                      }
    3.47 @@ -474,9 +496,8 @@
    3.48              //  This updates workingMessage
    3.49              ///////////////////////////////////////////////////////////
    3.50  
    3.51 -            Log.Verbose("ProcessSentMessage: Processing CIDs and flattening recipients.");
    3.52 +            Log.Verbose("ProcessSentMessage: Flattening recipients.");
    3.53  
    3.54 -            workingMessage.NormalizeContentIDs();
    3.55              workingMessage.FlattenAllRecipientIdentities();
    3.56  
    3.57              ///////////////////////////////////////////////////////////
     4.1 --- a/PEPAttachment.cs	Sun Jul 16 11:56:42 2017 +0200
     4.2 +++ b/PEPAttachment.cs	Sun Jul 16 12:39:49 2017 +0200
     4.3 @@ -7,6 +7,7 @@
     4.4  using System.Drawing;
     4.5  using System.IO;
     4.6  using System.Runtime.InteropServices;
     4.7 +using System.Text;
     4.8  using Outlook = Microsoft.Office.Interop.Outlook;
     4.9  
    4.10  namespace pEp
    4.11 @@ -32,6 +33,9 @@
    4.12          /// </summary>
    4.13          public event PropertyChangedEventHandler PropertyChanged;
    4.14  
    4.15 +        public const string                 CID_PREFIX      = @"cid://";
    4.16 +        public const string                 FILE_PREFIX     = @"file://";
    4.17 +
    4.18          private string _ContentId;
    4.19          private byte[] _Data;
    4.20          private string _FileName;
    4.21 @@ -80,9 +84,27 @@
    4.22          /// <param name="b">The blob to build from.</param>
    4.23          public PEPAttachment(Blob b)
    4.24          {
    4.25 -            this._ContentId = null;
    4.26 +            this._ContentId = string.Empty;
    4.27 +            this._FileName = string.Empty;
    4.28 +
    4.29 +            /* If the blob's file name has the content id prefix "cid://", this attachment
    4.30 +             * is embedded and the Filename property has to be assigned to the attachment's
    4.31 +             * ContentId property (we need to trim the prefix as it is referenced from the
    4.32 +             * mail's HTML without it.)
    4.33 +             */ 
    4.34 +            if (b.Filename?.StartsWith(PEPAttachment.CID_PREFIX) ?? false)
    4.35 +            {
    4.36 +                this._ContentId = b.Filename.Substring(PEPAttachment.CID_PREFIX.Length);
    4.37 +            }
    4.38 +            else if (b.Filename?.StartsWith(PEPAttachment.FILE_PREFIX) ?? false)
    4.39 +            {
    4.40 +                this._FileName = b.Filename.Substring(PEPAttachment.FILE_PREFIX.Length);
    4.41 +            }
    4.42 +            else
    4.43 +            {
    4.44 +                this._FileName = b.Filename;
    4.45 +            }
    4.46              this._Data = b.Value;
    4.47 -            this._FileName = b.Filename;
    4.48              this._MimeType = b.MimeType;
    4.49              this._Tag = null;
    4.50          }
    4.51 @@ -343,6 +365,91 @@
    4.52              }
    4.53          }
    4.54  
    4.55 +        /// <summary>
    4.56 +        /// Gets whether this attachment is formatted like PGP/MIME content.
    4.57 +        /// </summary>
    4.58 +        /// <returns>True if the attachment is PGP/MIME content formatted, otherwise false.</returns>
    4.59 +        public bool IsPGPMIMEContentFormat
    4.60 +        {
    4.61 +            get
    4.62 +            {
    4.63 +                bool result = false;
    4.64 +
    4.65 +                if ((this != null) &&
    4.66 +                    (string.IsNullOrEmpty(this.MimeType) == false) &&
    4.67 +                    (string.Equals(this.MimeType.Trim(), "application/octet-stream", StringComparison.OrdinalIgnoreCase)))
    4.68 +                {
    4.69 +                    // Require data to start with PGP text and be at least 100 bytes
    4.70 +                    if ((this.Data != null) &&
    4.71 +                        (this.Data.Length > 100))
    4.72 +                    {
    4.73 +                        // Check for PGP text after converting to string
    4.74 +                        if (PEPMessage.IsPGPText(Encoding.ASCII.GetString(this.Data, 0, 100)))
    4.75 +                        {
    4.76 +                            result = true;
    4.77 +                        }
    4.78 +                    }
    4.79 +                }
    4.80 +
    4.81 +                return (result);
    4.82 +            }
    4.83 +        }
    4.84 +
    4.85 +        /// <summary>
    4.86 +        /// Gets whether this attachment is formatted like a PGP/MIME version identification.
    4.87 +        /// </summary>
    4.88 +        /// <returns>True if the attachment is PGP/MIME version identifcation formatted, otherwise false.</returns>
    4.89 +        public bool IsPGPMIMEVersionInfoFormat
    4.90 +        {
    4.91 +            get
    4.92 +            {
    4.93 +                bool result = false;
    4.94 +
    4.95 +                if ((this != null) &&
    4.96 +                    (string.IsNullOrEmpty(this.MimeType) == false) &&
    4.97 +                    (string.Equals(this.MimeType.Trim(), "application/pgp-encrypted", StringComparison.OrdinalIgnoreCase)))
    4.98 +                {
    4.99 +                    // Allow any data
   4.100 +                    if ((this.Data != null) &&
   4.101 +                        (this.Data.Length > 0))
   4.102 +                    {
   4.103 +                        result = true;
   4.104 +                    }
   4.105 +                }
   4.106 +
   4.107 +                return (result);
   4.108 +            }
   4.109 +        }
   4.110 +
   4.111 +        /// <summary>
   4.112 +        /// Gets whether this attachment is a key.
   4.113 +        /// The logic follows the one applied in the pEpEngine's message_api.c:is_key() function.
   4.114 +        /// </summary>
   4.115 +        /// <returns>True, if the attachment is a key, otherwise false.</returns>
   4.116 +        public bool IsKey
   4.117 +        {
   4.118 +            get
   4.119 +            {
   4.120 +                return
   4.121 +                    (
   4.122 +                    // Workaround for Apple Mail bugs
   4.123 +                    (string.Equals(this.MimeType, "application/x-apple-msg-attachment") &&
   4.124 +                     string.Equals(this.FileExtension, ".asc")) ||
   4.125 +                    // As binary, by file name
   4.126 +                    ((this.MimeType == null ||
   4.127 +                      string.Equals(this.MimeType, "application/octet-stream")) &&
   4.128 +                     (string.Equals(this.FileExtension, ".pgp") || string.Equals(this.FileExtension, ".gpg") ||
   4.129 +                      string.Equals(this.FileExtension, ".key") || string.Equals(this.FileExtension, ".asc"))) ||
   4.130 +                    // Explicit mime type
   4.131 +                    (string.Equals(this.MimeType, "application/pgp-keys")) ||
   4.132 +                    // As text, by file name
   4.133 +                    (string.Equals(this.MimeType, "text/plain") &&
   4.134 +                    (string.Equals(this.FileExtension, ".pgp") || string.Equals(this.FileExtension, ".gpg") ||
   4.135 +                     string.Equals(this.FileExtension, ".key") || string.Equals(this.FileExtension, ".asc")))
   4.136 +                   );
   4.137 +            }
   4.138 +        }
   4.139 +
   4.140          /**************************************************************
   4.141           * 
   4.142           * Methods
   4.143 @@ -368,7 +475,38 @@
   4.144          {
   4.145              Blob b = new Blob();
   4.146              b.Value = (this._Data != null ? this._Data : new byte[0]);
   4.147 -            b.Filename = this._FileName;
   4.148 +
   4.149 +            /* If attachment has content id (= is embedded), assign this content id
   4.150 +             * to the blob's Filename property and add the content id prefix so that 
   4.151 +             * it will be preserved during the engine processing.
   4.152 +             * Else, use file name or empty string if not available.
   4.153 +             */ 
   4.154 +            if (string.IsNullOrEmpty(this._ContentId) == false)
   4.155 +            {
   4.156 +                if (this._ContentId.StartsWith(PEPAttachment.CID_PREFIX))
   4.157 +                {
   4.158 +                    b.Filename = this._ContentId;
   4.159 +                }
   4.160 +                else
   4.161 +                {
   4.162 +                    b.Filename = PEPAttachment.CID_PREFIX + this._ContentId;
   4.163 +                }
   4.164 +            }
   4.165 +            else if (string.IsNullOrEmpty(this._FileName) == false)
   4.166 +            {
   4.167 +                if (this._FileName.StartsWith(PEPAttachment.FILE_PREFIX))
   4.168 +                {
   4.169 +                    b.Filename = this._FileName;
   4.170 +                }
   4.171 +                else
   4.172 +                {
   4.173 +                    b.Filename = PEPAttachment.FILE_PREFIX + this._FileName;
   4.174 +                }
   4.175 +            }
   4.176 +            else
   4.177 +            {
   4.178 +                b.Filename = string.Empty;
   4.179 +            }
   4.180              b.MimeType = this._MimeType;
   4.181  
   4.182              return (b);
     5.1 --- a/PEPIdentity.cs	Sun Jul 16 11:56:42 2017 +0200
     5.2 +++ b/PEPIdentity.cs	Sun Jul 16 12:39:49 2017 +0200
     5.3 @@ -1086,8 +1086,11 @@
     5.4                      if (string.IsNullOrWhiteSpace(address))
     5.5                      {
     5.6                          address = (string)MapiHelper.GetProperty(omi, MapiProperty.PidTagReceivedByEmailAddress);
     5.7 -                        if (address[0] == '/')
     5.8 +                        if (string.IsNullOrEmpty(address) ||
     5.9 +                            (address[0] == '/'))
    5.10 +                        {
    5.11                              address = null;
    5.12 +                        }
    5.13                      }
    5.14  
    5.15                      // Attempt to find the account, test by containing folder
    5.16 @@ -1106,6 +1109,18 @@
    5.17                          // Marshal.ReleaseComObject(parent);
    5.18                      }
    5.19  
    5.20 +                    // Attempt to get the email address from the user property. Do not use it if it is an internal
    5.21 +                    // Exchange X500 address ("/O=DOMAIN/OU=EXCHANGE ADMINISTRATIVE GROUP...").
    5.22 +                    if (string.IsNullOrWhiteSpace(address))
    5.23 +                    {
    5.24 +                        string addr = omi.GetUserProperty(MapiProperty.PidTagReceivedByEmailAddress.DaslName) as string;
    5.25 +                        if ((string.IsNullOrWhiteSpace(addr) == false) &&
    5.26 +                            (addr.StartsWith("/O") == false))
    5.27 +                        {
    5.28 +                            address = addr;
    5.29 +                        }
    5.30 +                    }
    5.31 +
    5.32                      // nothing worked, use default
    5.33                      if (string.IsNullOrWhiteSpace(address))
    5.34                      {
    5.35 @@ -1434,13 +1449,13 @@
    5.36                      }
    5.37                      catch { }
    5.38  
    5.39 -                    /* SenderEmailType showed to be null in some occasions. Therefore, we need to 
    5.40 -                     * doublecheck if this is an Exchange user.
    5.41 -                     */
    5.42                      if (omi.SenderEmailType == "EX")
    5.43                      {
    5.44                          exchangeUser = true;
    5.45                      }
    5.46 +                    /* SenderEmailType showed to be null in some occasions. Therefore, we need to 
    5.47 +                     * doublecheck if this is an Exchange user.
    5.48 +                     */
    5.49                      else if (omi.SenderEmailType == null)
    5.50                      {
    5.51                          try
     6.1 --- a/PEPMessage.cs	Sun Jul 16 11:56:42 2017 +0200
     6.2 +++ b/PEPMessage.cs	Sun Jul 16 12:39:49 2017 +0200
     6.3 @@ -1581,7 +1581,7 @@
     6.4  
     6.5                  foreach (PEPAttachment attach in this._Attachments)
     6.6                  {
     6.7 -                    if (PEPMessage.IsPGPMIMEVersionInfoFormat(attach))
     6.8 +                    if (attach.IsPGPMIMEVersionInfoFormat)
     6.9                      {
    6.10                          data.AddRange(attach.Data);
    6.11                          versionInfoFound = true;
    6.12 @@ -1600,7 +1600,7 @@
    6.13  
    6.14                  foreach (PEPAttachment attach in this._Attachments)
    6.15                  {
    6.16 -                    if (PEPMessage.IsPGPMIMEContentFormat(attach))
    6.17 +                    if (attach.IsPGPMIMEContentFormat)
    6.18                      {
    6.19                          data.AddRange(attach.Data);
    6.20                          contentFound = true;
    6.21 @@ -2327,100 +2327,6 @@
    6.22  
    6.23          /// <summary>
    6.24          /// Determines if the given message is considered secure.
    6.25 -        /// </summary>
    6.26 -        /// <param name="omi">The outlook mail item to check security for.</param>
    6.27 -        /// <returns>True if the given message is encrypted, otherwise false.</returns>
    6.28 -        public static bool GetIsSecure(Outlook.MailItem omi)
    6.29 -        {
    6.30 -            if (omi != null)
    6.31 -            {
    6.32 -                // PGP/MIME format
    6.33 -                if (PEPMessage.GetIsPGPMIMEEncrypted(omi))
    6.34 -                {
    6.35 -                    return (true);
    6.36 -                }
    6.37 -
    6.38 -                // Partitioned or inline PGP format
    6.39 -                if (omi.Body != null && PEPMessage.IsPGPText(omi.Body))
    6.40 -                {
    6.41 -                    return (true);
    6.42 -                }                
    6.43 -            }
    6.44 -
    6.45 -            return (false);
    6.46 -        }
    6.47 -
    6.48 -        /// <summary>
    6.49 -        /// Determines if the given message is encrypted in PGP/MIME format.
    6.50 -        /// </summary>
    6.51 -        /// <param name="msg">The message to check encryption for.</param>
    6.52 -        /// <returns>True if the given message is PGP/MIME encrypted, otherwise false.</returns>
    6.53 -        public static bool GetIsPGPMIMEEncrypted(Outlook.MailItem omi)
    6.54 -        {
    6.55 -            bool result = false;
    6.56 -            bool versionInfoFound = false;
    6.57 -            bool contentFound = false;
    6.58 -            PEPAttachment attach;
    6.59 -            Outlook.Attachment attachment = null;
    6.60 -            Outlook.Attachments attachments = null;
    6.61 -
    6.62 -            try
    6.63 -            {
    6.64 -                if (omi != null)
    6.65 -                {
    6.66 -                    attachments = omi.Attachments;
    6.67 -
    6.68 -                    // Require only two attachments (version identification & encrypted content)
    6.69 -                    // However, allow the attachments to be in any order
    6.70 -                    if (attachments.Count == 2)
    6.71 -                    {
    6.72 -                        // Note: attachment index starts at 1
    6.73 -                        for (int i = 1; i <= attachments.Count; i++)
    6.74 -                        {
    6.75 -                            attachment = attachments[i];
    6.76 -                            attach = new PEPAttachment(attachment);
    6.77 -
    6.78 -                            if (PEPMessage.IsPGPMIMEVersionInfoFormat(attach))
    6.79 -                            {
    6.80 -                                versionInfoFound = true;
    6.81 -                            }
    6.82 -                            else if (PEPMessage.IsPGPMIMEContentFormat(attach))
    6.83 -                            {
    6.84 -                                contentFound = true;
    6.85 -                            }
    6.86 -
    6.87 -                            // Marshal.ReleaseComObject(attachment);
    6.88 -                            attachment = null;
    6.89 -                        }
    6.90 -
    6.91 -                        if (versionInfoFound && contentFound)
    6.92 -                        {
    6.93 -                            result = true;
    6.94 -                        }
    6.95 -                    }
    6.96 -                }
    6.97 -            }
    6.98 -            catch { }
    6.99 -            finally
   6.100 -            {
   6.101 -                if (attachment != null)
   6.102 -                {
   6.103 -                    // Marshal.ReleaseComObject(attachment);
   6.104 -                    attachment = null;
   6.105 -                }
   6.106 -
   6.107 -                if (attachments != null)
   6.108 -                {
   6.109 -                    // Marshal.ReleaseComObject(attachments);
   6.110 -                    attachments = null;
   6.111 -                }
   6.112 -            }
   6.113 -
   6.114 -            return (result);
   6.115 -        }
   6.116 -
   6.117 -        /// <summary>
   6.118 -        /// Determines if the given message is considered secure.
   6.119          /// Currently, only PGP encrypted messages will be detected.
   6.120          /// </summary>
   6.121          /// <param name="msg">The message to check security for.</param>
   6.122 @@ -2464,11 +2370,11 @@
   6.123                  {
   6.124                      foreach (PEPAttachment attach in msg.Attachments)
   6.125                      {
   6.126 -                        if (PEPMessage.IsPGPMIMEVersionInfoFormat(attach))
   6.127 +                        if (attach.IsPGPMIMEVersionInfoFormat)
   6.128                          {
   6.129                              versionInfoFound = true;
   6.130                          }
   6.131 -                        else if (PEPMessage.IsPGPMIMEContentFormat(attach))
   6.132 +                        else if (attach.IsPGPMIMEContentFormat)
   6.133                          {
   6.134                              contentFound = true;
   6.135                          }
   6.136 @@ -2483,58 +2389,6 @@
   6.137  
   6.138              return (result);
   6.139          }
   6.140 -
   6.141 -        /// <summary>
   6.142 -        /// Determines if the given attachment is formatted like a PGP/MIME version identification.
   6.143 -        /// </summary>
   6.144 -        /// <param name="attachment">The attachment to check.</param>
   6.145 -        /// <returns>True if the attachment is PGP/MIME version identifcation formatted, otherwise false.</returns>
   6.146 -        private static bool IsPGPMIMEVersionInfoFormat(PEPAttachment attachment)
   6.147 -        {
   6.148 -            bool result = false;
   6.149 -
   6.150 -            if ((attachment != null) &&
   6.151 -                (string.IsNullOrEmpty(attachment.MimeType) == false) &&
   6.152 -                (string.Equals(attachment.MimeType.Trim(), "application/pgp-encrypted", StringComparison.OrdinalIgnoreCase)))
   6.153 -            {
   6.154 -                // Allow any data
   6.155 -                if ((attachment.Data != null) &&
   6.156 -                    (attachment.Data.Length > 0))
   6.157 -                {
   6.158 -                    result = true;
   6.159 -                }
   6.160 -            }
   6.161 -
   6.162 -            return (result);
   6.163 -        }
   6.164 -
   6.165 -        /// <summary>
   6.166 -        /// Determines if the given attachment is formatted like PGP/MIME content.
   6.167 -        /// </summary>
   6.168 -        /// <param name="attachment">The attachment to check.</param>
   6.169 -        /// <returns>True if the attachment is PGP/MIME content formatted, otherwise false.</returns>
   6.170 -        private static bool IsPGPMIMEContentFormat(PEPAttachment attachment)
   6.171 -        {
   6.172 -            bool result = false;
   6.173 -
   6.174 -            if ((attachment != null) &&
   6.175 -                (string.IsNullOrEmpty(attachment.MimeType) == false) &&
   6.176 -                (string.Equals(attachment.MimeType.Trim(), "application/octet-stream", StringComparison.OrdinalIgnoreCase)))
   6.177 -            {
   6.178 -                // Require data to start with PGP text and be at least 100 bytes
   6.179 -                if ((attachment.Data != null) &&
   6.180 -                    (attachment.Data.Length > 100))
   6.181 -                {
   6.182 -                    // Check for PGP text after converting to string
   6.183 -                    if (PEPMessage.IsPGPText(Encoding.ASCII.GetString(attachment.Data, 0, 100)))
   6.184 -                    {
   6.185 -                        result = true;
   6.186 -                    }
   6.187 -                }
   6.188 -            }
   6.189 -
   6.190 -            return (result);
   6.191 -        }
   6.192      }
   6.193  
   6.194      /// <summary>
     7.1 --- a/Properties/AssemblyInfo.cs	Sun Jul 16 11:56:42 2017 +0200
     7.2 +++ b/Properties/AssemblyInfo.cs	Sun Jul 16 12:39:49 2017 +0200
     7.3 @@ -43,5 +43,5 @@
     7.4  // You can specify all the values or you can default the Build and Revision Numbers 
     7.5  // by using the '*' as shown below:
     7.6  // [assembly: AssemblyVersion("1.0.*")]
     7.7 -[assembly: AssemblyVersion("1.1.18.0")]
     7.8 -[assembly: AssemblyFileVersion("1.1.18.0")]
     7.9 +[assembly: AssemblyVersion("1.1.19.0")]
    7.10 +[assembly: AssemblyFileVersion("1.1.19.0")]
     8.1 --- a/ThisAddIn.cs	Sun Jul 16 11:56:42 2017 +0200
     8.2 +++ b/ThisAddIn.cs	Sun Jul 16 12:39:49 2017 +0200
     8.3 @@ -2,6 +2,7 @@
     8.4  using pEp.UI;
     8.5  using pEpCOMServerAdapterLib;
     8.6  using System;
     8.7 +using System.Collections.Concurrent;
     8.8  using System.Collections.Generic;
     8.9  using System.ComponentModel;
    8.10  using System.Diagnostics;
    8.11 @@ -59,6 +60,13 @@
    8.12          private List<WatchedFolder>        watchedFolders            = new List<WatchedFolder>();
    8.13          private System.Windows.Forms.Timer inboxCleaner              = null;
    8.14  
    8.15 +        private Stack<string> decryptionStack                        = new Stack<string>();
    8.16 +        internal System.Windows.Forms.Timer DecryptionTimer          = null;
    8.17 +        private int DecryptionCounter                                = 0;
    8.18 +        private object mutexDecryptionCounter                        = new object();
    8.19 +        private object mutexDecryptionStack                          = new object();
    8.20 +        private const int MAX_DECRYPTION_COUNT                       = 5;
    8.21 +
    8.22          /**************************************************************
    8.23           * 
    8.24           * Property Accessors
    8.25 @@ -178,6 +186,136 @@
    8.26              return new RibbonCustomizations();
    8.27          }
    8.28  
    8.29 +        /// <summary>
    8.30 +        /// Increments the decryption counter if maximum has not yet been reached.
    8.31 +        /// </summary>
    8.32 +        /// <returns>True if counter was encrypted, false if maximum has already been reached.</returns>
    8.33 +        public bool IncrementDecryptionCounter()
    8.34 +        {
    8.35 +            bool incremented = false;
    8.36 +
    8.37 +            lock (mutexDecryptionCounter)
    8.38 +            {
    8.39 +                if (this.DecryptionCounter < ThisAddIn.MAX_DECRYPTION_COUNT)
    8.40 +                {
    8.41 +                    this.DecryptionCounter++;
    8.42 +                    incremented = true;
    8.43 +                }
    8.44 +            }
    8.45 +
    8.46 +            return incremented;
    8.47 +        }
    8.48 +
    8.49 +        /// <summary>
    8.50 +        /// Decrements the decryption counter if it is bigger than 0.
    8.51 +        /// </summary>
    8.52 +        public void DecrementDecryptionCounter()
    8.53 +        {
    8.54 +            lock (mutexDecryptionCounter)
    8.55 +            {
    8.56 +                if (this.DecryptionCounter > 0)
    8.57 +                {
    8.58 +                    this.DecryptionCounter--;
    8.59 +                }
    8.60 +            }
    8.61 +        }
    8.62 +
    8.63 +        /// <summary>
    8.64 +        /// Resets the decryption counter to 0.
    8.65 +        /// </summary>
    8.66 +        public void ResetDecryptionCounter()
    8.67 +        {
    8.68 +            lock (mutexDecryptionCounter)
    8.69 +            {
    8.70 +                this.DecryptionCounter = 0;
    8.71 +            }
    8.72 +        }
    8.73 +
    8.74 +        /// <summary>
    8.75 +        /// Locks the decryption stack and tries to get its first element.
    8.76 +        /// Also checks if the following element(s) are duplicates of the one
    8.77 +        /// returned and removes them in this case.
    8.78 +        /// </summary>
    8.79 +        /// <param name="entryId">The retrieved entryId or an empty string if none was retrieved.</param>
    8.80 +        /// <returns>Tue if the operation retrieved successfully an entryId. Otherwise false.</returns>
    8.81 +        public bool TryPopDecryptionStack(out string entryId)
    8.82 +        {
    8.83 +            entryId = string.Empty;
    8.84 +
    8.85 +            lock (mutexDecryptionStack)
    8.86 +            {
    8.87 +                try
    8.88 +                {
    8.89 +                    if (this.decryptionStack?.Count > 0)
    8.90 +                    {
    8.91 +                        entryId = this.decryptionStack.Pop();
    8.92 +                    }
    8.93 +                }
    8.94 +                catch (Exception ex)
    8.95 +                {
    8.96 +                    entryId = string.Empty;
    8.97 +                    Log.Error("TryPopDecryptionStack: Error getting first element from stack. " + ex.ToString());
    8.98 +                }
    8.99 +
   8.100 +                // If an entryId could be retrieved, check following item(s)
   8.101 +                if (string.IsNullOrEmpty(entryId) == false)
   8.102 +                {
   8.103 +                    do
   8.104 +                    {
   8.105 +                        if (this.decryptionStack?.Count > 0)
   8.106 +                        {
   8.107 +                            string nextEntryId = string.Empty;
   8.108 +
   8.109 +                            try
   8.110 +                            {
   8.111 +                                nextEntryId = this.decryptionStack.Peek();
   8.112 +                            }
   8.113 +                            catch (Exception ex)
   8.114 +                            {
   8.115 +                                Log.Error("TryPopDecryptionStack: Error removing duplicates from stack. " + ex.ToString());
   8.116 +                            }
   8.117 +
   8.118 +                            // If next item equals the retrieved entryId, remove it
   8.119 +                            if (string.Equals(entryId, nextEntryId))
   8.120 +                            {
   8.121 +                                try
   8.122 +                                {
   8.123 +                                    this.decryptionStack.Pop();
   8.124 +                                }
   8.125 +                                catch (Exception ex)
   8.126 +                                {
   8.127 +                                    Log.Error("TryPopDecryptionStack: Error removing duplicates from stack. " + ex.ToString());
   8.128 +                                    break;
   8.129 +                                }
   8.130 +                            }
   8.131 +                            else
   8.132 +                            {
   8.133 +                                break;
   8.134 +                            }
   8.135 +                        }
   8.136 +                        else
   8.137 +                        {
   8.138 +                            break;
   8.139 +                        }
   8.140 +
   8.141 +                    } while (true);
   8.142 +                }
   8.143 +            }
   8.144 +
   8.145 +            return (string.IsNullOrEmpty(entryId) == false);
   8.146 +        }
   8.147 +
   8.148 +        /// <summary>
   8.149 +        /// Locks the decryption stack and pushes the given entryId.
   8.150 +        /// </summary>
   8.151 +        /// <param name="entryId">The entryId to push to the stack.</param>
   8.152 +        public void PushToDecryptionStack(string entryId)
   8.153 +        {
   8.154 +            lock (mutexDecryptionStack)
   8.155 +            {
   8.156 +                this.decryptionStack.Push(entryId);
   8.157 +            }
   8.158 +        }
   8.159  
   8.160          /// <summary>
   8.161          /// This updates the accounts list in pEp settings with the ones found in Outlook, adding new accounts and removing
   8.162 @@ -1486,34 +1624,26 @@
   8.163                              this.watchedFolders.Add(new WatchedFolder(folder, Outlook.OlDefaultFolders.olFolderInbox));
   8.164                          }
   8.165  
   8.166 -                        /* Add Sent folder if account type is ActiveSync (to delete beacons and decrypt if necessary)
   8.167 -                         * or if Outlook Version is 2010 or earlier. In the latter case, this serves to set the custom
   8.168 -                         * message class "IPM.Note.Secure.pEp" to the mail item, which is needed so that the unencrypted 
   8.169 -                         * preview on untrusted servers is always visible on the first click.
   8.170 -                         */
   8.171 -                        if (store.GetIsAccountType(Outlook.OlAccountType.olEas) || 
   8.172 -                            Globals.GetOutlookVersion() == Globals.Version.Outlook2010)
   8.173 +                        // Add Sent folder. This is needed so sent mails get decrypted on trusted servers to make the search function working
   8.174 +                        try
   8.175                          {
   8.176 -                            try
   8.177 -                            {
   8.178 -                                folder = (Outlook.Folder)store.GetDefaultFolder(Outlook.OlDefaultFolders.olFolderSentMail);
   8.179 -                            }
   8.180 -                            catch
   8.181 -                            {
   8.182 -                                if (folder != null)
   8.183 -                                {
   8.184 -                                    // Marshal.ReleaseComObject(folder);
   8.185 -                                    folder = null;
   8.186 -                                }
   8.187 -
   8.188 -                                Log.Warning("ConnectWatchedFolders: Failure getting default sent folder");
   8.189 -                            }
   8.190 -
   8.191 -                            // Add the folder to the watched list (do not release it)
   8.192 +                            folder = (Outlook.Folder)store.GetDefaultFolder(Outlook.OlDefaultFolders.olFolderSentMail);
   8.193 +                        }
   8.194 +                        catch
   8.195 +                        {
   8.196                              if (folder != null)
   8.197                              {
   8.198 -                                this.watchedFolders.Add(new WatchedFolder(folder, Outlook.OlDefaultFolders.olFolderSentMail));
   8.199 +                                // Marshal.ReleaseComObject(folder);
   8.200 +                                folder = null;
   8.201                              }
   8.202 +
   8.203 +                            Log.Warning("ConnectWatchedFolders: Failure getting default sent folder");
   8.204 +                        }
   8.205 +
   8.206 +                        // Add the folder to the watched list (do not release it)
   8.207 +                        if (folder != null)
   8.208 +                        {
   8.209 +                            this.watchedFolders.Add(new WatchedFolder(folder, Outlook.OlDefaultFolders.olFolderSentMail));
   8.210                          }
   8.211  
   8.212                          //Marshal.ReleaseComObject(store);
   8.213 @@ -2704,6 +2834,13 @@
   8.214              Log.Info("ThisAddIn_Startup: Connect Events");
   8.215              Globals.ConnectEvents(true);
   8.216  
   8.217 +            Log.Info("ThisAddin_Startup: Connect decryption timer");
   8.218 +            this.DecryptionTimer = new System.Windows.Forms.Timer
   8.219 +            {
   8.220 +                Interval = 500
   8.221 +            };
   8.222 +            this.DecryptionTimer.Tick += DecryptionTimer_Tick;
   8.223 +
   8.224              /* Open the pEp store
   8.225               * Warning: This must be done BEFORE settings because settings will determine its visibility
   8.226               */
   8.227 @@ -2923,7 +3060,7 @@
   8.228                                                      if (DateTime.UtcNow - (mailItem.ReceivedTime.ToUniversalTime()) > TimeSpan.FromSeconds(Globals.TIMEOUT_SYNC_MESSAGE))
   8.229                                                      {
   8.230                                                          mailItem.PermanentlyDelete();
   8.231 -                                                    }                                                   
   8.232 +                                                    }
   8.233                                                  }
   8.234                                                  catch (Exception ex)
   8.235                                                  {
   8.236 @@ -2980,6 +3117,69 @@
   8.237          }
   8.238  
   8.239          /// <summary>
   8.240 +        /// Event handler for when the decryption timer has elapsed.
   8.241 +        /// </summary>
   8.242 +        private void DecryptionTimer_Tick(object sender, EventArgs e)
   8.243 +        {
   8.244 +            bool process = false;
   8.245 +            bool itemFound = true;
   8.246 +            Outlook.MailItem omi = null;
   8.247 +            string entryId = string.Empty;
   8.248 +            CryptableMailItem cmi = null;
   8.249 +
   8.250 +            // Try to increment the decryption counter
   8.251 +            if (this.IncrementDecryptionCounter() == false)
   8.252 +            {
   8.253 +                return;
   8.254 +            }
   8.255 +
   8.256 +            // Pop items from the decryption stack 
   8.257 +            do
   8.258 +            {
   8.259 +                if (this.TryPopDecryptionStack(out entryId))
   8.260 +                {
   8.261 +                    // Get mail item and set process flag to true
   8.262 +                    omi = this.Application.Session.GetItemFromID(entryId);
   8.263 +                    if (omi != null)
   8.264 +                    {
   8.265 +                        process = true;
   8.266 +                    }
   8.267 +                }
   8.268 +                else
   8.269 +                {
   8.270 +                    // If TryPop returns false, this means that no more item is
   8.271 +                    // in the decryption list
   8.272 +                    itemFound = false;
   8.273 +                }
   8.274 +
   8.275 +            } while (process == false && itemFound);
   8.276 +
   8.277 +            // Process the mail item
   8.278 +            if (process)
   8.279 +            {
   8.280 +                cmi = new CryptableMailItem(omi, true);
   8.281 +
   8.282 +                /* Note: Make sure to dispose the MailItem after processing (pass true).
   8.283 +                 * This is needed as decryption is asynchronous but the MailItem reference still needs to be released.
   8.284 +                 * Do NOT use omi or cmi references after this point!
   8.285 +                 */
   8.286 +                cmi.StartProcessing(true);
   8.287 +            }
   8.288 +            else
   8.289 +            {
   8.290 +                // If we don't process, decrement the decryption counter again
   8.291 +                this.DecrementDecryptionCounter();
   8.292 +            }
   8.293 +
   8.294 +            // If no more item is on the stack, stop the timer and set decryption counter to 0
   8.295 +            if (itemFound == false)
   8.296 +            {
   8.297 +                this.ResetDecryptionCounter();
   8.298 +                this.DecryptionTimer.Stop();
   8.299 +            }
   8.300 +        }
   8.301 +
   8.302 +        /// <summary>
   8.303          /// Event handler for when the add-in is shutdown.
   8.304          /// Importantly: In Outlook, The Shutdown event is raised only when the user 
   8.305          /// disables the VSTO Add-in by using the COM Add-ins dialog box in Outlook. 
   8.306 @@ -3181,7 +3381,6 @@
   8.307              string[] idList;
   8.308              Outlook.NameSpace ns = this.Application.Session;
   8.309              Outlook.MailItem omi = null;
   8.310 -            CryptableMailItem cmi;
   8.311  
   8.312              Log.Verbose("Application_NewMailEx: Started processing new items.");
   8.313  
   8.314 @@ -3197,13 +3396,12 @@
   8.315                          // Only process if pEp is enabled
   8.316                          if (omi.GetIsPEPEnabled())
   8.317                          {
   8.318 -                            cmi = new CryptableMailItem(omi);
   8.319 -
   8.320 -                            /* Note: Make sure to dispose the MailItem after processing (pass true).
   8.321 -                             * This is needed as decryption is asynchronous but the MailItem reference still needs to be released.
   8.322 -                             * Do NOT use omi or cmi references after this point!
   8.323 -                             */
   8.324 -                            cmi.StartProcessing(true);
   8.325 +                            this.PushToDecryptionStack(eid);
   8.326 +
   8.327 +                            if (this.DecryptionTimer.Enabled == false)
   8.328 +                            {
   8.329 +                                this.DecryptionTimer.Start();
   8.330 +                            }
   8.331                          }
   8.332                          else
   8.333                          {
   8.334 @@ -3406,7 +3604,7 @@
   8.335                                          Log.Verbose("Application_ItemSend: Error applying encrypted message. " + e.Message);
   8.336                                          sentMessage.ApplyTo(omi, false, false);
   8.337                                          throw;
   8.338 -                                    }                             
   8.339 +                                    }
   8.340                                  }
   8.341                                  else
   8.342                                  {
   8.343 @@ -3416,7 +3614,7 @@
   8.344                                      {
   8.345                                          /* Send the messages directly and catch errors the same way as in case of
   8.346                                           * only one message being applied directly (see above).                                      
   8.347 -                                         */ 
   8.348 +                                         */
   8.349                                          try
   8.350                                          {
   8.351                                              this.SendWithoutProcessing(messages[i], true, false);
   8.352 @@ -4071,7 +4269,6 @@
   8.353                  bool process = false;
   8.354                  object propValue = null;
   8.355                  Outlook.MailItem omi = null;
   8.356 -                CryptableMailItem cmi;
   8.357  
   8.358                  try
   8.359                  {
   8.360 @@ -4082,7 +4279,7 @@
   8.361  
   8.362                      if (omi != null)
   8.363                      {
   8.364 -                        // Check, if message is a beacon or keysync message and delete it in this case
   8.365 +                        // Check, if message is a beacon or keysync message
   8.366                          omi.GetPEPProperty(MailItemExtensions.PEPProperty.AutoConsume, out propValue, null);
   8.367                          if (propValue != null)
   8.368                          {
   8.369 @@ -4097,16 +4294,44 @@
   8.370                                  process = true;
   8.371                              }
   8.372                          }
   8.373 -
   8.374 +                        /* Else, process the mail item and decrypt it. This is needed especially for Sent folders as sent mails are normally
   8.375 +                         * not read and therefore remain encrypted and not searchable in this folder.
   8.376 +                         * 
   8.377 +                         * NOTE: Via debugging, it could be determined that the Copy() method called during the creation of a mirror item on
   8.378 +                         * untrusted servers most likely raises internally the Items.ItemAdd event. Therefore, we add the copied item's EntryID
   8.379 +                         * to a list of copied items during its processing and check here if the newly added mail item is in this list. If yes,
   8.380 +                         * do not process it. However, if for whatever reason, the ItemAdd event for one of those copied items was to be raised 
   8.381 +                         * at a later stage, we might run into problems with a loop of copied items that will be processed, copied during creation
   8.382 +                         * of the mirror, raise an ItemAdd event for the new copy and so on.
   8.383 +                         */
   8.384 +                        else if (CryptableMailItem.IsInCopiedItemsList(omi?.EntryID) == false)
   8.385 +                        {
   8.386 +                            // Do not process Sent folders of secure stores (not needed)
   8.387 +                            if ((omi.GetIsInSecureStore()) &&
   8.388 +                                (this.defaultFolder == Outlook.OlDefaultFolders.olFolderSentMail))
   8.389 +                            {
   8.390 +                                if (Globals.GetOutlookVersion() == Globals.Version.Outlook2010)
   8.391 +                                {
   8.392 +                                    MapiHelper.SetProperty(omi, MapiProperty.PidTagMessageClass, MapiPropertyValue.PidTagMessageClassSecurePEP);
   8.393 +                                    omi.Save();
   8.394 +                                }
   8.395 +                            }
   8.396 +                            else
   8.397 +                            {
   8.398 +                                process = true;
   8.399 +                            }
   8.400 +
   8.401 +                        }
   8.402 +
   8.403 +                        // If this item is to be processed, add to decryption stack
   8.404                          if (process)
   8.405                          {
   8.406 -                            cmi = new CryptableMailItem(omi);
   8.407 -
   8.408 -                            /* Note: Make sure to dispose the MailItem after processing (pass true).
   8.409 -                             * This is needed as decryption is asynchronous but the MailItem reference still needs to be released.
   8.410 -                             * Do NOT use omi or cmi references after this point!
   8.411 -                             */
   8.412 -                            cmi.StartProcessing(true);
   8.413 +                            Globals.ThisAddIn.PushToDecryptionStack(omi.EntryID);
   8.414 +
   8.415 +                            if (Globals.ThisAddIn.DecryptionTimer.Enabled == false)
   8.416 +                            {
   8.417 +                                Globals.ThisAddIn.DecryptionTimer.Start();
   8.418 +                            }
   8.419                          }
   8.420                      }
   8.421                  }
     9.1 --- a/UI/FormControlPreviewMessage.xaml	Sun Jul 16 11:56:42 2017 +0200
     9.2 +++ b/UI/FormControlPreviewMessage.xaml	Sun Jul 16 12:39:49 2017 +0200
     9.3 @@ -15,6 +15,7 @@
     9.4              <!-- Converters -->
     9.5              <local:InvertBoolConverter x:Key="InvertBool" />
     9.6              <local:IconToImageSourceConverter x:Key="IconToImageSource" />
     9.7 +            <local:HasNonInlineAttachmentsConverter x:Key="HasNonInlineAttachments" />
     9.8              <local:MultiBooleanToVisibilityConverter x:Key="MultiBooleanToVisibility" />
     9.9              <local:ValueConverterGroup x:Key="InvertBoolToVisibility">
    9.10                  <local:InvertBoolConverter />
    9.11 @@ -24,6 +25,10 @@
    9.12                  <local:IsListEmptyConverter />
    9.13                  <local:InvertBoolConverter />
    9.14              </local:ValueConverterGroup>
    9.15 +            <local:ValueConverterGroup x:Key="IsStringEmptyToVisibility">
    9.16 +                <local:IsStringEmptyConverter />
    9.17 +                <BooleanToVisibilityConverter />
    9.18 +            </local:ValueConverterGroup>
    9.19  
    9.20              <!-- Dictionary -->
    9.21              <ResourceDictionary.MergedDictionaries>
    9.22 @@ -232,6 +237,9 @@
    9.23                          <Binding Path="Message.Attachments"
    9.24                                   Mode="OneWay"
    9.25                                   Converter="{StaticResource IsListEmptyToInvertBool}" />
    9.26 +                        <Binding Path="Message.Attachments"
    9.27 +                                 Mode="OneWay"
    9.28 +                                 Converter="{StaticResource HasNonInlineAttachments}" />
    9.29                          <Binding Path="IsNoteModeEnabled"
    9.30                                   Mode="OneWay"
    9.31                                   Converter="{StaticResource InvertBool}" />
    9.32 @@ -239,94 +247,98 @@
    9.33                  </ItemsControl.Visibility>
    9.34                  <ItemsControl.ItemTemplate>
    9.35                      <DataTemplate>
    9.36 -                        <Border BorderBrush="DarkGray"
    9.37 -                                BorderThickness="1"
    9.38 -                                Height="35"
    9.39 -                                Width="180"
    9.40 -                                Margin="2">
    9.41 -                            <Grid>
    9.42 -                                <Grid.ColumnDefinitions>
    9.43 -                                    <ColumnDefinition Width="*" />
    9.44 -                                    <ColumnDefinition Width="20" />
    9.45 -                                </Grid.ColumnDefinitions>
    9.46 -                                <Grid.RowDefinitions>
    9.47 -                                    <RowDefinition Height="*" />
    9.48 -                                </Grid.RowDefinitions>
    9.49 -                                <ContentControl MouseDoubleClick="ButtonAttachment_DoubleClick">
    9.50 -                                    <Grid Grid.Column="0" Grid.Row="0">
    9.51 -                                        <Grid.ColumnDefinitions>
    9.52 -                                            <ColumnDefinition Width="30" />
    9.53 -                                            <ColumnDefinition Width="*" />
    9.54 -                                        </Grid.ColumnDefinitions>
    9.55 -                                        <Grid.RowDefinitions>
    9.56 -                                            <RowDefinition Height="*" />
    9.57 -                                            <RowDefinition Height="*" />
    9.58 -                                        </Grid.RowDefinitions>
    9.59 +                        <StackPanel Visibility="{Binding Path=ContentId, Converter={StaticResource IsStringEmptyToVisibility}}">
    9.60 +                            <Border BorderBrush="DarkGray"
    9.61 +                                    BorderThickness="1"
    9.62 +                                    Height="35"
    9.63 +                                    Width="180"
    9.64 +                                    Margin="2">
    9.65 +                                <Grid>
    9.66 +                                    <Grid.ColumnDefinitions>
    9.67 +                                        <ColumnDefinition Width="*" />
    9.68 +                                        <ColumnDefinition Width="20" />
    9.69 +                                    </Grid.ColumnDefinitions>
    9.70 +                                    <Grid.RowDefinitions>
    9.71 +                                        <RowDefinition Height="*" />
    9.72 +                                    </Grid.RowDefinitions>
    9.73 +                                    <ContentControl MouseDoubleClick="ButtonAttachment_DoubleClick">
    9.74 +                                        <Grid Grid.Column="0"
    9.75 +                                              Grid.Row="0">
    9.76 +                                            <Grid.ColumnDefinitions>
    9.77 +                                                <ColumnDefinition Width="30" />
    9.78 +                                                <ColumnDefinition Width="*" />
    9.79 +                                            </Grid.ColumnDefinitions>
    9.80 +                                            <Grid.RowDefinitions>
    9.81 +                                                <RowDefinition Height="*" />
    9.82 +                                                <RowDefinition Height="*" />
    9.83 +                                            </Grid.RowDefinitions>
    9.84  
    9.85 -                                        <!-- File icon -->
    9.86 -                                        <Image Grid.Column="0"
    9.87 -                                       Grid.Row="0"
    9.88 -                                       Grid.RowSpan="2"
    9.89 -                                       Stretch="Uniform"
    9.90 -                                       Source="{Binding Path='FileIcon', Mode=OneWay, Converter={StaticResource IconToImageSource}}"
    9.91 -                                       HorizontalAlignment="Stretch"
    9.92 -                                       VerticalAlignment="Stretch"
    9.93 -                                       Margin="5" />
    9.94 +                                            <!-- File icon -->
    9.95 +                                            <Image Grid.Column="0"
    9.96 +                                                   Grid.Row="0"
    9.97 +                                                   Grid.RowSpan="2"
    9.98 +                                                   Stretch="Uniform"
    9.99 +                                                   Source="{Binding Path='FileIcon', Mode=OneWay, Converter={StaticResource IconToImageSource}}"
   9.100 +                                                   HorizontalAlignment="Stretch"
   9.101 +                                                   VerticalAlignment="Stretch"
   9.102 +                                                   Margin="5" />
   9.103  
   9.104 -                                        <!-- File name and size -->
   9.105 -                                        <TextBlock Grid.Column="1"
   9.106 -                                           Grid.Row="0"
   9.107 -                                           HorizontalAlignment="Stretch"
   9.108 -                                           VerticalAlignment="Bottom"
   9.109 -                                           Text="{Binding Path='FileName', Mode=OneWay}" />
   9.110 -                                        <TextBlock Grid.Column="1"
   9.111 -                                           Grid.Row="1"
   9.112 -                                           HorizontalAlignment="Stretch"
   9.113 -                                           VerticalAlignment="Center"
   9.114 -                                           Text="{Binding Path='FileSizeString', Mode=OneWay}" />
   9.115 -                                    </Grid>
   9.116 -                                </ContentControl>
   9.117 -                                <Grid Grid.Column="1" Grid.Row="0">
   9.118 -                                    <!-- Attachment actions -->
   9.119 -                                    <!-- The button needs to pass both the PEPAttachment and DataContext to the ContextMenu
   9.120 +                                            <!-- File name and size -->
   9.121 +                                            <TextBlock Grid.Column="1"
   9.122 +                                                       Grid.Row="0"
   9.123 +                                                       HorizontalAlignment="Stretch"
   9.124 +                                                       VerticalAlignment="Bottom"
   9.125 +                                                       Text="{Binding Path='FileName', Mode=OneWay}" />
   9.126 +                                            <TextBlock Grid.Column="1"
   9.127 +                                                       Grid.Row="1"
   9.128 +                                                       HorizontalAlignment="Stretch"
   9.129 +                                                       VerticalAlignment="Center"
   9.130 +                                                       Text="{Binding Path='FileSizeString', Mode=OneWay}" />
   9.131 +                                        </Grid>
   9.132 +                                    </ContentControl>
   9.133 +                                    <Grid Grid.Column="1"
   9.134 +                                          Grid.Row="0">
   9.135 +                                        <!-- Attachment actions -->
   9.136 +                                        <!-- The button needs to pass both the PEPAttachment and DataContext to the ContextMenu
   9.137                                       This is a little tricky because the ContextMenu is outside of the visual tree. 
   9.138                                       In order to do this, the Button Tag is set to the DataContext and the Tag is the UserControl DataContext.
   9.139                                       You cannot Set the Button DataContext to the UserControl DataContext or the PEPAttachment is lost. -->
   9.140 -                                    <Button Name="ButtonAttachment"
   9.141 -                                        Grid.Column="0"
   9.142 -                                        Grid.Row="0"
   9.143 -                                        Style="{StaticResource {x:Static ToolBar.ButtonStyleKey}}"
   9.144 -                                        BorderThickness="0"
   9.145 -                                        HorizontalContentAlignment="Center"
   9.146 -                                        VerticalContentAlignment="Center"
   9.147 -                                        HorizontalAlignment="Stretch"
   9.148 -                                        VerticalAlignment="Stretch"
   9.149 -                                        ContextMenuService.Placement="Bottom"
   9.150 -                                        Tag="{Binding Path='DataContext', RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}}"
   9.151 -                                        DataContext="{Binding Path=.}"
   9.152 -                                        Click="ButtonAttachment_Click">
   9.153 -                                        <Button.ContextMenu>
   9.154 -                                            <ContextMenu Name="ContextMenuAttachment"
   9.155 -                                                     Placement="Bottom"
   9.156 -                                                     DataContext="{Binding Path='PlacementTarget', RelativeSource={RelativeSource Self}}">
   9.157 -                                                <MenuItem Header="{Binding Path='Tag.OpenText'}"
   9.158 -                                                      Tag="{Binding Path='DataContext'}"
   9.159 -                                                      Click="MenuItemOpen_Click" />
   9.160 -                                                <Separator />
   9.161 -                                                <MenuItem Header="{Binding Path='Tag.SaveAsText'}"
   9.162 -                                                      Tag="{Binding Path='DataContext'}"
   9.163 -                                                      Click="MenuItemSaveAs_Click" />
   9.164 -                                            </ContextMenu>
   9.165 -                                        </Button.ContextMenu>
   9.166 -                                        <TextBlock Text="▼"
   9.167 -                                               TextAlignment="Center"
   9.168 -                                               FontFamily="Arial"
   9.169 -                                               FontSize="9"
   9.170 -                                               Foreground="DarkGray" />
   9.171 -                                    </Button>
   9.172 +                                        <Button Name="ButtonAttachment"
   9.173 +                                                Grid.Column="0"
   9.174 +                                                Grid.Row="0"
   9.175 +                                                Style="{StaticResource {x:Static ToolBar.ButtonStyleKey}}"
   9.176 +                                                BorderThickness="0"
   9.177 +                                                HorizontalContentAlignment="Center"
   9.178 +                                                VerticalContentAlignment="Center"
   9.179 +                                                HorizontalAlignment="Stretch"
   9.180 +                                                VerticalAlignment="Stretch"
   9.181 +                                                ContextMenuService.Placement="Bottom"
   9.182 +                                                Tag="{Binding Path='DataContext', RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}}"
   9.183 +                                                DataContext="{Binding Path=.}"
   9.184 +                                                Click="ButtonAttachment_Click">
   9.185 +                                            <Button.ContextMenu>
   9.186 +                                                <ContextMenu Name="ContextMenuAttachment"
   9.187 +                                                             Placement="Bottom"
   9.188 +                                                             DataContext="{Binding Path='PlacementTarget', RelativeSource={RelativeSource Self}}">
   9.189 +                                                    <MenuItem Header="{Binding Path='Tag.OpenText'}"
   9.190 +                                                              Tag="{Binding Path='DataContext'}"
   9.191 +                                                              Click="MenuItemOpen_Click" />
   9.192 +                                                    <Separator />
   9.193 +                                                    <MenuItem Header="{Binding Path='Tag.SaveAsText'}"
   9.194 +                                                              Tag="{Binding Path='DataContext'}"
   9.195 +                                                              Click="MenuItemSaveAs_Click" />
   9.196 +                                                </ContextMenu>
   9.197 +                                            </Button.ContextMenu>
   9.198 +                                            <TextBlock Text="▼"
   9.199 +                                                       TextAlignment="Center"
   9.200 +                                                       FontFamily="Arial"
   9.201 +                                                       FontSize="9"
   9.202 +                                                       Foreground="DarkGray" />
   9.203 +                                        </Button>
   9.204 +                                    </Grid>
   9.205                                  </Grid>
   9.206 -                            </Grid>
   9.207 -                        </Border>
   9.208 +                            </Border>
   9.209 +                        </StackPanel>
   9.210                      </DataTemplate>
   9.211                  </ItemsControl.ItemTemplate>
   9.212                  <ItemsControl.ItemsPanel>
   9.213 @@ -343,6 +355,9 @@
   9.214                          <Binding Path="Message.Attachments"
   9.215                                   Mode="OneWay"
   9.216                                   Converter="{StaticResource IsListEmptyToInvertBool}" />
   9.217 +                        <Binding Path="Message.Attachments"
   9.218 +                                 Mode="OneWay"
   9.219 +                                 Converter="{StaticResource HasNonInlineAttachments}" />
   9.220                          <Binding Path="IsNoteModeEnabled"
   9.221                                   Mode="OneWay"
   9.222                                   Converter="{StaticResource InvertBool}" />
    10.1 --- a/UI/FormControlPreviewMessage.xaml.cs	Sun Jul 16 11:56:42 2017 +0200
    10.2 +++ b/UI/FormControlPreviewMessage.xaml.cs	Sun Jul 16 12:39:49 2017 +0200
    10.3 @@ -246,16 +246,30 @@
    10.4  
    10.5                  // Take Images names from HTML to seek them in Attachments 
    10.6                  List<string> imagesNames = new List<string>();
    10.7 +                List<string> imagesTags = new List<string>();
    10.8                  try
    10.9                  {
   10.10 -                    foreach (Match m in Regex.Matches(html, "<img.+?src=[\"'](.+?)[\"'].+?>", RegexOptions.IgnoreCase | RegexOptions.Multiline))
   10.11 +                    foreach (Match m in Regex.Matches(html, "<img[^>]*>", RegexOptions.IgnoreCase | RegexOptions.Multiline))
   10.12 +                    {                                            
   10.13 +                            if (string.IsNullOrEmpty(m.Value) == false)
   10.14 +                            {
   10.15 +                                imagesTags.Add(m.Value);                                
   10.16 +                            }                       
   10.17 +                    }
   10.18 +                    if (imagesTags.Count > 0)
   10.19                      {
   10.20 -                        if (m.Groups?.Count > 1)
   10.21 +                        foreach (string imgTag in imagesTags)
   10.22                          {
   10.23 -                            string src = m.Groups[1].Value;
   10.24 -                            if (string.IsNullOrEmpty(src) == false)
   10.25 +                            foreach (Match m in Regex.Matches(imgTag, "cid:(.+?)[\"']", RegexOptions.IgnoreCase | RegexOptions.Multiline))
   10.26                              {
   10.27 -                                imagesNames.Add(src.Replace("cid:", ""));
   10.28 +                                if (m.Groups?.Count > 1)
   10.29 +                                {
   10.30 +                                    string src = m.Groups[1].Value;
   10.31 +                                    if (string.IsNullOrEmpty(src) == false)
   10.32 +                                    {
   10.33 +                                        imagesNames.Add(src);
   10.34 +                                    }
   10.35 +                                }
   10.36                              }
   10.37                          }
   10.38                      }
   10.39 @@ -277,13 +291,17 @@
   10.40                  List<Bitmap> images = new List<Bitmap>();
   10.41                  try
   10.42                  {
   10.43 -                    foreach (var attachmentImage in this.displayState?.Message?.Attachments?.Where(i => imagesNames.Contains(i.FileName)))
   10.44 +                    foreach (var imageName in imagesNames)
   10.45                      {
   10.46 +                        var attachmentImage = this.displayState?.Message?.Attachments?.SingleOrDefault(i => i.ContentId == imageName);
   10.47                          Bitmap img = null;
   10.48  
   10.49                          try
   10.50                          {
   10.51 -                            img = (new ImageConverter()).ConvertFrom(attachmentImage.Data) as Bitmap;
   10.52 +                            if(attachmentImage != null)
   10.53 +                            {
   10.54 +                                img = (new ImageConverter()).ConvertFrom(attachmentImage.Data) as Bitmap;
   10.55 +                            }                            
   10.56                          }
   10.57                          catch (Exception e)
   10.58                          {
    11.1 --- a/UI/FormRegionPrivacyStatus.cs	Sun Jul 16 11:56:42 2017 +0200
    11.2 +++ b/UI/FormRegionPrivacyStatus.cs	Sun Jul 16 12:39:49 2017 +0200
    11.3 @@ -616,11 +616,7 @@
    11.4                      this.cryptableMailItem.ProcessingCompleted += MailItem_ProcessingCompleted;
    11.5                      this.cryptableMailItem.GetMirrorCompleted += MailItem_GetMirrorCompleted;
    11.6                      this.cryptableMailItem.Send += MailItem_Send;
    11.7 -
    11.8 -                    if (this.cryptableMailItem.IsSecurelyStored)
    11.9 -                    {
   11.10 -                        this.cryptableMailItem.Open += MailItem_Open;
   11.11 -                    }
   11.12 +                    this.cryptableMailItem.Open += MailItem_Open;
   11.13                  }
   11.14                  catch { }
   11.15              }
    12.1 --- a/UI/HandshakeDialog.xaml	Sun Jul 16 11:56:42 2017 +0200
    12.2 +++ b/UI/HandshakeDialog.xaml	Sun Jul 16 12:39:49 2017 +0200
    12.3 @@ -97,6 +97,11 @@
    12.4                              </Style>
    12.5                          </StackPanel.Style>
    12.6  
    12.7 +                        <!--Separator between items-->
    12.8 +                        <Separator Margin="5,5,5,0"
    12.9 +                                   Background="LightGray"
   12.10 +                                   Visibility="{Binding Path=IsSeparatorVisible, Converter={StaticResource BoolToVisibility}}" />
   12.11 +
   12.12                          <!--The list entry-->
   12.13                          <Grid Name="IdentityGrid"
   12.14                                Background="Transparent"
   12.15 @@ -272,12 +277,6 @@
   12.16                                          Visibility="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, Path=Mode, Converter={StaticResource IsNotStandardModeToVisibility}, ConverterParameter=Standard}" />
   12.17                              </StackPanel>
   12.18                          </StackPanel>
   12.19 -
   12.20 -                        <!--Separator between items-->
   12.21 -                        <Separator Margin="5,5,5,0"
   12.22 -                                   Background="LightGray"
   12.23 -                                   Visibility="{Binding Path=IsSeparatorVisible, Converter={StaticResource BoolToVisibility}}" />
   12.24 -
   12.25                      </StackPanel>
   12.26                  </DataTemplate>
   12.27              </ItemsControl.ItemTemplate>
    13.1 --- a/UI/HandshakeDialog.xaml.cs	Sun Jul 16 11:56:42 2017 +0200
    13.2 +++ b/UI/HandshakeDialog.xaml.cs	Sun Jul 16 12:39:49 2017 +0200
    13.3 @@ -614,6 +614,10 @@
    13.4                  catch (COMException ex)
    13.5                  {
    13.6                      this.Myself.Rating = pEpRating.pEpRatingUndefined;
    13.7 +                    Log.Warning("BuildState: Failed to get myself identity rating, COM exception raised. " + ex.ToString());
    13.8 +                }
    13.9 +                catch (Exception ex)
   13.10 +                {
   13.11                      Log.Warning("BuildState: Failed to get myself identity rating, " + ex.ToString());
   13.12                  }
   13.13  
   13.14 @@ -759,12 +763,9 @@
   13.15                              addItemToDialog = false;
   13.16                          }
   13.17  
   13.18 -                        index++;
   13.19 -
   13.20                          if (addItemToDialog)
   13.21                          {
   13.22 -                            item.IsSeparatorVisible = (index != this.Recipients.Count);
   13.23 -
   13.24 +                            item.IsSeparatorVisible = (index++ != 0);
   13.25                              this.Items.Add(item);
   13.26                          }
   13.27                      }
   13.28 @@ -776,11 +777,11 @@
   13.29  
   13.30                  /* If no item was added to the list, it means that no action can be taken. The dialog
   13.31                   * then opens with only a text, which has to be adjusted according to the message's UI rating.
   13.32 -                 */ 
   13.33 +                 */
   13.34                  if (this.Items?.Count == 0)
   13.35                  {
   13.36                      switch (this._MessageRating)
   13.37 -                    {                
   13.38 +                    {
   13.39                          case pEpRating.pEpRatingCannotDecrypt:
   13.40                          case pEpRating.pEpRatingHaveNoKey:
   13.41                              if (this._IsIncoming)
    14.1 --- a/UI/RibbonCustomizations.cs	Sun Jul 16 11:56:42 2017 +0200
    14.2 +++ b/UI/RibbonCustomizations.cs	Sun Jul 16 12:39:49 2017 +0200
    14.3 @@ -8,6 +8,7 @@
    14.4  using System.Windows.Forms;
    14.5  using Office = Microsoft.Office.Core;
    14.6  using Outlook = Microsoft.Office.Interop.Outlook;
    14.7 +using System.Linq; 
    14.8  
    14.9  namespace pEp
   14.10  {
   14.11 @@ -929,8 +930,18 @@
   14.12          #region IRibbonExtensibility Members
   14.13  
   14.14          public string GetCustomUI(string ribbonID)
   14.15 -        {
   14.16 -            return GetResourceText("pEp.UI.RibbonCustomizations.xml");
   14.17 +        {            
   14.18 +            switch(ribbonID)
   14.19 +            {
   14.20 +                case "Microsoft.Outlook.Explorer":
   14.21 +                    return GetResourceText("pEp.UI.RibbonCustomizationsExplorer.xml");
   14.22 +                case "Microsoft.Outlook.Mail.Compose":
   14.23 +                    return GetResourceText("pEp.UI.RibbonCustomizationsCompose.xml");
   14.24 +                case "Microsoft.Outlook.Mail.Read":
   14.25 +                    return GetResourceText("pEp.UI.RibbonCustomizationsRead.xml");
   14.26 +                default:
   14.27 +                    return GetResourceText("pEp.UI.RibbonCustomizations.xml");
   14.28 +            }
   14.29          }
   14.30  
   14.31          #endregion
    15.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    15.2 +++ b/UI/RibbonCustomizationsCompose.xml	Sun Jul 16 12:39:49 2017 +0200
    15.3 @@ -0,0 +1,117 @@
    15.4 +<?xml version="1.0" encoding="UTF-8"?>
    15.5 +<customUI xmlns="http://schemas.microsoft.com/office/2009/07/customui"
    15.6 +          onLoad="Ribbon_Load">
    15.7 +  <ribbon>
    15.8 +    <tabs>
    15.9 +      <tab idMso="TabNewMailMessage">
   15.10 +        <group id="GroupPEPNewMailMessage"
   15.11 +               insertAfterMso="GroupMessageOptions"
   15.12 +               label="p≡p"
   15.13 +               getVisible="GroupPEPNewMailMessage_GetVisible">
   15.14 +          <toggleButton id="ToggleButtonForceUnencrypted"
   15.15 +                        size="large"
   15.16 +                        getEnabled="ToggleButtonForceUnencrypted_GetEnabled"
   15.17 +                        getImage="ToggleButtonForceUnencrypted_GetImage"
   15.18 +                        getLabel="ToggleButtonForceUnencrypted_GetLabel"
   15.19 +                        getPressed="ToggleButtonForceUnencrypted_GetPressed"
   15.20 +                        getSupertip="ToggleButtonForceUnencrypted_GetSupertip"
   15.21 +                        getVisible="ToggleButtonForceUnencrypted_GetVisible"
   15.22 +                        onAction="ToggleButtonForceUnencrypted_Click" />
   15.23 +          <toggleButton id="ToggleButtonForceEncrypted"
   15.24 +                        size="large"
   15.25 +                        getImage="ToggleButtonForceEncrypted_GetImage"
   15.26 +                        getLabel="ToggleButtonForceEncrypted_GetLabel"
   15.27 +                        getPressed="ToggleButtonForceEncrypted_GetPressed"
   15.28 +                        getSupertip="ToggleButtonForceEncrypted_GetSupertip"
   15.29 +                        getVisible="ToggleButtonForceEncrypted_GetVisible"
   15.30 +                        onAction="ToggleButtonForceEncrypted_Click" />
   15.31 +          <toggleButton id="ToggleButtonNeverUnsecure"
   15.32 +                        size="large"
   15.33 +                        getEnabled="ToggleButtonNeverUnsecure_GetEnabled"
   15.34 +                        getImage="ToggleButtonNeverUnsecure_GetImage"
   15.35 +                        getLabel="ToggleButtonNeverUnsecure_GetLabel"
   15.36 +                        getPressed="ToggleButtonNeverUnsecure_GetPressed"
   15.37 +                        getSupertip="ToggleButtonNeverUnsecure_GetSupertip"
   15.38 +                        getVisible="ToggleButtonNeverUnsecure_GetVisible"
   15.39 +                        onAction="ToggleButtonNeverUnsecure_Click" />
   15.40 +          <button id="ButtonUpgradePEPNewMessage"
   15.41 +                  size="large"
   15.42 +                  getEnabled="ButtonUpgradePEP_GetEnabled"
   15.43 +                  getImage="ButtonUpgradePEP_GetImage"
   15.44 +                  getLabel="ButtonUpgradePEP_GetLabel"
   15.45 +                  getSupertip="ButtonUpgradePEP_GetSupertip"
   15.46 +                  getVisible="ButtonUpgradePEP_GetVisible"
   15.47 +                  onAction="ButtonUpgradePEP_Click" />
   15.48 +        </group>
   15.49 +      </tab>            
   15.50 +    </tabs>
   15.51 +  </ribbon>
   15.52 +  <backstage>
   15.53 +    <!-- Available Mso for insertAfterMso: FileSaveAs, TabRecent, TabPrint, TabHelp, ApplicationOptionsDialog -->
   15.54 +    <tab id="BackstagePEP"
   15.55 +         label="p≡p"
   15.56 +         title="p≡p for Outlook"
   15.57 +         columnWidthPercent="65"
   15.58 +         insertAfterMso="ApplicationOptionsDialog"
   15.59 +         visible="true" >
   15.60 +      <firstColumn>
   15.61 +        <!-- Account Options -->
   15.62 +        <group id="GroupAccounts"
   15.63 +               getLabel="GroupAccounts_GetLabel"
   15.64 +               getHelperText="GroupAccounts_GetHelperText">
   15.65 +          <primaryItem>
   15.66 +            <button id="ButtonAccounts"
   15.67 +                    isDefinitive="true"
   15.68 +                    onAction="ButtonAccounts_Click"
   15.69 +                    getImage="ButtonAccounts_GetImage"
   15.70 +                    getLabel="ButtonAccounts_GetLabel" />
   15.71 +          </primaryItem>
   15.72 +        </group>
   15.73 +        <!-- Compatibility options -->
   15.74 +        <group id="GroupCompatibility"
   15.75 +               getLabel="GroupCompatibility_GetLabel"
   15.76 +               getHelperText="GroupCompatibility_GetHelperText">
   15.77 +          <primaryItem>
   15.78 +            <button id="ButtonCompatibility"
   15.79 +                    isDefinitive="true"
   15.80 +                    onAction="ButtonCompatibility_Click"
   15.81 +                    getImage="ButtonCompatibility_GetImage"
   15.82 +                    getLabel="ButtonCompatibility_GetLabel"/>
   15.83 +          </primaryItem>
   15.84 +        </group>
   15.85 +      </firstColumn>
   15.86 +      <secondColumn>
   15.87 +        <!-- About -->
   15.88 +        <group id="GroupAbout">
   15.89 +          <topItems>
   15.90 +            <layoutContainer id="LayoutContainer3"
   15.91 +                             layoutChildren="vertical">
   15.92 +              <imageControl id="ImageControlLogo"
   15.93 +                            getImage="ImageControlLogo_GetImage"
   15.94 +                            visible="true" />
   15.95 +              <labelControl id="Spacer1"
   15.96 +                            label=" " />
   15.97 +              <labelControl id="LabelControlName"
   15.98 +                            getLabel="LabelControlName_GetLabel"
   15.99 +                            alignLabel="right" />
  15.100 +              <labelControl id="LabelControlCopyright"
  15.101 +                            getLabel="LabelControlCopyright_GetLabel"
  15.102 +                            alignLabel="right"/>
  15.103 +              <labelControl id="LabelControlVersion"
  15.104 +                            getLabel="LabelControlVersion_GetLabel"
  15.105 +                            alignLabel="right"/>
  15.106 +            </layoutContainer>
  15.107 +          </topItems>
  15.108 +          <bottomItems>
  15.109 +            <labelControl id="LabelSpacer1"
  15.110 +                          label=" "/>
  15.111 +            <hyperlink id="HyperlinkPEP"
  15.112 +                       label="www.prettyeasyprivacy.com"
  15.113 +                       target="https://prettyeasyprivacy.com"/>
  15.114 +          </bottomItems>
  15.115 +        </group>
  15.116 +      </secondColumn>
  15.117 +    </tab>
  15.118 +  </backstage>
  15.119 +</customUI>
  15.120 +
    16.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    16.2 +++ b/UI/RibbonCustomizationsExplorer.xml	Sun Jul 16 12:39:49 2017 +0200
    16.3 @@ -0,0 +1,109 @@
    16.4 +<?xml version="1.0" encoding="UTF-8"?>
    16.5 +<customUI xmlns="http://schemas.microsoft.com/office/2009/07/customui"
    16.6 +          onLoad="Ribbon_Load">
    16.7 +  <ribbon>
    16.8 +    <tabs>               
    16.9 +      <tab idMso="TabMail">
   16.10 +        <group id="GroupUpgradePEPHome"
   16.11 +               insertAfterMso="GroupSendReceive"
   16.12 +               label="p≡p"
   16.13 +               getVisible="GroupUpgradePEP_GetVisible">
   16.14 +          <button id="ButtonUpgradePEPHome"
   16.15 +                  size="large"
   16.16 +                  getEnabled="ButtonUpgradePEP_GetEnabled"
   16.17 +                  getImage="ButtonUpgradePEP_GetImage"
   16.18 +                  getLabel="ButtonUpgradePEP_GetLabel"
   16.19 +                  getSupertip="ButtonUpgradePEP_GetSupertip"
   16.20 +                  getVisible="ButtonUpgradePEP_GetVisible"
   16.21 +                  onAction="ButtonUpgradePEP_Click" />
   16.22 +        </group>
   16.23 +      </tab>
   16.24 +    </tabs>
   16.25 +    <contextualTabs>
   16.26 +      <tabSet idMso="TabComposeTools">
   16.27 +        <tab idMso="TabMessage">
   16.28 +          <group id="GroupUpgradePEPComposeMessageInline"
   16.29 +                 label="p≡p"
   16.30 +                 getVisible="GroupUpgradePEP_GetVisible">
   16.31 +            <button id="ButtonUpgradePEPComposeMessageInline"
   16.32 +                    size="large"
   16.33 +                    getEnabled="ButtonUpgradePEP_GetEnabled"
   16.34 +                    getImage="ButtonUpgradePEP_GetImage"
   16.35 +                    getLabel="ButtonUpgradePEP_GetLabel"
   16.36 +                    getSupertip="ButtonUpgradePEP_GetSupertip"
   16.37 +                    getVisible="ButtonUpgradePEP_GetVisible"
   16.38 +                    onAction="ButtonUpgradePEP_Click" />
   16.39 +          </group>
   16.40 +        </tab>
   16.41 +      </tabSet>
   16.42 +    </contextualTabs>
   16.43 +  </ribbon>
   16.44 +  <backstage>
   16.45 +    <!-- Available Mso for insertAfterMso: FileSaveAs, TabRecent, TabPrint, TabHelp, ApplicationOptionsDialog -->
   16.46 +    <tab id="BackstagePEP"
   16.47 +         label="p≡p"
   16.48 +         title="p≡p for Outlook"
   16.49 +         columnWidthPercent="65"
   16.50 +         insertAfterMso="ApplicationOptionsDialog"
   16.51 +         visible="true" >
   16.52 +      <firstColumn>
   16.53 +        <!-- Account Options -->
   16.54 +        <group id="GroupAccounts"
   16.55 +               getLabel="GroupAccounts_GetLabel"
   16.56 +               getHelperText="GroupAccounts_GetHelperText">
   16.57 +          <primaryItem>
   16.58 +            <button id="ButtonAccounts"
   16.59 +                    isDefinitive="true"
   16.60 +                    onAction="ButtonAccounts_Click"
   16.61 +                    getImage="ButtonAccounts_GetImage"
   16.62 +                    getLabel="ButtonAccounts_GetLabel" />
   16.63 +          </primaryItem>
   16.64 +        </group>
   16.65 +        <!-- Compatibility options -->
   16.66 +        <group id="GroupCompatibility"
   16.67 +               getLabel="GroupCompatibility_GetLabel"
   16.68 +               getHelperText="GroupCompatibility_GetHelperText">
   16.69 +          <primaryItem>
   16.70 +            <button id="ButtonCompatibility"
   16.71 +                    isDefinitive="true"
   16.72 +                    onAction="ButtonCompatibility_Click"
   16.73 +                    getImage="ButtonCompatibility_GetImage"
   16.74 +                    getLabel="ButtonCompatibility_GetLabel"/>
   16.75 +          </primaryItem>
   16.76 +        </group>
   16.77 +      </firstColumn>
   16.78 +      <secondColumn>
   16.79 +        <!-- About -->
   16.80 +        <group id="GroupAbout">
   16.81 +          <topItems>
   16.82 +            <layoutContainer id="LayoutContainer3"
   16.83 +                             layoutChildren="vertical">
   16.84 +              <imageControl id="ImageControlLogo"
   16.85 +                            getImage="ImageControlLogo_GetImage"
   16.86 +                            visible="true" />
   16.87 +              <labelControl id="Spacer1"
   16.88 +                            label=" " />
   16.89 +              <labelControl id="LabelControlName"
   16.90 +                            getLabel="LabelControlName_GetLabel"
   16.91 +                            alignLabel="right" />
   16.92 +              <labelControl id="LabelControlCopyright"
   16.93 +                            getLabel="LabelControlCopyright_GetLabel"
   16.94 +                            alignLabel="right"/>
   16.95 +              <labelControl id="LabelControlVersion"
   16.96 +                            getLabel="LabelControlVersion_GetLabel"
   16.97 +                            alignLabel="right"/>
   16.98 +            </layoutContainer>
   16.99 +          </topItems>
  16.100 +          <bottomItems>
  16.101 +            <labelControl id="LabelSpacer1"
  16.102 +                          label=" "/>
  16.103 +            <hyperlink id="HyperlinkPEP"
  16.104 +                       label="www.prettyeasyprivacy.com"
  16.105 +                       target="https://prettyeasyprivacy.com"/>
  16.106 +          </bottomItems>
  16.107 +        </group>
  16.108 +      </secondColumn>
  16.109 +    </tab>
  16.110 +  </backstage>
  16.111 +</customUI>
  16.112 +
    17.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    17.2 +++ b/UI/RibbonCustomizationsRead.xml	Sun Jul 16 12:39:49 2017 +0200
    17.3 @@ -0,0 +1,92 @@
    17.4 +<?xml version="1.0" encoding="UTF-8"?>
    17.5 +<customUI xmlns="http://schemas.microsoft.com/office/2009/07/customui"
    17.6 +          onLoad="Ribbon_Load">
    17.7 +  <ribbon>
    17.8 +    <tabs>        
    17.9 +      <tab idMso="TabReadMessage">
   17.10 +        <group id="GroupUpgradePEPReadMessage"
   17.11 +               insertAfterMso="GroupZoom"
   17.12 +               label="p≡p"
   17.13 +               getVisible="GroupUpgradePEP_GetVisible">
   17.14 +          <button id="ButtonUpgradePEPReadMessage"
   17.15 +                  size="large"
   17.16 +                  getEnabled="ButtonUpgradePEP_GetEnabled"
   17.17 +                  getImage="ButtonUpgradePEP_GetImage"
   17.18 +                  getLabel="ButtonUpgradePEP_GetLabel"
   17.19 +                  getSupertip="ButtonUpgradePEP_GetSupertip"
   17.20 +                  getVisible="ButtonUpgradePEP_GetVisible"
   17.21 +                  onAction="ButtonUpgradePEP_Click" />
   17.22 +        </group>
   17.23 +      </tab>   
   17.24 +    </tabs>
   17.25 +   
   17.26 +  </ribbon>
   17.27 +  <backstage>
   17.28 +    <!-- Available Mso for insertAfterMso: FileSaveAs, TabRecent, TabPrint, TabHelp, ApplicationOptionsDialog -->
   17.29 +    <tab id="BackstagePEP"
   17.30 +         label="p≡p"
   17.31 +         title="p≡p for Outlook"
   17.32 +         columnWidthPercent="65"
   17.33 +         insertAfterMso="ApplicationOptionsDialog"
   17.34 +         visible="true" >
   17.35 +      <firstColumn>
   17.36 +        <!-- Account Options -->
   17.37 +        <group id="GroupAccounts"
   17.38 +               getLabel="GroupAccounts_GetLabel"
   17.39 +               getHelperText="GroupAccounts_GetHelperText">
   17.40 +          <primaryItem>
   17.41 +            <button id="ButtonAccounts"
   17.42 +                    isDefinitive="true"
   17.43 +                    onAction="ButtonAccounts_Click"
   17.44 +                    getImage="ButtonAccounts_GetImage"
   17.45 +                    getLabel="ButtonAccounts_GetLabel" />
   17.46 +          </primaryItem>
   17.47 +        </group>
   17.48 +        <!-- Compatibility options -->
   17.49 +        <group id="GroupCompatibility"
   17.50 +               getLabel="GroupCompatibility_GetLabel"
   17.51 +               getHelperText="GroupCompatibility_GetHelperText">
   17.52 +          <primaryItem>
   17.53 +            <button id="ButtonCompatibility"
   17.54 +                    isDefinitive="true"
   17.55 +                    onAction="ButtonCompatibility_Click"
   17.56 +                    getImage="ButtonCompatibility_GetImage"
   17.57 +                    getLabel="ButtonCompatibility_GetLabel"/>
   17.58 +          </primaryItem>
   17.59 +        </group>
   17.60 +      </firstColumn>
   17.61 +      <secondColumn>
   17.62 +        <!-- About -->
   17.63 +        <group id="GroupAbout">
   17.64 +          <topItems>
   17.65 +            <layoutContainer id="LayoutContainer3"
   17.66 +                             layoutChildren="vertical">
   17.67 +              <imageControl id="ImageControlLogo"
   17.68 +                            getImage="ImageControlLogo_GetImage"
   17.69 +                            visible="true" />
   17.70 +              <labelControl id="Spacer1"
   17.71 +                            label=" " />
   17.72 +              <labelControl id="LabelControlName"
   17.73 +                            getLabel="LabelControlName_GetLabel"
   17.74 +                            alignLabel="right" />
   17.75 +              <labelControl id="LabelControlCopyright"
   17.76 +                            getLabel="LabelControlCopyright_GetLabel"
   17.77 +                            alignLabel="right"/>
   17.78 +              <labelControl id="LabelControlVersion"
   17.79 +                            getLabel="LabelControlVersion_GetLabel"
   17.80 +                            alignLabel="right"/>
   17.81 +            </layoutContainer>
   17.82 +          </topItems>
   17.83 +          <bottomItems>
   17.84 +            <labelControl id="LabelSpacer1"
   17.85 +                          label=" "/>
   17.86 +            <hyperlink id="HyperlinkPEP"
   17.87 +                       label="www.prettyeasyprivacy.com"
   17.88 +                       target="https://prettyeasyprivacy.com"/>
   17.89 +          </bottomItems>
   17.90 +        </group>
   17.91 +      </secondColumn>
   17.92 +    </tab>
   17.93 +  </backstage>
   17.94 +</customUI>
   17.95 +
    18.1 --- a/UI/ValueConverters.cs	Sun Jul 16 11:56:42 2017 +0200
    18.2 +++ b/UI/ValueConverters.cs	Sun Jul 16 12:39:49 2017 +0200
    18.3 @@ -10,6 +10,116 @@
    18.4  namespace pEp.UI
    18.5  {
    18.6      /// <summary>
    18.7 +    /// Returns transparent if false or white if true or error.
    18.8 +    /// </summary>
    18.9 +    public class BooleanToBackgroundConverter : IValueConverter
   18.10 +    {
   18.11 +        public object Convert(object value,
   18.12 +                              Type targetType,
   18.13 +                              object parameter,
   18.14 +                              CultureInfo culture)
   18.15 +        {
   18.16 +            System.Windows.Media.Brush color = System.Windows.Media.Brushes.Transparent;
   18.17 +
   18.18 +            if ((value is bool) &&
   18.19 +                ((bool)value))
   18.20 +            {
   18.21 +                color = System.Windows.Media.Brushes.White;
   18.22 +            }
   18.23 +
   18.24 +            return color;
   18.25 +        }
   18.26 +
   18.27 +        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
   18.28 +        {
   18.29 +            throw new NotImplementedException();
   18.30 +        }
   18.31 +    }
   18.32 +
   18.33 +    /// <summary>
   18.34 +    /// Converter to check whether a PEPMessage has attachments that are not
   18.35 +    /// inline attachments.
   18.36 +    /// </summary>
   18.37 +    public class HasNonInlineAttachmentsConverter : IValueConverter
   18.38 +    {
   18.39 +        public object Convert(object value,
   18.40 +                              Type targetType,
   18.41 +                              object parameter,
   18.42 +                              CultureInfo culture)
   18.43 +        {
   18.44 +            if (value is List<PEPAttachment>)
   18.45 +            {
   18.46 +                var attachments = value as List<PEPAttachment>;
   18.47 +
   18.48 +                foreach (var attachment in attachments)
   18.49 +                {
   18.50 +                    if (string.IsNullOrEmpty(attachment.ContentId))
   18.51 +                    {
   18.52 +                        return true;
   18.53 +                    }
   18.54 +                }
   18.55 +
   18.56 +                return false;
   18.57 +            }
   18.58 +            else
   18.59 +            {
   18.60 +                throw new ArgumentException("Input has to be List<PEPAttachment>.");
   18.61 +            }
   18.62 +        }
   18.63 +
   18.64 +        public object ConvertBack(object value,
   18.65 +                                  Type targetType,
   18.66 +                                  object parameter,
   18.67 +                                  CultureInfo culture)
   18.68 +        {
   18.69 +            throw new NotImplementedException();
   18.70 +        }
   18.71 +    }
   18.72 +
   18.73 +    /// <summary>
   18.74 +    /// Converter to change an Icon into an ImageSource.
   18.75 +    /// </summary>
   18.76 +    public class IconToImageSourceConverter : IValueConverter
   18.77 +    {
   18.78 +        public object Convert(object value,
   18.79 +                              Type targetType,
   18.80 +                              object parameter,
   18.81 +                              CultureInfo culture)
   18.82 +        {
   18.83 +            Icon icon;
   18.84 +            ImageSource source = null;
   18.85 +
   18.86 +            if (value == null)
   18.87 +            {
   18.88 +                return new System.Windows.Media.Imaging.BitmapImage();
   18.89 +                //                throw new ArgumentNullException();
   18.90 +            }
   18.91 +
   18.92 +            if (value is Icon)
   18.93 +            {
   18.94 +                icon = value as Icon;
   18.95 +                source = System.Windows.Interop.Imaging.CreateBitmapSourceFromHIcon(icon.Handle,
   18.96 +                                                                                    new Int32Rect(0, 0, icon.Width, icon.Height),
   18.97 +                                                                                    System.Windows.Media.Imaging.BitmapSizeOptions.FromEmptyOptions());
   18.98 +            }
   18.99 +            else
  18.100 +            {
  18.101 +                throw new ArgumentException();
  18.102 +            }
  18.103 +
  18.104 +            return (source);
  18.105 +        }
  18.106 +
  18.107 +        public object ConvertBack(object value,
  18.108 +                                  Type targetType,
  18.109 +                                  object parameter,
  18.110 +                                  CultureInfo culture)
  18.111 +        {
  18.112 +            throw new NotImplementedException();
  18.113 +        }
  18.114 +    }
  18.115 +
  18.116 +    /// <summary>
  18.117      /// Inverts a bool value.
  18.118      /// </summary>
  18.119      public class InvertBoolConverter : IValueConverter
  18.120 @@ -46,77 +156,6 @@
  18.121      }
  18.122  
  18.123      /// <summary>
  18.124 -    /// Returns gray if false or black if true or error.
  18.125 -    /// </summary>
  18.126 -    public class IsEnabledToColorConverter : IValueConverter
  18.127 -    {
  18.128 -        public object Convert(object value,
  18.129 -                              Type targetType,
  18.130 -                              object parameter,
  18.131 -                              CultureInfo culture)
  18.132 -        {
  18.133 -            var color = System.Windows.SystemColors.ControlTextBrush;
  18.134 -
  18.135 -            if ((value is bool) &&
  18.136 -                ((bool)value == false))
  18.137 -            {
  18.138 -                color = System.Windows.SystemColors.GrayTextBrush;
  18.139 -            }
  18.140 -
  18.141 -            return color;
  18.142 -        }
  18.143 -
  18.144 -        public object ConvertBack(object value,
  18.145 -                                  Type targetType,
  18.146 -                                  object parameter,
  18.147 -                                  CultureInfo culture)
  18.148 -        {
  18.149 -            throw new NotImplementedException();
  18.150 -        }
  18.151 -    }
  18.152 -
  18.153 -    /// <summary>
  18.154 -    /// Returns true if given parameter is equal to HandshakeMode.Standard. Otherwise false
  18.155 -    /// </summary>
  18.156 -    public class IsStandardModeToBoolConverter : IValueConverter
  18.157 -    {
  18.158 -        public object Convert(object value,
  18.159 -                              Type targetType,
  18.160 -                              object parameter,
  18.161 -                              CultureInfo culture)
  18.162 -        {
  18.163 -            bool result = false;
  18.164 -            string param = null;
  18.165 -            string val = null;
  18.166 -
  18.167 -            param = parameter as string;
  18.168 -
  18.169 -            try
  18.170 -            {
  18.171 -                val = Enum.GetName(typeof(HandshakeDialog.HandshakeMode), value);
  18.172 -            }
  18.173 -            catch
  18.174 -            {
  18.175 -                val = null;
  18.176 -            }
  18.177 -
  18.178 -            if ((param != null) &&
  18.179 -                (val != null) &&
  18.180 -                (val == param))
  18.181 -            {
  18.182 -                result = true;
  18.183 -            }
  18.184 -
  18.185 -            return result;
  18.186 -        }
  18.187 -
  18.188 -        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
  18.189 -        {
  18.190 -            throw new NotImplementedException();
  18.191 -        }
  18.192 -    }
  18.193 -
  18.194 -    /// <summary>
  18.195      /// Returns true if given parameter is same as an active tab. Otherwise false
  18.196      /// </summary>
  18.197      public class IsActiveTabToBoolConverter : IValueConverter
  18.198 @@ -158,72 +197,26 @@
  18.199      }
  18.200  
  18.201      /// <summary>
  18.202 -    /// Returns transparent if false or white if true or error.
  18.203 +    /// Returns gray if false or black if true or error.
  18.204      /// </summary>
  18.205 -    public class BooleanToBackgroundConverter : IValueConverter
  18.206 +    public class IsEnabledToColorConverter : IValueConverter
  18.207      {
  18.208          public object Convert(object value,
  18.209                                Type targetType,
  18.210                                object parameter,
  18.211                                CultureInfo culture)
  18.212          {
  18.213 -            System.Windows.Media.Brush color = System.Windows.Media.Brushes.Transparent;
  18.214 +            var color = System.Windows.SystemColors.ControlTextBrush;
  18.215  
  18.216              if ((value is bool) &&
  18.217 -                ((bool)value))
  18.218 +                ((bool)value == false))
  18.219              {
  18.220 -                color = System.Windows.Media.Brushes.White;
  18.221 +                color = System.Windows.SystemColors.GrayTextBrush;
  18.222              }
  18.223  
  18.224              return color;
  18.225          }
  18.226  
  18.227 -        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
  18.228 -        {
  18.229 -            throw new NotImplementedException();
  18.230 -        }
  18.231 -    }
  18.232 -
  18.233 -    /// <summary>
  18.234 -    /// Returns a bool value indicating if the release mode matches the given parameter.
  18.235 -    /// </summary>
  18.236 -    public class IsReleaseModeConverter : IValueConverter
  18.237 -    {
  18.238 -        public object Convert(object value,
  18.239 -                              Type targetType,
  18.240 -                              object parameter,
  18.241 -                              CultureInfo culture)
  18.242 -        {
  18.243 -            bool success;
  18.244 -            Globals.ReleaseMode param;
  18.245 -
  18.246 -            if ((value is Globals.ReleaseMode) &&
  18.247 -                (parameter is string))
  18.248 -            {
  18.249 -                success = Enum.TryParse((parameter as string), out param);
  18.250 -
  18.251 -                if (success)
  18.252 -                {
  18.253 -                    if (((Globals.ReleaseMode)value) == param)
  18.254 -                    {
  18.255 -                        return (true);
  18.256 -                    }
  18.257 -                    else
  18.258 -                    {
  18.259 -                        return (false);
  18.260 -                    }
  18.261 -                }
  18.262 -                else
  18.263 -                {
  18.264 -                    throw new ArgumentException();
  18.265 -                }
  18.266 -            }
  18.267 -            else
  18.268 -            {
  18.269 -                throw new ArgumentException();
  18.270 -            }
  18.271 -        }
  18.272 -
  18.273          public object ConvertBack(object value,
  18.274                                    Type targetType,
  18.275                                    object parameter,
  18.276 @@ -283,78 +276,6 @@
  18.277      }
  18.278  
  18.279      /// <summary>
  18.280 -    /// Converter to change an Icon into an ImageSource.
  18.281 -    /// </summary>
  18.282 -    public class IconToImageSourceConverter : IValueConverter
  18.283 -    {
  18.284 -        public object Convert(object value,
  18.285 -                              Type targetType,
  18.286 -                              object parameter,
  18.287 -                              CultureInfo culture)
  18.288 -        {
  18.289 -            Icon icon;
  18.290 -            ImageSource source = null;
  18.291 -
  18.292 -            if (value == null)
  18.293 -            {
  18.294 -                return new System.Windows.Media.Imaging.BitmapImage();
  18.295 -                //                throw new ArgumentNullException();
  18.296 -            }
  18.297 -
  18.298 -            if (value is Icon)
  18.299 -            {
  18.300 -                icon = value as Icon;
  18.301 -                source = System.Windows.Interop.Imaging.CreateBitmapSourceFromHIcon(icon.Handle,
  18.302 -                                                                                    new Int32Rect(0, 0, icon.Width, icon.Height),
  18.303 -                                                                                    System.Windows.Media.Imaging.BitmapSizeOptions.FromEmptyOptions());
  18.304 -            }
  18.305 -            else
  18.306 -            {
  18.307 -                throw new ArgumentException();
  18.308 -            }
  18.309 -
  18.310 -            return (source);
  18.311 -        }
  18.312 -
  18.313 -        public object ConvertBack(object value,
  18.314 -                                  Type targetType,
  18.315 -                                  object parameter,
  18.316 -                                  CultureInfo culture)
  18.317 -        {
  18.318 -            throw new NotImplementedException();
  18.319 -        }
  18.320 -    }
  18.321 -
  18.322 -    /// <summary>
  18.323 -    /// Converter to check if a string is null or empty.
  18.324 -    /// </summary>
  18.325 -    public class IsStringEmptyConverter : IValueConverter
  18.326 -    {
  18.327 -        public object Convert(object value,
  18.328 -                              Type targetType,
  18.329 -                              object parameter,
  18.330 -                              CultureInfo culture)
  18.331 -        {
  18.332 -            if (value is string)
  18.333 -            {
  18.334 -                return (string.IsNullOrEmpty(value.ToString()));
  18.335 -            }
  18.336 -            else
  18.337 -            {
  18.338 -                throw new ArgumentException();
  18.339 -            }
  18.340 -        }
  18.341 -
  18.342 -        public object ConvertBack(object value,
  18.343 -                                  Type targetType,
  18.344 -                                  object parameter,
  18.345 -                                  CultureInfo culture)
  18.346 -        {
  18.347 -            throw new NotImplementedException();
  18.348 -        }
  18.349 -    }
  18.350 -
  18.351 -    /// <summary>
  18.352      /// Converter to check if a list is empty.
  18.353      /// </summary>
  18.354      public class IsListEmptyConverter : IValueConverter
  18.355 @@ -395,24 +316,43 @@
  18.356      }
  18.357  
  18.358      /// <summary>
  18.359 -    /// Converter to chain together multiple converters.
  18.360 +    /// Returns a bool value indicating if the release mode matches the given parameter.
  18.361      /// </summary>
  18.362 -    public class ValueConverterGroup : List<IValueConverter>, IValueConverter
  18.363 +    public class IsReleaseModeConverter : IValueConverter
  18.364      {
  18.365          public object Convert(object value,
  18.366                                Type targetType,
  18.367                                object parameter,
  18.368                                CultureInfo culture)
  18.369          {
  18.370 -            object curValue;
  18.371 +            bool success;
  18.372 +            Globals.ReleaseMode param;
  18.373  
  18.374 -            curValue = value;
  18.375 -            for (int i = 0; i < this.Count; i++)
  18.376 +            if ((value is Globals.ReleaseMode) &&
  18.377 +                (parameter is string))
  18.378              {
  18.379 -                curValue = this[i].Convert(curValue, targetType, parameter, culture);
  18.380 +                success = Enum.TryParse((parameter as string), out param);
  18.381 +
  18.382 +                if (success)
  18.383 +                {
  18.384 +                    if (((Globals.ReleaseMode)value) == param)
  18.385 +                    {
  18.386 +                        return (true);
  18.387 +                    }
  18.388 +                    else
  18.389 +                    {
  18.390 +                        return (false);
  18.391 +                    }
  18.392 +                }
  18.393 +                else
  18.394 +                {
  18.395 +                    throw new ArgumentException();
  18.396 +                }
  18.397              }
  18.398 -
  18.399 -            return (curValue);
  18.400 +            else
  18.401 +            {
  18.402 +                throw new ArgumentException();
  18.403 +            }
  18.404          }
  18.405  
  18.406          public object ConvertBack(object value,
  18.407 @@ -420,15 +360,77 @@
  18.408                                    object parameter,
  18.409                                    CultureInfo culture)
  18.410          {
  18.411 -            object curValue;
  18.412 +            throw new NotImplementedException();
  18.413 +        }
  18.414 +    }
  18.415  
  18.416 -            curValue = value;
  18.417 -            for (int i = (this.Count - 1); i >= 0; i--)
  18.418 +    /// <summary>
  18.419 +    /// Returns true if given parameter is equal to HandshakeMode.Standard. Otherwise false
  18.420 +    /// </summary>
  18.421 +    public class IsStandardModeToBoolConverter : IValueConverter
  18.422 +    {
  18.423 +        public object Convert(object value,
  18.424 +                              Type targetType,
  18.425 +                              object parameter,
  18.426 +                              CultureInfo culture)
  18.427 +        {
  18.428 +            bool result = false;
  18.429 +            string param = null;
  18.430 +            string val = null;
  18.431 +
  18.432 +            param = parameter as string;
  18.433 +
  18.434 +            try
  18.435              {
  18.436 -                curValue = this[i].ConvertBack(curValue, targetType, parameter, culture);
  18.437 +                val = Enum.GetName(typeof(HandshakeDialog.HandshakeMode), value);
  18.438 +            }
  18.439 +            catch
  18.440 +            {
  18.441 +                val = null;
  18.442              }
  18.443  
  18.444 -            return (curValue);
  18.445 +            if ((param != null) &&
  18.446 +                (val != null) &&
  18.447 +                (val == param))
  18.448 +            {
  18.449 +                result = true;
  18.450 +            }
  18.451 +
  18.452 +            return result;
  18.453 +        }
  18.454 +
  18.455 +        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
  18.456 +        {
  18.457 +            throw new NotImplementedException();
  18.458 +        }
  18.459 +    }
  18.460 +
  18.461 +    /// <summary>
  18.462 +    /// Converter to check if a string is null or empty.
  18.463 +    /// </summary>
  18.464 +    public class IsStringEmptyConverter : IValueConverter
  18.465 +    {
  18.466 +        public object Convert(object value,
  18.467 +                              Type targetType,
  18.468 +                              object parameter,
  18.469 +                              CultureInfo culture)
  18.470 +        {
  18.471 +            if (value is string)
  18.472 +            {
  18.473 +                return (string.IsNullOrEmpty(value.ToString()));
  18.474 +            }
  18.475 +            else
  18.476 +            {
  18.477 +                throw new ArgumentException();
  18.478 +            }
  18.479 +        }
  18.480 +
  18.481 +        public object ConvertBack(object value,
  18.482 +                                  Type targetType,
  18.483 +                                  object parameter,
  18.484 +                                  CultureInfo culture)
  18.485 +        {
  18.486 +            throw new NotImplementedException();
  18.487          }
  18.488      }
  18.489  
  18.490 @@ -474,4 +476,42 @@
  18.491              throw new NotImplementedException();
  18.492          }
  18.493      }
  18.494 +
  18.495 +    /// <summary>
  18.496 +    /// Converter to chain together multiple converters.
  18.497 +    /// </summary>
  18.498 +    public class ValueConverterGroup : List<IValueConverter>, IValueConverter
  18.499 +    {
  18.500 +        public object Convert(object value,
  18.501 +                              Type targetType,
  18.502 +                              object parameter,
  18.503 +                              CultureInfo culture)
  18.504 +        {
  18.505 +            object curValue;
  18.506 +
  18.507 +            curValue = value;
  18.508 +            for (int i = 0; i < this.Count; i++)
  18.509 +            {
  18.510 +                curValue = this[i].Convert(curValue, targetType, parameter, culture);
  18.511 +            }
  18.512 +
  18.513 +            return (curValue);
  18.514 +        }
  18.515 +
  18.516 +        public object ConvertBack(object value,
  18.517 +                                  Type targetType,
  18.518 +                                  object parameter,
  18.519 +                                  CultureInfo culture)
  18.520 +        {
  18.521 +            object curValue;
  18.522 +
  18.523 +            curValue = value;
  18.524 +            for (int i = (this.Count - 1); i >= 0; i--)
  18.525 +            {
  18.526 +                curValue = this[i].ConvertBack(curValue, targetType, parameter, culture);
  18.527 +            }
  18.528 +
  18.529 +            return (curValue);
  18.530 +        }
  18.531 +    }
  18.532  }
    19.1 --- a/pEpForOutlook.csproj	Sun Jul 16 11:56:42 2017 +0200
    19.2 +++ b/pEpForOutlook.csproj	Sun Jul 16 12:39:49 2017 +0200
    19.3 @@ -41,7 +41,7 @@
    19.4      <PublishUrl>publish\</PublishUrl>
    19.5      <InstallUrl>https://pep-project.org/</InstallUrl>
    19.6      <TargetCulture>en</TargetCulture>
    19.7 -    <ApplicationVersion>1.1.18.0</ApplicationVersion>
    19.8 +    <ApplicationVersion>1.1.19.0</ApplicationVersion>
    19.9      <AutoIncrementApplicationRevision>true</AutoIncrementApplicationRevision>
   19.10      <UpdateEnabled>true</UpdateEnabled>
   19.11      <UpdateInterval>0</UpdateInterval>
   19.12 @@ -526,6 +526,15 @@
   19.13      <WCFMetadata Include="Service References\" />
   19.14    </ItemGroup>
   19.15    <ItemGroup>
   19.16 +    <EmbeddedResource Include="UI\RibbonCustomizationsCompose.xml">
   19.17 +      <SubType>Designer</SubType>
   19.18 +    </EmbeddedResource>
   19.19 +    <EmbeddedResource Include="UI\RibbonCustomizationsExplorer.xml">
   19.20 +      <SubType>Designer</SubType>
   19.21 +    </EmbeddedResource>
   19.22 +    <EmbeddedResource Include="UI\RibbonCustomizationsRead.xml">
   19.23 +      <SubType>Designer</SubType>
   19.24 +    </EmbeddedResource>
   19.25      <Resource Include="Resources\ImageLogoBuyMedium.png" />
   19.26      <Resource Include="Resources\ImagePreviewContact.png" />
   19.27      <Resource Include="Resources\ImagePrivacyStatusGreenInvert.png" />