DecryptionStack.cs
author Thomas
Wed, 02 Aug 2017 13:07:30 +0200
changeset 1770 5cae62d6ad4c
child 1772 09eeda973946
child 1778 b2ed8469cdc0
permissions -rw-r--r--
Move decryption stack to own class
     1 ´╗┐using System;
     2 using System.Collections.Generic;
     3 using Outlook = Microsoft.Office.Interop.Outlook;
     4 
     5 namespace pEp
     6 {
     7     public class DecryptionStack : Stack<string>
     8     {
     9         private const int MAX_DECRYPTION_COUNT                       = 5;       // Maximum amount of concurrently decrypted items
    10         private const int DECRYPTION_INTERVAL                        = 500;     // in ms
    11 
    12         private System.Windows.Forms.Timer decryptionTimer           = null;
    13         private int decryptionCounter                                = 0;
    14         private object mutexDecryptionCounter                        = new object();
    15         private object mutexDecryptionStack                          = new object();
    16 
    17         /// <summary>
    18         /// Default constructor.
    19         /// </summary>
    20         public DecryptionStack()
    21         {
    22             this.decryptionTimer = new System.Windows.Forms.Timer();
    23             this.decryptionTimer.Interval = DECRYPTION_INTERVAL;
    24             this.decryptionTimer.Tick += this.DecryptionTimer_Tick;
    25         }
    26 
    27         /// <summary>
    28         /// Increments the decryption counter if maximum has not yet been reached.
    29         /// </summary>
    30         /// <returns>True if counter was encrypted, false if maximum has already been reached.</returns>
    31         private bool IncrementDecryptionCounter()
    32         {
    33             bool incremented = false;
    34 
    35             lock (mutexDecryptionCounter)
    36             {
    37                 if (this.decryptionCounter < MAX_DECRYPTION_COUNT)
    38                 {
    39                     this.decryptionCounter++;
    40                     incremented = true;
    41                 }
    42             }
    43 
    44             return incremented;
    45         }
    46 
    47         /// <summary>
    48         /// Decrements the decryption counter if it is bigger than 0.
    49         /// </summary>
    50         public void DecrementDecryptionCounter()
    51         {
    52             lock (mutexDecryptionCounter)
    53             {
    54                 if (this.decryptionCounter > 0)
    55                 {
    56                     this.decryptionCounter--;
    57                 }
    58             }
    59         }
    60 
    61         /// <summary>
    62         /// Resets the decryption counter to 0.
    63         /// </summary>
    64         private void ResetDecryptionCounter()
    65         {
    66             lock (mutexDecryptionCounter)
    67             {
    68                 this.decryptionCounter = 0;
    69             }
    70         }
    71 
    72         /// <summary>
    73         /// Locks the decryption stack and tries to get its first element.
    74         /// Also checks if the following element(s) are duplicates of the one
    75         /// returned and removes them in this case.
    76         /// </summary>
    77         /// <param name="entryId">The retrieved entryId or an empty string if none was retrieved.</param>
    78         /// <returns>Tue if the operation retrieved successfully an entryId. Otherwise false.</returns>
    79         private bool TryPopDecryptionStack(out string entryId)
    80         {
    81             entryId = string.Empty;
    82 
    83             lock (mutexDecryptionStack)
    84             {
    85                 try
    86                 {
    87                     if (this.Count > 0)
    88                     {
    89                         entryId = this.Pop();
    90                     }
    91                 }
    92                 catch (Exception ex)
    93                 {
    94                     entryId = string.Empty;
    95                     Log.Error("TryPopDecryptionStack: Error getting first element from stack. " + ex.ToString());
    96                 }
    97 
    98                 // If an entryId could be retrieved, check following item(s)
    99                 if (string.IsNullOrEmpty(entryId) == false)
   100                 {
   101                     do
   102                     {
   103                         if (this.Count > 0)
   104                         {
   105                             string nextEntryId = string.Empty;
   106 
   107                             try
   108                             {
   109                                 nextEntryId = this.Peek();
   110                             }
   111                             catch (Exception ex)
   112                             {
   113                                 Log.Error("TryPopDecryptionStack: Error removing duplicates from stack. " + ex.ToString());
   114                             }
   115 
   116                             // If next item equals the retrieved entryId, remove it
   117                             if (string.Equals(entryId, nextEntryId))
   118                             {
   119                                 try
   120                                 {
   121                                     this.Pop();
   122                                 }
   123                                 catch (Exception ex)
   124                                 {
   125                                     Log.Error("TryPopDecryptionStack: Error removing duplicates from stack. " + ex.ToString());
   126                                     break;
   127                                 }
   128                             }
   129                             else
   130                             {
   131                                 break;
   132                             }
   133                         }
   134                         else
   135                         {
   136                             break;
   137                         }
   138 
   139                     } while (true);
   140                 }
   141             }
   142 
   143             return (string.IsNullOrEmpty(entryId) == false);
   144         }
   145 
   146         /// <summary>
   147         /// Locks the decryption stack and tries to push the given entryId.
   148         /// The item is not pushed if the last entryId is the same.
   149         /// </summary>
   150         /// <param name="entryId">The entryId to push to the stack.</param>
   151         /// <returns>True if item was pushed, otherwise false.</returns>
   152         public bool PushEntryId(string entryId)
   153         {
   154             bool pushed = false;
   155             string currentId = string.Empty;
   156 
   157             if (string.IsNullOrEmpty(entryId))
   158             {
   159                 return false;
   160             }
   161 
   162             // Push item to stack if not already on top of it
   163             lock (mutexDecryptionStack)
   164             {
   165                 if (this.Count > 0)
   166                 {
   167                     try
   168                     {
   169                         currentId = this.Peek();
   170                     }
   171                     catch (Exception ex)
   172                     {
   173                         currentId = string.Empty;
   174                         Log.Error("PushToDecryptionStack: Error peeking item. " + ex.ToString());
   175                     }
   176                 }
   177 
   178                 if (entryId?.Equals(currentId) == false)
   179                 {
   180                     this.Push(entryId);
   181                     pushed = true;
   182                 }
   183             }
   184 
   185             // If item was pushed, enable timer and start decryption
   186             if (pushed &&
   187                 this.decryptionTimer.Enabled == false)
   188             {
   189                 this.decryptionTimer.Start();
   190                 this.DecryptionTimer_Tick(null, null);
   191             }
   192 
   193             return pushed;
   194         }
   195 
   196         /// <summary>
   197         /// /// Event handler for when the decryption timer has elapsed.
   198         /// </summary>
   199         private void DecryptionTimer_Tick(object sender, EventArgs e)
   200         {
   201             bool process = false;
   202             bool itemFound = true;
   203             Outlook.MailItem omi = null;
   204             string entryId = string.Empty;
   205             CryptableMailItem cmi = null;
   206 
   207             // Try to increment the decryption counter
   208             if (this.IncrementDecryptionCounter() == false)
   209             {
   210                 return;
   211             }
   212 
   213             // Pop items from the decryption stack 
   214             do
   215             {
   216                 if (this.TryPopDecryptionStack(out entryId))
   217                 {
   218                     // Get mail item and set process flag to true
   219                     try
   220                     {
   221                         omi = Globals.ThisAddIn.Application.Session.GetItemFromID(entryId);
   222                     }
   223                     catch (Exception ex)
   224                     {
   225                         omi = null;
   226                         Log.Error("DecryptionTimer_Tick: Error getting item from ID. " + ex.ToString());
   227                     }
   228 
   229                     if (omi != null)
   230                     {
   231                         process = true;
   232                     }
   233                 }
   234                 else
   235                 {
   236                     // If TryPop returns false, this means that no more item is
   237                     // in the decryption list
   238                     itemFound = false;
   239                 }
   240 
   241             } while (process == false && itemFound);
   242 
   243             // Process the mail item
   244             if (process)
   245             {
   246                 cmi = new CryptableMailItem(omi, true);
   247 
   248                 /* Note: Make sure to dispose the MailItem after processing (pass true).
   249                  * This is needed as decryption is asynchronous but the MailItem reference still needs to be released.
   250                  * Do NOT use omi or cmi references after this point!
   251                  */
   252                 cmi.StartProcessing(true);
   253             }
   254             else
   255             {
   256                 // If we don't process, decrement the decryption counter again
   257                 this.DecrementDecryptionCounter();
   258             }
   259 
   260             // If no more item is on the stack, stop the timer and set decryption counter to 0
   261             if (itemFound == false)
   262             {
   263                 this.ResetDecryptionCounter();
   264                 this.decryptionTimer.Stop();
   265             }
   266         }
   267     }
   268 }