DecryptionStack.cs
author Thomas
Mon, 07 Aug 2017 14:06:04 +0200
changeset 1778 b2ed8469cdc0
parent 1770 5cae62d6ad4c
child 1782 8bf7b3e00d2a
child 1784 ed85d00f8f29
child 1785 faca85bbefc3
permissions -rw-r--r--
Rename and reorder decryption stack methods
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@1778
    79
        private bool TryPop(out string entryId)
Thomas@1770
    80
        {
Thomas@1778
    81
            entryId = null;
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@1778
   105
                            string nextEntryId = null;
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@1778
   152
        public bool TryPush(string entryId)
Thomas@1770
   153
        {
Thomas@1770
   154
            bool pushed = false;
Thomas@1778
   155
            string currentId = null;
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@1778
   173
                        Log.Error("DecryptionStack.TryPush: Error peeking item. " + ex.ToString());
Thomas@1770
   174
                    }
Thomas@1770
   175
                }
Thomas@1770
   176
Thomas@1770
   177
                if (entryId?.Equals(currentId) == false)
Thomas@1770
   178
                {
Thomas@1770
   179
                    this.Push(entryId);
Thomas@1770
   180
                    pushed = true;
Thomas@1770
   181
                }
Thomas@1770
   182
            }
Thomas@1770
   183
Thomas@1770
   184
            // If item was pushed, enable timer and start decryption
Thomas@1770
   185
            if (pushed &&
Thomas@1770
   186
                this.decryptionTimer.Enabled == false)
Thomas@1770
   187
            {
Thomas@1770
   188
                this.decryptionTimer.Start();
Thomas@1770
   189
                this.DecryptionTimer_Tick(null, null);
Thomas@1770
   190
            }
Thomas@1770
   191
Thomas@1770
   192
            return pushed;
Thomas@1770
   193
        }
Thomas@1770
   194
Thomas@1770
   195
        /// <summary>
Thomas@1778
   196
        /// Event handler for when the decryption timer has elapsed.
Thomas@1770
   197
        /// </summary>
Thomas@1770
   198
        private void DecryptionTimer_Tick(object sender, EventArgs e)
Thomas@1770
   199
        {
Thomas@1770
   200
            Outlook.MailItem omi = null;
Thomas@1778
   201
            string entryId = null;
Thomas@1770
   202
            CryptableMailItem cmi = null;
Thomas@1770
   203
Thomas@1770
   204
            // Try to increment the decryption counter
Thomas@1770
   205
            if (this.IncrementDecryptionCounter() == false)
Thomas@1770
   206
            {
Thomas@1770
   207
                return;
Thomas@1770
   208
            }
Thomas@1770
   209
Thomas@1778
   210
            // Pop item from the decryption stack
Thomas@1778
   211
            if (this.TryPop(out entryId))
Thomas@1770
   212
            {
Thomas@1778
   213
                // Get mail item and set process flag to true
Thomas@1778
   214
                try
Thomas@1770
   215
                {
Thomas@1778
   216
                    omi = Globals.ThisAddIn.Application.Session.GetItemFromID(entryId);
Thomas@1778
   217
                }
Thomas@1778
   218
                catch (Exception ex)
Thomas@1778
   219
                {
Thomas@1778
   220
                    omi = null;
Thomas@1778
   221
                    Log.Error("DecryptionTimer_Tick: Error getting item from ID. " + ex.ToString());
Thomas@1778
   222
                }
Thomas@1770
   223
Thomas@1778
   224
                if (omi != null)
Thomas@1778
   225
                {
Thomas@1778
   226
                    // Process item
Thomas@1778
   227
                    cmi = new CryptableMailItem(omi, true);
Thomas@1778
   228
Thomas@1778
   229
                    /* Note: Make sure to dispose the MailItem after processing (pass true).
Thomas@1778
   230
                     * This is needed as decryption is asynchronous but the MailItem reference still needs to be released.
Thomas@1778
   231
                     * Do NOT use omi or cmi references after this point!
Thomas@1778
   232
                     */
Thomas@1778
   233
                    cmi.StartProcessing(true);
Thomas@1770
   234
                }
Thomas@1770
   235
                else
Thomas@1770
   236
                {
Thomas@1778
   237
                    // If we don't process, decrement the decryption counter again
Thomas@1778
   238
                    this.DecrementDecryptionCounter();
Thomas@1770
   239
                }
Thomas@1770
   240
            }
Thomas@1770
   241
Thomas@1770
   242
            // If no more item is on the stack, stop the timer and set decryption counter to 0
Thomas@1778
   243
            if (this.Count <= 0)
Thomas@1770
   244
            {
Thomas@1770
   245
                this.ResetDecryptionCounter();
Thomas@1770
   246
                this.decryptionTimer.Stop();
Thomas@1770
   247
            }
Thomas@1770
   248
        }
Thomas@1770
   249
    }
Thomas@1770
   250
}