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