Prevent the creation of two drafts OUT-78
authorThomas
Mon, 25 Mar 2019 13:23:30 +0100
branchOUT-78
changeset 2609f64d506177ff
parent 2608 df5464e386a8
Prevent the creation of two drafts
PEPMessage.cs
Wrappers/MailItemWrapper.cs
     1.1 --- a/PEPMessage.cs	Fri Mar 22 08:42:40 2019 +0100
     1.2 +++ b/PEPMessage.cs	Mon Mar 25 13:23:30 2019 +0100
     1.3 @@ -1720,13 +1720,13 @@
     1.4                          {
     1.5                              // Add by address
     1.6                              fromRecipient = recipients.Add(this._From.Address);
     1.7 -                            fromRecipient.Type = (int)Outlook.OlMailRecipientType.olOriginator;
     1.8 +                            fromRecipient.Type = (int)Outlook.OlMailRecipientType.olTo;
     1.9                          }
    1.10                          else if (string.IsNullOrWhiteSpace(this._From.UserName) == false)
    1.11                          {
    1.12                              // Add by user name (required for distribution lists)
    1.13                              fromRecipient = recipients.Add(this._From.UserName);
    1.14 -                            fromRecipient.Type = (int)Outlook.OlMailRecipientType.olOriginator;
    1.15 +                            fromRecipient.Type = (int)Outlook.OlMailRecipientType.olTo;
    1.16                          }
    1.17                      }
    1.18  
     2.1 --- a/Wrappers/MailItemWrapper.cs	Fri Mar 22 08:42:40 2019 +0100
     2.2 +++ b/Wrappers/MailItemWrapper.cs	Mon Mar 25 13:23:30 2019 +0100
     2.3 @@ -633,8 +633,13 @@
     2.4          private void MailItem_Write(ref bool cancel)
     2.5          {
     2.6  #if !READER_RELEASE_MODE
     2.7 -            /* If a draft is saved on an untrusted server
     2.8 -             */ 
     2.9 +            /* If a draft is saved on an untrusted server, we have to intercept the write event to prevent
    2.10 +             * the unencrypted data getting synced to the server.
    2.11 +             * The following situations are considered:
    2.12 +             *  • Draft is saved while open (manually or autosave)  => create or replace mirror in pEp Drafts folder
    2.13 +             *  • Draft is closed and saved                         => create or replace mirror in pEp Drafts folder AND
    2.14 +             *                                                         encrypt original message in Drafts folder for myself
    2.15 +             */
    2.16              if ((this.IsDraft) &&
    2.17                  (this.sent == false) &&
    2.18                  (this.IsInSecureStore || this.NeverUnsecure) &&
    2.19 @@ -645,14 +650,21 @@
    2.20  
    2.21                  // If the window has been closed, encrypt the original
    2.22                  string originalEntryId = null;
    2.23 +                bool closeItem = false;
    2.24                  if ((this.isClosed) &&
    2.25                      (new MsgProcessor().EncryptForSelf(message, message.From, Globals.ThisAddIn.Settings.ExtraKeys, out PEPMessage encryptedMessage)))
    2.26                  {
    2.27                      try
    2.28                      {
    2.29 -                        encryptedMessage.ApplyTo(this.MailItem, false, true);
    2.30 +                        encryptedMessage.ApplyTo(this.MailItem, false, true, true, false);
    2.31                          this.MailItem.Save();
    2.32 -                        cancel = false;
    2.33 +                        originalEntryId = this.MailItem.EntryID;
    2.34 +
    2.35 +                        /* If the item has been closed, we need to manually replicate the process:
    2.36 +                         *  • If we do not cancel the Write event, two mail items are being generated in the drafts folder (for unknown reasons)
    2.37 +                         *  • If we do cancel the event, the inspector window stays open unless we close it manually
    2.38 +                         */
    2.39 +                        closeItem = true;
    2.40                      }
    2.41                      catch (Exception ex)
    2.42                      {
    2.43 @@ -660,14 +672,18 @@
    2.44                      }
    2.45                  }
    2.46  
    2.47 +                // Store a mirror in the pEp Drafts folder
    2.48                  Outlook.MailItem mirror = null;
    2.49                  try
    2.50                  {
    2.51 -                    originalEntryId = this.MailItem.EntryID;
    2.52                      mirror = this.MailItem.GetMirror(this.draftEntryId, true) ?? this.MailItem.CreateMirrorOMI(this.draftEntryId, true);
    2.53                      if (mirror != null)
    2.54                      {
    2.55                          message.ApplyTo(mirror, false, false);
    2.56 +
    2.57 +                        /* If we have the original mail item's entry id (e.g. because the window has been closed
    2.58 +                         * and it has been stored encrypted), save it as a user property on the mirror item.
    2.59 +                         */ 
    2.60                          if (string.IsNullOrEmpty(originalEntryId) == false)
    2.61                          {
    2.62                              mirror.SetUserProperty(MailItemExtensions.USER_PROPERTY_KEY_ORIG_ENTRY_ID, originalEntryId);
    2.63 @@ -679,37 +695,28 @@
    2.64                      {
    2.65                          Log.Error("MailItem_Write: Error getting mirror.");
    2.66                      }
    2.67 +
    2.68 +                    // Close the inspector manually if needed
    2.69 +                    if (closeItem)
    2.70 +                    {
    2.71 +                        this.MailItem.Close(Outlook.OlInspectorClose.olDiscard);
    2.72 +                    }
    2.73                  }
    2.74                  catch (Exception ex)
    2.75                  {
    2.76                      Log.Error("MailItem_Write: Error saving mirror. " + ex.ToString());
    2.77                  }
    2.78 +                finally
    2.79 +                {
    2.80 +                    mirror = null;
    2.81 +                }
    2.82  
    2.83 -                mirror = null;
    2.84 -            }
    2.85 -
    2.86 -            //this.Write?.Invoke(ref cancel);
    2.87 -            return;
    2.88 -
    2.89 -
    2.90 -            /* Save drafts for untrusted servers in a secure location.
    2.91 -             * First we try to save it to a custom drafts folder in the pEp store
    2.92 -             * and make this store visible in the favorites.
    2.93 -             * If this fails, as a fallback solution, we offer to save as a message file.
    2.94 -             * Note: Inline responses need to be processed differently.
    2.95 -             */
    2.96 -            if ((this.sent == false) &&
    2.97 -                (this.IsPEPMessage == false) &&
    2.98 -                (this.MailItem.GetIsDraft()) &&
    2.99 -                ((this.MailItem.GetIsInSecureStore() || this.MailItem.GetNeverUnsecure())))
   2.100 -            {
   2.101 -                bool useFallback = false;
   2.102 -                Outlook.Folder parentFolder = null;
   2.103 -                Outlook.Folder pEpDraftsFolder = null;
   2.104 -                Outlook.Inspector currentInspector = null;
   2.105 -                Outlook.Inspector newInspector = null;
   2.106 -                Outlook.MailItem omi = null;
   2.107 -
   2.108 +                /* Add the pEp drafts folder to favorites.
   2.109 +                 * This is dangerous, as the Add() method crashes Outlook if the pEp store is not visible.
   2.110 +                 * To make this more secure, we only proceed if the pEp store is already visible at this point.
   2.111 +                 * Otherwise, we just set the pEp store's visibility to true and try to add the drafts folder
   2.112 +                 * to the favorites next time.
   2.113 +                 */
   2.114                  Outlook.Application app = null;
   2.115                  Outlook.Explorer activeExplorer = null;
   2.116                  Outlook.NavigationPane navPane = null;
   2.117 @@ -718,221 +725,8 @@
   2.118                  Outlook.NavigationGroups navGroups = null;
   2.119                  Outlook.NavigationGroup navGroup = null;
   2.120                  Outlook.NavigationFolders navFolders = null;
   2.121 -
   2.122                  try
   2.123                  {
   2.124 -                    // Get the pEp drafts folder and the current mail item's folder
   2.125 -                    pEpDraftsFolder = Globals.ThisAddIn.GetPEPStoreDraftsFolder();
   2.126 -
   2.127 -                    // Get the caret position to later set it again
   2.128 -                    bool caretPositionRetrieved = false;
   2.129 -                    NativeMethods.Point caretPosition = new NativeMethods.Point(-1,-1);
   2.130 -                    IntPtr inspectorHandle = IntPtr.Zero;
   2.131 -
   2.132 -                    try
   2.133 -                    {
   2.134 -                        // First, get the current process to retrieve a handle to the caret's window
   2.135 -                        uint currentThreadId = NativeMethods.GetCurrentThreadId();
   2.136 -                        NativeMethods.GuiThreadInfo guiThreadInfo = new NativeMethods.GuiThreadInfo();
   2.137 -                        guiThreadInfo.size = Marshal.SizeOf(guiThreadInfo);
   2.138 -
   2.139 -                        if (NativeMethods.GetGUIThreadInfo(currentThreadId, ref guiThreadInfo))
   2.140 -                        {
   2.141 -                            inspectorHandle = guiThreadInfo.hwndCaret;
   2.142 -
   2.143 -                            // Get caret position and convert to screen coordinates
   2.144 -                            if (NativeMethods.GetCaretPos(out caretPosition) &&
   2.145 -                                NativeMethods.ClientToScreen(inspectorHandle, ref caretPosition))
   2.146 -                            {
   2.147 -                                caretPositionRetrieved = true;
   2.148 -                            }
   2.149 -                            else
   2.150 -                            {
   2.151 -                                int error = Marshal.GetLastWin32Error();
   2.152 -                                Log.Error("MailItem_Write: Error getting caret position. Last Win32 error: " + error.ToString("X"));
   2.153 -                            }
   2.154 -                        }
   2.155 -                        else
   2.156 -                        {
   2.157 -                            int error = Marshal.GetLastWin32Error();
   2.158 -                            Log.Error("MailItem_Write: Error getting inspector Handle. Last Win32 error: " + error.ToString("X"));
   2.159 -                        }
   2.160 -                    }
   2.161 -                    catch (Exception ex)
   2.162 -                    {
   2.163 -                        Log.Error("MailItem_Write: Error getting caret position. " + ex.ToString());
   2.164 -                    }
   2.165 -
   2.166 -                    /* Inline responses need a special treatment.
   2.167 -                     * We cannot just intercept the Write event and check, if the item
   2.168 -                     * is in the pEp folder, as it gets synced to the server anyhow if we
   2.169 -                     * don't cancel the event.
   2.170 -                     * So, we look up if the mail item is already present in pEp drafts and overwrite it 
   2.171 -                     * with the current state if possible. If the item isn't there yet, create new item, 
   2.172 -                     * move to pEp drafts folder and save current state.
   2.173 -                     */
   2.174 -                    if (this.IsInlineResponse)
   2.175 -                    {
   2.176 -                        Outlook.Items items = null;
   2.177 -                        Outlook.MailItem draft = null;
   2.178 -
   2.179 -                        try
   2.180 -                        {
   2.181 -                            // Always cancel
   2.182 -                            cancel = true;
   2.183 -
   2.184 -                            // Try to find the draft (if existing)
   2.185 -                            if (string.IsNullOrEmpty(this.draftEntryId) == false)
   2.186 -                            {
   2.187 -                                // Get all items in the pEp drafts folder with the same subject as the current draft
   2.188 -                                items = pEpDraftsFolder?.Items?.Restrict(string.Format("[Subject] = '{0}'", this.MailItem?.Subject));
   2.189 -
   2.190 -                                for (int i = 1; i <= items?.Count; i++)
   2.191 -                                {
   2.192 -                                    draft = items[i] as Outlook.MailItem;
   2.193 -
   2.194 -                                    if (draftEntryId.Equals(draft?.EntryID) == true)
   2.195 -                                    {
   2.196 -                                        // Draft found. Just break and continue.
   2.197 -                                        break;
   2.198 -                                    }
   2.199 -
   2.200 -                                    draft = null;
   2.201 -                                }
   2.202 -
   2.203 -                                items = null;
   2.204 -                            }
   2.205 -
   2.206 -                            if (PEPMessage.Create(this.MailItem, out PEPMessage createdMessage) == Globals.ReturnStatus.Success)
   2.207 -                            {
   2.208 -                                if (draft == null)
   2.209 -                                {
   2.210 -                                    draft = Globals.ThisAddIn.Application.CreateItem(Outlook.OlItemType.olMailItem);
   2.211 -                                    createdMessage.ApplyTo(draft, true, true);
   2.212 -                                    draft = draft.Move(pEpDraftsFolder);
   2.213 -                                    draft.Save();
   2.214 -                                    this.draftEntryId = draft.EntryID;
   2.215 -                                }
   2.216 -                                else
   2.217 -                                {
   2.218 -                                    createdMessage.ApplyTo(draft, true, true);
   2.219 -                                    draft.Save();
   2.220 -                                }
   2.221 -                            }
   2.222 -                            else
   2.223 -                            {
   2.224 -                                Log.Error("MailItem_Write: Error creating PEPMessage.");
   2.225 -                            }
   2.226 -                        }
   2.227 -                        catch (Exception ex)
   2.228 -                        {
   2.229 -                            Log.Error("MailItem_Write: Error saving inline item. " + ex.ToString());
   2.230 -                        }
   2.231 -                        finally
   2.232 -                        {
   2.233 -                            items = null;
   2.234 -                            omi = null;
   2.235 -                        }
   2.236 -                    }
   2.237 -                    else
   2.238 -                    {
   2.239 -                        // Get the mail item's parent folder
   2.240 -                        parentFolder = this.MailItem.Parent as Outlook.Folder;
   2.241 -
   2.242 -                        // Save to pEp folder if not already there
   2.243 -                        if (pEpDraftsFolder?.FullFolderPath?.Equals(parentFolder?.FullFolderPath) == false)
   2.244 -                        {
   2.245 -                            currentInspector = this.MailItem.GetInspector;
   2.246 -                            //omi = this.internalMailItem.Copy();
   2.247 -                            omi = this.MailItem.Move(pEpDraftsFolder);
   2.248 -
   2.249 -                            if (currentInspector != null)
   2.250 -                            {
   2.251 -                                Outlook.OlWindowState windowState = currentInspector.WindowState;
   2.252 -                                newInspector = Globals.ThisAddIn?.Application?.Inspectors?.Add(omi);
   2.253 -
   2.254 -                                if (windowState == Outlook.OlWindowState.olNormalWindow)
   2.255 -                                {
   2.256 -                                    newInspector.Left = currentInspector.Left;
   2.257 -                                    newInspector.Top = currentInspector.Top;
   2.258 -                                    newInspector.Width = currentInspector.Width;
   2.259 -                                    newInspector.Height = currentInspector.Height;
   2.260 -                                }
   2.261 -
   2.262 -                                /* In some circumstances (e.g. ForceProtection on Exchange), the Sender information 
   2.263 -                                 * may be missing. If this is the case, try to add it from the internal mail item.
   2.264 -                                 */
   2.265 -                                try
   2.266 -                                {
   2.267 -                                    // Check if there really is no useful Sender information
   2.268 -                                    if ((omi != null) &&
   2.269 -                                        (omi.Sender == null) &&
   2.270 -                                        (string.IsNullOrEmpty(omi.SenderEmailAddress)) &&
   2.271 -                                        (omi.SendUsingAccount == null))
   2.272 -                                    {
   2.273 -                                        // Try to add SendUsingAccount
   2.274 -                                        if (this.MailItem.SendUsingAccount != null)
   2.275 -                                        {
   2.276 -                                            omi.SendUsingAccount = this.MailItem.SendUsingAccount;
   2.277 -                                        }
   2.278 -
   2.279 -                                        // Try to add the Sender directly
   2.280 -                                        if (this.MailItem.Sender != null)
   2.281 -                                        {
   2.282 -                                            omi.Sender = this.MailItem.Sender;
   2.283 -                                        }
   2.284 -                                    }
   2.285 -                                }
   2.286 -                                catch (Exception ex)
   2.287 -                                {
   2.288 -                                    Log.Error("MailItem_Write: Error determining/setting sender information of new Inspector. " + ex.ToString());
   2.289 -                                }
   2.290 -
   2.291 -                                newInspector.Display();
   2.292 -                                newInspector.WindowState = windowState;
   2.293 -                                currentInspector.Close(Outlook.OlInspectorClose.olDiscard);
   2.294 -
   2.295 -                                // Set caret position again to where it was before
   2.296 -                                if (caretPositionRetrieved)
   2.297 -                                {
   2.298 -                                    try
   2.299 -                                    {
   2.300 -                                        // Set cursor to the caret's previous position
   2.301 -                                        NativeMethods.SetCursorPos(caretPosition.X, caretPosition.Y);
   2.302 -
   2.303 -                                        // Send mouse click event (mouse down and mouse up)
   2.304 -                                        NativeMethods.Input[] inputs = new NativeMethods.Input[1];
   2.305 -                                        inputs[0].Type = (int)NativeMethods.InputType.InputMouse;
   2.306 -                                        inputs[0].Mi.Flags = (int)NativeMethods.MouseEvents.MouseEventFLeftDown | (int)NativeMethods.MouseEvents.MouseEventFLeftUp;
   2.307 -
   2.308 -                                        if (NativeMethods.SendInput((uint)inputs.Length, inputs, Marshal.SizeOf(inputs[0])) == 0)
   2.309 -                                        {
   2.310 -                                            int error = Marshal.GetLastWin32Error();
   2.311 -                                            Log.Error("MailItem_Write: Error setting caret to original position. Win32 error " + error.ToString("X"));
   2.312 -                                        }
   2.313 -                                    }
   2.314 -                                    catch (Exception ex)
   2.315 -                                    {
   2.316 -                                        Log.Error("MailItem_Write: Error setting caret to original position. " + ex.ToString());
   2.317 -                                    }
   2.318 -                                }
   2.319 -                            }
   2.320 -                            else
   2.321 -                            {
   2.322 -                                newInspector = Globals.ThisAddIn?.Application?.Inspectors?.Add(omi);
   2.323 -                                newInspector.Display();
   2.324 -                            }
   2.325 -
   2.326 -                            cancel = true;
   2.327 -                        }
   2.328 -                    }
   2.329 -
   2.330 -                    /* Add the pEp drafts folder to favorites.
   2.331 -                     * This is dangerous, as the Add() method crashes Outlook if the pEp store is not visible.
   2.332 -                     * To make this more secure, we only proceed if the pEp store is already visible at this point.
   2.333 -                     * Otherwise, we just set the pEp store's visibility to true and try to add the drafts folder
   2.334 -                     * to the favorites next time.
   2.335 -                     */
   2.336                      if (Globals.ThisAddIn.Settings.IsPEPFolderVisible)
   2.337                      {
   2.338                          // Get favorite folders group. See: https://msdn.microsoft.com/en-us/library/office/ff865603.aspx
   2.339 @@ -953,7 +747,7 @@
   2.340                          {
   2.341                              if (navFolders != null)
   2.342                              {
   2.343 -                                navFolders.Add(pEpDraftsFolder);
   2.344 +                                navFolders.Add(Globals.ThisAddIn.GetPEPStoreDraftsFolder());
   2.345                              }
   2.346                              else
   2.347                              {
   2.348 @@ -968,16 +762,10 @@
   2.349                  }
   2.350                  catch (Exception ex)
   2.351                  {
   2.352 -                    Log.Error("MailItem_Write: Error while saving to pEp Drafts folder. " + ex.ToString());
   2.353 -                    useFallback = true;
   2.354 +                    Log.Error("MailItem_Write: Error adding pEp Drafts folder to favorites. " + ex.ToString());
   2.355                  }
   2.356                  finally
   2.357                  {
   2.358 -                    currentInspector = null;
   2.359 -                    parentFolder = null;
   2.360 -                    pEpDraftsFolder = null;
   2.361 -                    omi = null;
   2.362 -
   2.363                      app = null;
   2.364                      activeExplorer = null;
   2.365                      navPane = null;
   2.366 @@ -987,40 +775,6 @@
   2.367                      navGroup = null;
   2.368                      navFolders = null;
   2.369                  }
   2.370 -
   2.371 -                if (useFallback)
   2.372 -                {
   2.373 -                    Log.Verbose("MailItem_Write: pEp folder could not be found or created. Trying fallback");
   2.374 -
   2.375 -                    // Fallback solution
   2.376 -                    if (draftFileName == null)
   2.377 -                    {
   2.378 -                        var result = System.Windows.MessageBox.Show(Properties.Resources.DraftProtection_Warning, Properties.Resources.DraftProtection_Title, System.Windows.MessageBoxButton.YesNo, System.Windows.MessageBoxImage.Exclamation);
   2.379 -                        if (result == System.Windows.MessageBoxResult.Yes)
   2.380 -                        {
   2.381 -                            System.Windows.Forms.SaveFileDialog sfd = new System.Windows.Forms.SaveFileDialog
   2.382 -                            {
   2.383 -                                Filter = Properties.Resources.DraftProtection_MSG_Format + " (*.msg)|*.msg",
   2.384 -                                FilterIndex = 1
   2.385 -                            };
   2.386 -                            if (string.IsNullOrEmpty(this.MailItem.Subject) == false)
   2.387 -                                sfd.FileName = this.MailItem.Subject;
   2.388 -                            var r = sfd.ShowDialog();
   2.389 -                            if (r == System.Windows.Forms.DialogResult.OK)
   2.390 -                            {
   2.391 -                                draftFileName = sfd.FileName;
   2.392 -                                this.MailItem.SaveAs(draftFileName, Outlook.OlSaveAsType.olMSGUnicode);
   2.393 -                            }
   2.394 -                        }
   2.395 -
   2.396 -                        cancel = true;
   2.397 -                    }
   2.398 -                    else
   2.399 -                    {
   2.400 -                        this.MailItem.SaveAs(draftFileName, Outlook.OlSaveAsType.olMSGUnicode);
   2.401 -                        cancel = true;
   2.402 -                    }
   2.403 -                }
   2.404              }
   2.405  #endif
   2.406              this.Write?.Invoke(ref cancel);