Commits (16)
using System;
using pEp.Extensions;
using System;
using System.Net;
using System.Net.Http;
......
......@@ -28,14 +28,15 @@ namespace pEp.DPE
if (context.Request.HttpMethod != "POST")
{
Log.Warning("Process request: Ignoring request of type " + context.Request.HttpMethod);
return;
}
string request = null;
try
{
using (var stream = context.Request.InputStream)
using (var sr = new StreamReader(stream))
using (Stream stream = context.Request.InputStream)
using (StreamReader sr = new StreamReader(stream))
{
request = sr.ReadToEnd();
}
......@@ -43,6 +44,8 @@ namespace pEp.DPE
catch (Exception ex)
{
Log.Error("ProcessRequest: Error getting request. " + ex.ToString());
context.Response.StatusCode = (int)HttpStatusCode.BadRequest;
context.Response.Close();
return;
}
......
using pEp.DPE.Interfaces;
using pEp.Extensions;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Outlook = Microsoft.Office.Interop.Outlook;
namespace pEp.DPE
{
internal class DistributedPolicyEngine : IDistributedPolicyEngine
{
private readonly static string DPE_FOLDER = Path.Combine(Globals.PEPUserFolder, "DPE");
private readonly static string PATCH_EXTENSION = ".patch";
private static readonly string DPE_FOLDER = Path.Combine(Globals.PEPUserFolder, "DPE");
public static readonly string DPE_TEMP_LOCATION = Path.Combine(DistributedPolicyEngine.DPE_FOLDER, "temp");
private const string PATCH_EXTENSION = ".patch";
private const string PATCH_MESSAGE_SUBJECT = "Configuration changes";
public const string DPE_MESSAGE_CLASS = "IPM.Note.DPE";
private readonly PEPIdentity ownIdentity;
private readonly PatchEvents patchEvents;
/// <summary>
......@@ -19,16 +26,17 @@ namespace pEp.DPE
{
this.patchEvents = new PatchEvents();
this.Subscribe(this.patchEvents);
this.ownIdentity = new PEPIdentity(Globals.ThisAddIn.Settings.AccountSettingsList.First().SmtpAddress);
}
#region Event handlers
/// <summary>
/// Event handler for when a patch has been accepted.
/// </summary>
private void PatchEvents_PatchAccepted(object sender, PatchEventArgs e)
{
ThisAddIn.PEPEngine.ShowNotification("pEp Distributed Policy Engine", $"Patch with id {e.Patch.Id} was accepted.");
ThisAddIn.PEPEngine.ShowNotification("pEp Distributed Policy Engine", $"Patch with id { e.Patch.Id } was accepted.");
}
/// <summary>
......@@ -36,7 +44,7 @@ namespace pEp.DPE
/// </summary>
private void PatchEvents_PatchRejected(object sender, PatchEventArgs e)
{
ThisAddIn.PEPEngine.ShowNotification("pEp Distributed Policy Engine", $"Patch with id {e.Patch.Id} was rejected.");
ThisAddIn.PEPEngine.ShowNotification("pEp Distributed Policy Engine", $"Patch with id { e.Patch.Id } was rejected.");
}
/// <summary>
......@@ -46,10 +54,58 @@ namespace pEp.DPE
{
// Save the patch and show a notification
Patch patch = e.Patch;
this.SavePatch(patch);
this.CreatePatchMailItem(patch, e.Submitter);
ThisAddIn.PEPEngine.ShowNotification("New patch suggested", patch.CommitMessage + " " + patch.Diff);
}
/// <summary>
/// Creates a mail item that shows a suggested patch.
/// </summary>
/// <param name="patch">The patch to show.</param>
/// <param name="submitter">The submitter of the patch.</param>
private void CreatePatchMailItem(Patch patch, PEPIdentity submitter)
{
Outlook.MailItem omi = null;
Outlook.MailItem mi = null;
try
{
// Use a PEPMessage to define the message to create
PEPMessage patchMessage = new PEPMessage
{
From = submitter,
LongMsgFormattedHtml = patch.Serialize(),
ShortMsg = DistributedPolicyEngine.PATCH_MESSAGE_SUBJECT,
};
patchMessage.To.Add(this.ownIdentity);
// Create the mail item and apply the PEPMessage
omi = Globals.ThisAddIn.Application.CreateItem(Outlook.OlItemType.olMailItem) as Outlook.MailItem;
patchMessage.ApplyTo(omi, false, true, true);
// Add custom message class, set time, and mark as Sent (so that it won't appear as draft)
MapiHelper.SetProperties(omi, new MAPIProperties
{
{ MapiProperty.PidTagMessageClass, DistributedPolicyEngine.DPE_MESSAGE_CLASS },
{ MapiProperty.PidTagMessageDeliveryTime, DateTime.UtcNow }
});
omi.SetMessageFlag(MapiPropertyValue.EnumPidTagMessageFlags.mfUnsent, false);
// Move to inbox and set Received time
mi = omi.Move(Globals.ThisAddIn.Application.Session.GetDefaultFolder(Outlook.OlDefaultFolders.olFolderInbox));
mi.Save();
}
catch (Exception ex)
{
Log.Error("CreatePatchMailItem: Error creating patch mail item. " + ex);
}
finally
{
omi = null;
mi = null;
}
}
#endregion
#region Methods
......@@ -71,7 +127,6 @@ namespace pEp.DPE
public void Reject(Patch patch, PEPIdentity me)
{
DPEWebClient.RejectPatch(patch, me);
this.DeletePatch(patch);
AdapterExtensions.ShowNotification("Patch rejected", patch.CommitMessage);
}
......@@ -81,9 +136,9 @@ namespace pEp.DPE
/// <param name="patchEvents">The patch events to subscribe to.</param>
public void Subscribe(PatchEvents patchEvents)
{
patchEvents.PatchAccepted += PatchEvents_PatchAccepted;
patchEvents.PatchRejected += PatchEvents_PatchRejected;
patchEvents.PatchSuggested += PatchEvents_PatchSuggested;
patchEvents.PatchAccepted += this.PatchEvents_PatchAccepted;
patchEvents.PatchRejected += this.PatchEvents_PatchRejected;
patchEvents.PatchSuggested += this.PatchEvents_PatchSuggested;
}
/// <summary>
......@@ -105,7 +160,6 @@ namespace pEp.DPE
public void Support(Patch patch, PEPIdentity me)
{
DPEWebClient.SupportPatch(patch, me);
this.DeletePatch(patch);
AdapterExtensions.ShowNotification("Patch supported", patch.CommitMessage);
}
......@@ -115,88 +169,9 @@ namespace pEp.DPE
/// <param name="patchEvents">The patch events to unsubscribe from.</param>
public void Unsubscribe(PatchEvents patchEvents)
{
patchEvents.PatchAccepted += PatchEvents_PatchAccepted;
patchEvents.PatchRejected += PatchEvents_PatchRejected;
patchEvents.PatchSuggested += PatchEvents_PatchSuggested;
}
/// <summary>
/// Deletes the patch from the disk.
/// </summary>
/// <param name="patch">The patch to delete.</param>
/// <returns>True if the patch was deleted or not found, otherwise false.</returns>
private bool DeletePatch(Patch patch)
{
try
{
string fileName = Path.Combine(DistributedPolicyEngine.DPE_FOLDER, patch.Id + DistributedPolicyEngine.PATCH_EXTENSION);
if (File.Exists(fileName))
{
File.Delete(fileName);
}
return true;
}
catch (Exception ex)
{
Log.Error("DeletePatch: Error occured. " + ex.ToString());
}
return false;
}
/// <summary>
/// Saves the patch to disk.
/// </summary>
/// <param name="patch">The patch to save.</param>
/// <returns>True if the patch was saved successfully, otherwise false.</returns>
private bool SavePatch(Patch patch)
{
try
{
string fileName = Path.Combine(DistributedPolicyEngine.DPE_FOLDER, patch.Id + DistributedPolicyEngine.PATCH_EXTENSION);
string xml = patch.Serialize();
File.WriteAllText(fileName, xml);
return File.Exists(fileName);
}
catch (Exception ex)
{
Log.Error("SavePatch: Error saving patch. " + ex.ToString());
}
return false;
}
#endregion
#region Static methods
/// <summary>
/// Gets the patches that are currently saved to disk.
/// </summary>
/// <returns>The list of open patches or an empty list if none was found.</returns>
internal static List<Tuple<Patch, bool>> GetOpenPatches()
{
List<Tuple<Patch, bool>> patches = new List<Tuple<Patch, bool>>();
try
{
foreach (string file in Directory.GetFiles(DistributedPolicyEngine.DPE_FOLDER, "*" + DistributedPolicyEngine.PATCH_EXTENSION))
{
string xml = File.ReadAllText(file);
if ((Patch.Deserialize(xml) is Patch patch) &&
(patch != null))
{
patches.Add(new Tuple<Patch, bool>(patch, false));
}
}
}
catch (Exception ex)
{
Log.Error("GetPatches: Error getting patches. " + ex.ToString());
}
return patches;
patchEvents.PatchAccepted -= this.PatchEvents_PatchAccepted;
patchEvents.PatchRejected -= this.PatchEvents_PatchRejected;
patchEvents.PatchSuggested -= this.PatchEvents_PatchSuggested;
}
#endregion
......
......@@ -2,7 +2,7 @@
{
internal interface IPatchEvents
{
void Suggested(Patch patch);
void Suggested(Patch patch, PEPIdentity submitter);
void Rejected(Patch patch, Patch.RejectReason rejectReason);
void Accepted(Patch patch);
}
......
using System;
using System.IO;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
using pEp.Extensions;
using System;
namespace pEp.DPE
{
......@@ -62,54 +59,13 @@ namespace pEp.DPE
}
/// <summary>
/// Serializes this patch into XML format.
/// Deserializes a JSON string into a Patch object.
/// </summary>
/// <returns>The serialized object as XML string.</returns>
public string Serialize()
{
XmlWriterSettings xmlWriterSettings = new XmlWriterSettings
{
//Indent = true,
OmitXmlDeclaration = true,
Encoding = Encoding.UTF8,
CheckCharacters = false
};
using (StringWriter stringWriter = new StringWriter())
using (XmlWriter writer = XmlWriter.Create(stringWriter, xmlWriterSettings))
{
XmlSerializer serializer = new XmlSerializer(typeof(Patch));
serializer.Serialize(writer, this, new XmlSerializerNamespaces(new[] { XmlQualifiedName.Empty }));
return stringWriter.ToString();
}
}
/// <summary>
/// Deserializes an XML string into a Patch object.
/// </summary>
/// <param name="xml">The XML to process.</param>
/// <param name="json">The JSON string to process.</param>
/// <returns>The Patch object or null if an error occured.</returns>
public static Patch Deserialize(string xml)
public static Patch Deserialize(string json)
{
if (string.IsNullOrEmpty(xml))
{
Log.Info("Deserialize: XML is empty.");
return null;
}
XmlSerializer xmlSerializer = new XmlSerializer(typeof(Patch));
using (StringReader stringReader = new StringReader(xml))
{
try
{
return xmlSerializer.Deserialize(stringReader) as Patch;
}
catch (Exception ex)
{
Log.Error("Deserialize: Error deserializing Patch object: " + ex.ToString());
return null;
}
}
return json.Deserialize<Patch>();
}
}
}
......@@ -4,7 +4,8 @@ namespace pEp.DPE
{
public class PatchEventArgs : EventArgs
{
public Patch Patch { get; set; }
public Patch.RejectReason Reason { get; set; } = Patch.RejectReason.None;
public Patch Patch { get; set; }
internal PEPIdentity Submitter { get; set; }
public Patch.RejectReason Reason { get; set; } = Patch.RejectReason.None;
}
}
......@@ -33,7 +33,7 @@ namespace pEp.DPE
Patch patch;
if ((patch = Patch.Deserialize(e.Request)) != null)
{
this.Suggested(patch);
this.Suggested(patch, new PEPIdentity("patchadmin@pep.security"));
}
}
......@@ -72,9 +72,9 @@ namespace pEp.DPE
/// Raises the Patch suggested event.
/// </summary>
/// <param name="patch">The patch that has been suggested.</param>
public void Suggested(Patch patch)
public void Suggested(Patch patch, PEPIdentity submitter)
{
this.PatchSuggested?.Invoke(this, new PatchEventArgs { Patch = patch });
this.PatchSuggested?.Invoke(this, new PatchEventArgs { Patch = patch, Submitter = submitter });
}
#endregion
......
......@@ -17,16 +17,18 @@ namespace pEp
/// </summary>
internal static class MailItemExtensions
{
public const string USER_PROPERTY_KEY_ORIG_ENTRY_ID = "origEntryID";
public const string USER_PROPERTY_KEY_DPE_PATCH_STATUS = "patchStatus";
public const string USER_PROPERTY_KEY_DPE_PATCH_EDIT_DATE = "patchEditDate";
public const string USER_PROPERTY_KEY_INSPECTOR_CLOSED = "inspectorClosed";
public const string USER_PROPERTY_KEY_IS_INCOMING = "isIncoming";
public const string USER_PROPERTY_KEY_IS_MIRROR = "isMirror";
public const string USER_PROPERTY_KEY_ORIG_ENTRY_ID = "origEntryID";
public const string USER_PROPERTY_KEY_PROCESSING_STATE = "processingState";
public const string USER_PROPERTY_KEY_REPLY_ACTION = "replyAction";
public const string UNKNOWN_SENDER = "unknown";
private static readonly object mutexCopiedItemsList = new object();
private static List<string> copiedItemsList = new List<string>();
private static readonly List<string> copiedItemsList = new List<string>();
/// <summary>
/// Enumeration defining the standard, setable pEp properties of an extended MailItem.
......@@ -1190,7 +1192,6 @@ namespace pEp
/// <returns>True if it is an attached mail, otherwise false.</returns>
public static bool GetIsAttachedMail(this Outlook.MailItem omi, out string messageId)
{
messageId = null;
HeaderList headers = omi.GetParsedTransportMessageHeaders();
try
......
using System;
using System.IO;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
using Newtonsoft.Json;
namespace pEp.Extensions
{
internal static class TypeExtensions
{
/// <summary>
/// Converts an object to its serialized XML format.
/// Deserializes a JSON string into a given object.
/// </summary>
/// <typeparam name="T">The type of object we are operating on</typeparam>
/// <param name="value">The object we are operating on</param>
/// <returns>The XML string representation of the object</returns>
public static string Serialize<T>(this T value, bool indent = true) where T : class
/// <param name="json">The json string to process.</param>
/// <returns>The deserialized object or null if an error occured.</returns>
public static T Deserialize<T>(this string json) where T : class
{
XmlWriterSettings xmlWriterSettings = new XmlWriterSettings
{
Indent = indent,
OmitXmlDeclaration = true,
CheckCharacters = false,
Encoding = Encoding.UTF8
};
using (StringWriter stringWriter = new StringWriter())
using (XmlWriter xmlWriter = XmlWriter.Create(stringWriter, xmlWriterSettings))
if (string.IsNullOrEmpty(json))
{
XmlSerializer serializer = new XmlSerializer(value.GetType());
try
{
serializer.Serialize(xmlWriter, value, new XmlSerializerNamespaces(new[] { XmlQualifiedName.Empty }));
}
catch (Exception ex)
{
Log.Error("Serialize: Error occured. " + ex.ToString());
return null;
}
return stringWriter.ToString();
Log.Info("Deserialize: json is empty.");
return null;
}
return JsonConvert.DeserializeObject<T>(json);
}
/// <summary>
/// Deserializes an XML string into a given object.
/// Serializes a given object into a´JSON string.
/// </summary>
/// <param name="xml">The XML to process.</param>
/// <returns>The deserialized object or null if an error occured.</returns>
public static object Deserialize(Type type, string xml)
/// <typeparam name="T">The type of the object to serialize.</typeparam>
/// <param name="obj">The object to serialize.</param>
/// <returns>The serialized string or null if an error occured.</returns>
public static string Serialize<T>(this T obj)
{
if (string.IsNullOrEmpty(xml))
{
Log.Info("Deserialize: XML is empty.");
return null;
}
XmlSerializer xmlSerializer = new XmlSerializer(type);
using (StringReader stringReader = new StringReader(xml))
{
try
{
return xmlSerializer.Deserialize(stringReader);
}
catch (Exception ex)
{
Log.Error("Deserialize: Error deserializing Patch object: " + ex.ToString());
return null;
}
}
return JsonConvert.SerializeObject(obj);
}
}
}
......@@ -578,6 +578,8 @@ namespace pEp
[FieldOffset(0)]
public double at;
[FieldOffset(0)]
public System.Runtime.InteropServices.ComTypes.FILETIME ft;
[FieldOffset(0)]
public IntPtr lpszA;
[FieldOffset(0)]
public IntPtr lpszW;
......@@ -1455,6 +1457,23 @@ namespace pEp
}
}
break;
case MapiProperty.MapiDataType.PtypTime:
{
try
{
long fileTime = ((DateTime)value).ToFileTimeUtc();
sPropValue.Value.ft = new System.Runtime.InteropServices.ComTypes.FILETIME
{
dwLowDateTime = (int)(fileTime & 0xFFFFFFFF),
dwHighDateTime = (int)(fileTime >> 32)
};
}
catch (Exception ex)
{
throw new Exception(string.Format("Error converting to Filetime. Property tag: {0}. Value: {1}. Exception: {2}.", property.DaslName, value?.ToString(), ex.ToString()));
}
}
break;
default:
{
throw new Exception(string.Format("Error creating SPropValue. Data type {0} not supported.", Enum.GetName(typeof(MapiProperty.MapiDataType), property.DataType)));
......@@ -1769,6 +1788,23 @@ namespace pEp
}
}
break;
case MapiProperty.MapiDataType.PtypTime:
{
try
{
long fileTime = ((DateTime)value).ToFileTimeUtc();
sPropValue.Value.ft = new System.Runtime.InteropServices.ComTypes.FILETIME
{
dwLowDateTime = (int)(fileTime & 0xFFFFFFFF),
dwHighDateTime = (int)(fileTime >> 32)
};
}
catch (Exception ex)
{
throw new Exception(string.Format("Error converting to Filetime. Property tag: {0}. Value: {1}. Exception: {2}.", mapiProperty.DaslName, value?.ToString(), ex.ToString()));
}
}
break;
default:
{
throw new Exception(string.Format("Error creating SPropValue. Data type {0} not supported.", Enum.GetName(typeof(MapiProperty.MapiDataType), mapiProperty.DataType)));
......
......@@ -1614,7 +1614,6 @@ namespace pEp
// TODO: Check whether this method should be part of the engine?
int unencryptedCount = 0;
pEpRating rating = pEpRating.pEpRatingUndefined;
List<PEPIdentity> forceUnencryptedList = new List<PEPIdentity>();
PEPIdentity[] recipients;
PEPMessage workingMessage;
......

namespace pEp
{
[System.ComponentModel.ToolboxItemAttribute(false)]
partial class FormRegionDPE : Microsoft.Office.Tools.Outlook.FormRegionBase
{
public FormRegionDPE(Microsoft.Office.Interop.Outlook.FormRegion formRegion)
: base(Globals.Factory, formRegion)
{
this.InitializeComponent();
}
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Form Region Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private static void InitializeManifest(Microsoft.Office.Tools.Outlook.FormRegionManifest manifest, Microsoft.Office.Tools.Outlook.Factory factory)
{
manifest.FormRegionName = "FormRegionDPE";
manifest.FormRegionType = Microsoft.Office.Tools.Outlook.FormRegionType.Replacement;
manifest.Title = "FormRegionDPE";
}
#endregion
#region Component Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.ElementHostFormControl = new System.Windows.Forms.Integration.ElementHost();
this.FormControlPatchView = new pEp.UI.Views.FormControlPatchView();
this.SuspendLayout();
//
// ElementHostFormControl
//
this.ElementHostFormControl.Dock = System.Windows.Forms.DockStyle.Fill;
this.ElementHostFormControl.Location = new System.Drawing.Point(0, 0);
this.ElementHostFormControl.Name = "ElementHostFormControl";
this.ElementHostFormControl.Size = new System.Drawing.Size(500, 350);
this.ElementHostFormControl.TabIndex = 0;
this.ElementHostFormControl.Child = this.FormControlPatchView;
//
// PatchView
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.Controls.Add(this.ElementHostFormControl);
this.Name = "PatchView";
this.Size = new System.Drawing.Size(500, 350);
this.FormRegionShowing += new System.EventHandler(this.FormRegionDPE_FormRegionShowing);
this.FormRegionClosed += new System.EventHandler(this.FormRegionDPE_FormRegionClosed);
this.ResumeLayout(false);
}
#endregion
private System.Windows.Forms.Integration.ElementHost ElementHostFormControl;
private UI.Views.FormControlPatchView FormControlPatchView;
public partial class FormRegionDPEFactory : Microsoft.Office.Tools.Outlook.IFormRegionFactory
{
public event Microsoft.Office.Tools.Outlook.FormRegionInitializingEventHandler FormRegionInitializing;
private Microsoft.Office.Tools.Outlook.FormRegionManifest _Manifest;
[System.Diagnostics.DebuggerNonUserCodeAttribute()]
public FormRegionDPEFactory()
{
this._Manifest = Globals.Factory.CreateFormRegionManifest();
FormRegionDPE.InitializeManifest(this._Manifest, Globals.Factory);
this.FormRegionInitializing += new Microsoft.Office.Tools.Outlook.FormRegionInitializingEventHandler(this.FormRegionDPEFactory_FormRegionInitializing);
}
[System.Diagnostics.DebuggerNonUserCodeAttribute()]
public Microsoft.Office.Tools.Outlook.FormRegionManifest Manifest
{
get
{
return this._Manifest;
}
}
[System.Diagnostics.DebuggerNonUserCodeAttribute()]
Microsoft.Office.Tools.Outlook.IFormRegion Microsoft.Office.Tools.Outlook.IFormRegionFactory.CreateFormRegion(Microsoft.Office.Interop.Outlook.FormRegion formRegion)
{
FormRegionDPE form = new FormRegionDPE(formRegion);
form.Factory = this;
return form;
}
[System.Diagnostics.DebuggerNonUserCodeAttribute()]
byte[] Microsoft.Office.Tools.Outlook.IFormRegionFactory.GetFormRegionStorage(object outlookItem, Microsoft.Office.Interop.Outlook.OlFormRegionMode formRegionMode, Microsoft.Office.Interop.Outlook.OlFormRegionSize formRegionSize)
{
throw new System.NotSupportedException();
}
[System.Diagnostics.DebuggerNonUserCodeAttribute()]
bool Microsoft.Office.Tools.Outlook.IFormRegionFactory.IsDisplayedForItem(object outlookItem, Microsoft.Office.Interop.Outlook.OlFormRegionMode formRegionMode, Microsoft.Office.Interop.Outlook.OlFormRegionSize formRegionSize)
{
if (this.FormRegionInitializing != null)
{
Microsoft.Office.Tools.Outlook.FormRegionInitializingEventArgs cancelArgs = Globals.Factory.CreateFormRegionInitializingEventArgs(outlookItem, formRegionMode, formRegionSize, false);
this.FormRegionInitializing(this, cancelArgs);
return !cancelArgs.Cancel;
}
else
{
return true;
}
}
[System.Diagnostics.DebuggerNonUserCodeAttribute()]
Microsoft.Office.Tools.Outlook.FormRegionKindConstants Microsoft.Office.Tools.Outlook.IFormRegionFactory.Kind
{
get
{
return Microsoft.Office.Tools.Outlook.FormRegionKindConstants.WindowsForms;
}
}
}
}
partial class WindowFormRegionCollection
{
internal FormRegionDPE FormRegionDPE
{
get
{
foreach (var item in this)
{
if (item.GetType() == typeof(FormRegionDPE))
return (FormRegionDPE)item;
}
return null;
}
}
}
}
using pEp.DPE;
using pEp.UI.ViewModels;
using System;
using System.ComponentModel;
using Outlook = Microsoft.Office.Interop.Outlook;
namespace pEp
{
internal partial class FormRegionDPE
{
#region Form Region Factory
[Microsoft.Office.Tools.Outlook.FormRegionMessageClass(DistributedPolicyEngine.DPE_MESSAGE_CLASS)]
[Microsoft.Office.Tools.Outlook.FormRegionName("pEpForOutlook.FormRegionDPE")]
public partial class FormRegionDPEFactory
{
// Occurs before the form region is initialized.
// To prevent the form region from appearing, set e.Cancel to true.
// Use e.OutlookItem to get a reference to the current Outlook item.
private void FormRegionDPEFactory_FormRegionInitializing(object sender, Microsoft.Office.Tools.Outlook.FormRegionInitializingEventArgs e)
{
}
}
#endregion
// Occurs before the form region is displayed.
// Use this.OutlookItem to get a reference to the current Outlook item.
// Use this.OutlookFormRegion to get a reference to the form region.
private void FormRegionDPE_FormRegionShowing(object sender, EventArgs e)
{
Outlook.MailItem omi = null;
try
{
omi = this.OutlookItem as Outlook.MailItem;
string xml = omi.HTMLBody;
Patch patch = Patch.Deserialize(xml);
FormControlPatchViewModel.PatchStatus patchStatus = FormControlPatchViewModel.PatchStatus.Open;
DateTime? editDate = null;
// Get patch status if available
if ((omi.GetUserProperty(MailItemExtensions.USER_PROPERTY_KEY_DPE_PATCH_STATUS, FormControlPatchViewModel.PatchStatus.Open) is string patchStatusString) &&
Enum.TryParse(patchStatusString, out FormControlPatchViewModel.PatchStatus status))
{
patchStatus = status;
}
// Get last edit date if available
if ((omi.GetUserProperty(MailItemExtensions.USER_PROPERTY_KEY_DPE_PATCH_EDIT_DATE) is string editDateString) &&
DateTime.TryParse(editDateString, out DateTime savedEditDate))
{
editDate = savedEditDate;
}
// Get the patch submitter
if (PEPIdentity.GetFromIdentity(omi, out PEPIdentity submitter) == Globals.ReturnStatus.Success)
{
this.FormControlPatchView.DataContext = new FormControlPatchViewModel(patch, submitter, patchStatus, editDate);
// Subscribe to property changed event
if (this.FormControlPatchView.DataContext is FormControlPatchViewModel formControlPatchViewModel)
{
formControlPatchViewModel.PropertyChanged += this.FormRegionDPE_PropertyChanged;
}
}
else
{
throw new Exception("FormRegionDPE_FormRegionShowing: Error getting patch submitter.");
}
}
catch (Exception ex)
{
Log.Error("FormRegionDPE_FormRegionShowing: Error getting Outlook item. " + ex);
}
finally
{
omi = null;
}
}
/// <summary>
/// Event handler for when a property in the associated view model changes.
/// </summary>
private void FormRegionDPE_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
// Set patch status and edit date as user properties if being changed
if ((e.PropertyName == nameof(FormControlPatchViewModel.Status)) ||
(e.PropertyName == nameof(FormControlPatchViewModel.EditDate)))
{
Outlook.MailItem omi = null;
try
{
omi = this.OutlookItem as Outlook.MailItem;
FormControlPatchViewModel.PatchStatus patchStatus = (sender as FormControlPatchViewModel).Status;
DateTime? editDate = (sender as FormControlPatchViewModel).EditDate;
if (patchStatus != FormControlPatchViewModel.PatchStatus.Open)
{
omi.SetUserProperty(MailItemExtensions.USER_PROPERTY_KEY_DPE_PATCH_STATUS, Enum.GetName(typeof(FormControlPatchViewModel.PatchStatus), patchStatus));
}
if (editDate != null)
{
omi.SetUserProperty(MailItemExtensions.USER_PROPERTY_KEY_DPE_PATCH_EDIT_DATE, editDate?.ToString("f"));
}
omi.Save();
}
catch (Exception ex)
{
Log.Error("FormRegionDPE_PropertyChanged: Error setting user property. " + ex);
}
finally
{
omi = null;
}
}
}
// Occurs when the form region is closed.
// Use this.OutlookItem to get a reference to the current Outlook item.
// Use this.OutlookFormRegion to get a reference to the form region.
private void FormRegionDPE_FormRegionClosed(object sender, EventArgs e)
{
// Unsubscribe from property changed event
if (this.FormControlPatchView.DataContext is FormControlPatchViewModel formControlPatchViewModel)
{
formControlPatchViewModel.PropertyChanged -= this.FormRegionDPE_PropertyChanged;
}
}
}
}
namespace pEp.UI.Models
{
public class ConfigFile
{
public string FileName { get; }
public string TempFileName { get; }
public string Diff { get; }
public ConfigFile(string fileName, string tempFileName, string diff)
{
this.FileName = fileName;
this.TempFileName = tempFileName;
this.Diff = diff;
}
}
}
......@@ -62,11 +62,11 @@ namespace pEp.UI.Models
{
// Get group manager
pEpIdentity groupManager = (pEpIdentity)AdapterExtensions.ExecuteWithPassphraseCheck(() => ThisAddIn.PEPEngine.GroupQueryManager(groupIdentity));
Log.Verbose("GetManagedGroupe: Retrieved group manager " + groupManager.Address);
Log.Verbose("GetManagedGroups: Retrieved group manager " + groupManager.Address);
// Get group members
pEpIdentity[] members = (pEpIdentity[])AdapterExtensions.ExecuteWithPassphraseCheck(() => ThisAddIn.PEPEngine.GroupQueryMembers(groupIdentity));
Log.Verbose("GetManagedGroupe: Retrieved group {0} group members", members.Length);
Log.Verbose("GetManagedGroups: Retrieved group {0} group members", members.Length);
// Add group
messageGroups.Add(new MessageGroup(groupIdentity, groupManager, members));
......
......@@ -97,7 +97,6 @@ namespace pEp.UI.Models
{
if (dialogResult == System.Windows.Forms.DialogResult.OK)
{
patchDialog.Patch.CreationDate = DateTime.UtcNow;
Globals.ThisAddIn.DistributedPolicyEngine.Suggest(patchDialog.Patch, me);
}
}
......
using pEp.DPE;
using System;
using System.Windows.Documents;
namespace pEp.UI.ViewModels
{
internal class FormControlPatchViewModel : ViewModelBase
{
public enum PatchStatus
{
Open,
Accepted,
Supported,
Rejected
}
#region Fields
private readonly Patch patch;
private PatchStatus _Status = PatchStatus.Open;
private DateTime? _EditDate = null;
private string _Explanation = null;
#endregion
#region Properties
/// <summary>
/// The commit message of the patch.
/// </summary>
public string CommitMessage
{
get => this.patch?.CommitMessage;
set
{
this.patch.CommitMessage = value;
this.OnPropertyChanged();
}
}
/// <summary>
/// Gets the creation date as string.
/// </summary>
public string CreationDateString => this.patch?.CreationDate.ToString("F");
/// <summary>
/// The diff of this patch.
/// </summary>
public string Diff
{
get => this.patch?.Diff;
set
{
this.patch.Diff = value;
this.OnPropertyChanged();
}
}
/// <summary>
/// Gets the diff of this patch as formatted flow document.
/// </summary>
public FlowDocument DisplayDiff => PatchDialogViewModel.FormatDiff(this.Diff);
/// <summary>
/// Gets or sets the last date the patch was edited.
/// </summary>
public DateTime? EditDate { get => this._EditDate; set => this.SetProperty(ref this._EditDate, value); }
/// <summary>
/// The explanation shown in the UI regarding this patch.
/// </summary>
public string Explanation { get => this._Explanation; set => this.SetProperty(ref this._Explanation, value); }
/// <summary>
/// Gets whether the OK button is visible.
/// </summary>
public bool IsOKButtonVisible { get; } = true;
/// <summary>
/// Gets whether the Reject button is visible.
/// </summary>
public bool IsRejectButtonVisible { get; } = false;
/// <summary>
/// The command to accept the patch dialog.
/// </summary>
public RelayCommand OKButtonCommand => new RelayCommand(this.SupportPatch, p => (this.Status == PatchStatus.Open));
/// <summary>
/// Gets the OK button text.
/// </summary>
public string OKButtonText { get; } = Properties.Resources.Options_OKText;
/// <summary>
/// The command to reject the dialog.
/// </summary>
public RelayCommand RejectButtonCommand => new RelayCommand(this.RejectPatch, p => (this.Status == PatchStatus.Open));
/// <summary>
/// Gets the Reject button text.
/// </summary>
public string RejectButtonText { get; } = Properties.Resources.SyncWizard_RejectButton;
/// <summary>
/// Gets the submitter of this patch.
/// </summary>
public PEPIdentity Submitter { get; }
/// <summary>
/// Gets or sets the status of this patch.
/// </summary>
public PatchStatus Status { get => this._Status; set => this.SetProperty(ref this._Status, value); }
/// <summary>
/// Gets or sets the tag of the patch.
/// </summary>
public string Tag
{
get => this.patch?.Tag;
set
{
this.patch.Tag = value;
this.OnPropertyChanged();
}
}
/// <summary>
/// Gets or sets the URI of the patch.
/// </summary>
public string Uri
{
get => this.patch?.Uri;
set
{
this.patch.Uri = value;
this.OnPropertyChanged();
}
}
#endregion
#region Constructors
/// <summary>
/// Primary constructor.
/// </summary>
/// <param name="patch">The patch to create the form region with.</param>
/// <param name="submitter">The submitter of the patch.</param>
/// <param name="status">The status of the patch.</param>
/// <param name="editDate">The last time this patch was edited (accepted/supported/rejected).</param>
public FormControlPatchViewModel(Patch patch, PEPIdentity submitter, PatchStatus status = PatchStatus.Open, DateTime? editDate = null)
{
this.EditDate = editDate;
this.patch = patch;
this.Submitter = submitter;
this.Status = status;
this.IsRejectButtonVisible = true;
this.OKButtonText = "Support";
this.SetExplanation();
}
#endregion
#region Methods
/// <summary>
/// Rejects this patch.
/// </summary>
/// <param name="parameter">The command parameter.</param>
private void RejectPatch(object parameter)
{
Globals.ThisAddIn.DistributedPolicyEngine.Reject(this.patch, new PEPIdentity());
this.Status = PatchStatus.Rejected;
this.EditDate = DateTime.UtcNow;
this.SetExplanation();
}
/// <summary>
/// Sets the explanation text.
/// </summary>
private void SetExplanation()
{
string editDate = "<n/a>";
if (this.EditDate != null)
{
editDate = ((DateTime)this.EditDate).ToLocalTime().ToString("F");
}
switch (this.Status)
{
case PatchStatus.Accepted:
this.Explanation = $"Accepted on { editDate }";
break;
case PatchStatus.Supported:
this.Explanation = $"Supported on { editDate }";
break;
case PatchStatus.Rejected:
this.Explanation = $"Rejected on { editDate }";
break;
case PatchStatus.Open:
default:
this.Explanation = "New configuration changes pending approval";
break;
}
}
/// <summary>
/// Supports this patch.
/// </summary>
/// <param name="parameter">The command parameter.</param>
private void SupportPatch(object parameter)
{
Globals.ThisAddIn.DistributedPolicyEngine.Support(this.patch, new PEPIdentity());
this.Status = PatchStatus.Supported;
this.EditDate = DateTime.UtcNow;
this.SetExplanation();
}
#endregion
}
}
......@@ -31,7 +31,7 @@ namespace pEp.UI.ViewModels
MessageGroups = 5
}
private PEPSettings pEpSettings;
private readonly PEPSettings pEpSettings;
private int _AccountSettingsListSelectedIndex = -1;
private RelayCommand _CommandButtonAddGroup = null;
......@@ -45,9 +45,6 @@ namespace pEp.UI.ViewModels
private RelayCommand _CommandButtonRefreshLogs = null;
private RelayCommand _CommandButtonResetPEPStore = null;
private RelayCommand _CommandButtonResetAllOwnKeys = null;
private RelayCommand<IList<object>> _CommandOpenPatch = null;
private RelayCommand<IList<object>> _CommandRejectPatch = null;
private RelayCommand<IList<object>> _CommandSupportPatch = null;
private bool _IsAdvancedEnabled = false;
private string _LogEngine = null;
private string _LogOutlook = null;
......@@ -78,12 +75,6 @@ namespace pEp.UI.ViewModels
this.AccountSettingsList.Add(new AccountViewModel(accountSettings, this.CalculateDependentProperties));
});
// Add patches
DistributedPolicyEngine.GetOpenPatches()?.ForEach((tuple) =>
{
this.Patches.Add(new PatchViewModel(tuple.Item1, tuple.Item2));
});
// Calculate dependent properties
this.calcDepPropIsEnabled = true;
this.CalculateDependentProperties();
......@@ -305,55 +296,6 @@ namespace pEp.UI.ViewModels
}
}
/// <summary>
/// Gets the command to open a patch.
/// </summary>
public RelayCommand<IList<object>> CommandOpenPatch
{
get
{
if (this._CommandOpenPatch == null)
{
this._CommandOpenPatch = new RelayCommand<IList<object>>(p => (p?.First() as PatchViewModel)?.OpenPatchDialog(PatchDialog.PatchAction.SupportOrRejectPatch));
}
return this._CommandOpenPatch;
}
}
/// <summary>
/// Gets the command to reject a patch.
/// </summary>
public RelayCommand<IList<object>> CommandRejectPatch
{
get
{
if (this._CommandRejectPatch == null)
{
this._CommandRejectPatch = new RelayCommand<IList<object>>(p => this.RejectPatch(p?.First() as PatchViewModel));
}
return this._CommandRejectPatch;
}
}
/// <summary>
/// Gets the command to support a patch.
/// </summary>
public RelayCommand<IList<object>> CommandSupportPatch
{
get
{
if (this._CommandSupportPatch == null)
{
this._CommandSupportPatch = new RelayCommand<IList<object>>(p => this.SupportPatch(p?.First() as PatchViewModel));
}
return this._CommandSupportPatch;
}
}
/// <summary>
/// Gets the list of projects that are used in pEp for Outlook and that
/// are being credited.
......@@ -1113,37 +1055,6 @@ namespace pEp.UI.ViewModels
} while (retry);
}
/// <summary>
/// Opens the Patch dialog.
/// </summary>
/// <param name="patchViewModel">The patch view model that </param>
/// <param name="patchAction">The patch action to execute.</param>
/// <returns>The dialog result.</returns>
public System.Windows.Forms.DialogResult OpenPatchDialog(PatchViewModel patchViewModel, PatchDialog.PatchAction patchAction)
{
if (patchViewModel == null)
{
Log.ErrorAndFailInDebugMode("OpenPatchDialog: patch view model is null.");
}
System.Windows.Forms.DialogResult dialogResult = patchViewModel?.OpenPatchDialog(patchAction) ?? System.Windows.Forms.DialogResult.Cancel;
if (dialogResult != System.Windows.Forms.DialogResult.Cancel)
{
this.Patches.Clear();
// Add patches
DistributedPolicyEngine.GetOpenPatches()?.ForEach((tuple) =>
{
this.Patches.Add(new PatchViewModel(tuple.Item1, tuple.Item2));
});
this.OnPropertyChanged(nameof(this.Patches));
}
return dialogResult;
}
/// <summary>
/// Resets the logs.
/// </summary>
......@@ -1179,43 +1090,6 @@ namespace pEp.UI.ViewModels
}
}
/// <summary>
/// Removes the given patch from the list of patches.
/// </summary>
/// <param name="patchViewModel">The patch view model to remove.</param>
private void RemovePatch(PatchViewModel patchViewModel)
{
if (patchViewModel == null)
{
Log.ErrorAndFailInDebugMode("RemovePatch: patch view model is null.");
}
if (!this.Patches.Remove(patchViewModel))
{
Log.Error("RemovePatch: Could not remove patch.");
}
}
/// <summary>
/// Rejects the patch.
/// </summary>
/// <param name="patchViewModel">The patch's view model.</param>
private void RejectPatch(PatchViewModel patchViewModel)
{
patchViewModel?.RejectPatch();
this.RemovePatch(patchViewModel);
}
/// <summary>
/// Supports the patch.
/// </summary>
/// <param name="patchViewModel">The patch's view model.</param>
private void SupportPatch(PatchViewModel patchViewModel)
{
patchViewModel?.SupportPatch();
this.RemovePatch(patchViewModel);
}
#endregion
}
}
This diff is collapsed.
<UserControl x:Class="pEp.UI.Views.FormControlPatchView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:p="clr-namespace:pEp.Properties"
xmlns:ui="clr-namespace:pEp.UI"
xmlns:vm="clr-namespace:pEp.UI.ViewModels"
mc:Ignorable="d"
FontFamily="Segoe UI"
FontSize="12"
d:DataContext="{d:DesignInstance Type=vm:FormControlPatchViewModel}"
d:DesignHeight="450" d:DesignWidth="800">
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/pEp;component/Resources/Dictionary.xaml" />
</ResourceDictionary.MergedDictionaries>
<ui:InvertBoolConverter x:Key="InvertBool"/>
<BooleanToVisibilityConverter x:Key="BoolToVisibility" />
<ui:ValueConverterGroup x:Key="InvertBoolToVisibility">
<ui:InvertBoolConverter />
<BooleanToVisibilityConverter />
</ui:ValueConverterGroup>
</ResourceDictionary>
</UserControl.Resources>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Label Grid.Column="0"
Grid.Row="0"
Grid.ColumnSpan="2"
Content="{Binding Explanation}"
FontWeight="Bold"
Margin="10,5"/>
<Label Grid.Column="0"
Grid.Row="1"
Grid.ColumnSpan="2"
Content="{Binding Submitter.DisplayString}"
Margin="10,1" />
<Label Grid.Column="0"
Grid.Row="2"
Grid.ColumnSpan="2"
Content="{Binding CreationDateString}"
Margin="10,1" />
<Image Grid.Column="2"
Grid.Row="0"
Grid.RowSpan="3"
Width="100"
Height="41"
Source="pack://application:,,,/pEp;component/Resources/ImageLogoMedium.png"
HorizontalAlignment="Right"
VerticalAlignment="Center"
Margin="5,5,10,5"/>
<Label Grid.Column="0"
Grid.Row="3"
Content="Diff"
Margin="10,10,5,10"/>
<FlowDocumentScrollViewer Grid.Column="1"
Grid.ColumnSpan="2"
Grid.Row="3"
HorizontalScrollBarVisibility="Auto"
BorderBrush="Black"
BorderThickness="1"
Margin="5,10,10,10"
Document="{Binding DisplayDiff}" />
<Label Grid.Column="0"
Grid.Row="4"
Content="Uri"
Margin="10,10,5,10"/>
<TextBox Grid.Column="1"
Grid.ColumnSpan="2"
Grid.Row="4"
VerticalAlignment="Center"
Padding="2"
Text="{Binding Uri}"
IsReadOnly="True"
MinWidth="400"
Margin="5,10,10,10"/>
<Label Grid.Column="0"
Grid.Row="5"
Content="Commit message"
Margin="10,10,5,10"/>
<TextBox Grid.Column="1"
Grid.ColumnSpan="2"
Grid.Row="5"
VerticalAlignment="Center"
Padding="2"
Text="{Binding CommitMessage}"
IsReadOnly="True"
MinWidth="400"
Margin="5,10,10,10"/>
<Label Grid.Column="0"
Grid.Row="6"
Content="Tag"
Margin="10,10,5,10"/>
<TextBox Grid.Column="1"
Grid.ColumnSpan="2"
Grid.Row="6"
VerticalAlignment="Center"
Padding="2"
Text="{Binding Tag}"
IsReadOnly="True"
MinWidth="400"
Margin="5,10,10,10"/>
<StackPanel Grid.Column="0"
Grid.ColumnSpan="3"
Grid.Row="7"
Orientation="Horizontal"
HorizontalAlignment="Center">
<Button Content="{Binding OKButtonText}"
Command="{Binding OKButtonCommand}"
IsDefault="True"
Visibility="{Binding IsOKButtonVisible, Converter={StaticResource BoolToVisibility}}"
Style="{StaticResource StyleButtonGray}"
Margin="10" />
<Button Content="{Binding RejectButtonText}"
Command="{Binding RejectButtonCommand}"
Visibility="{Binding IsRejectButtonVisible, Converter={StaticResource BoolToVisibility}}"
Style="{StaticResource StyleButtonGray}"
Margin="10" />
</StackPanel>
</Grid>
</UserControl>