Created
September 26, 2018 16:14
-
-
Save RealityAnomaly/2d0ad934e0146a2af7d906307b81fe62 to your computer and use it in GitHub Desktop.
my terrible code
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.Linq; | |
using System.Text; | |
using System.Threading.Tasks; | |
using System.Xml; | |
using System.Xml.Schema; | |
using System.Xml.XPath; | |
using System.IO; | |
using System.Security.Cryptography; | |
using System.Reflection; | |
using CoreDynamic.Providers; | |
using System.Runtime.InteropServices; | |
using System.Threading; | |
using System.Drawing; | |
using CoreDRXLibrary; | |
using System.Security.Cryptography.X509Certificates; | |
using Microsoft.Extensions.DependencyInjection; | |
using CoreDynamic.Interfaces; | |
using CoreDRXLibrary.Interfaces; | |
using CoreDRXLibrary.Controllers; | |
using PCLStorage; | |
namespace CoreDRXLibrary.Providers | |
{ | |
public class FileProvider : IFileProvider | |
{ | |
/// <summary> | |
/// The XmlDocument to modify. | |
/// </summary> | |
public XmlDocument activeDocument; | |
/// <summary> | |
/// Whether errors have occured while loading the file. | |
/// </summary> | |
private bool errorsOccured = false; | |
/// <summary> | |
/// The reader that stores the DRX xml document. | |
/// </summary> | |
private XmlReader fileReader; | |
/// <summary> | |
/// The decrypted decryption key that decrypts the DRX body (unwrapped). | |
/// </summary> | |
protected byte[] encryptionDecryptedKey; | |
/// <summary> | |
/// The body of the DRX, encoded in RTF format. | |
/// </summary> | |
protected string bodyText; | |
/// <summary> | |
/// The XmlNamespaceManager holding namespace information. | |
/// </summary> | |
private XmlNamespaceManager activeNamespace; | |
public string CurrentFilePath { get => CurrentFilePathInt; set { CurrentFilePathInt = value; } } | |
private string CurrentFilePathInt; | |
public int CreationID { get => CreationIDInt; set { CreationIDInt = value; } } | |
private int CreationIDInt; | |
public bool IsValid { get => IsValidInt; set { IsValidInt = value; } } | |
private bool IsValidInt = false; | |
public int Id { get => IdInt; set { IdInt = value; } } | |
private int IdInt; | |
public double FileRevision { get => FileRevisionInt; set { FileRevisionInt = value; } } | |
private double FileRevisionInt; | |
public int Series { get => SeriesInt; set { SeriesInt = value; } } | |
private int SeriesInt; | |
public bool EncryptionEnabled { get => EncryptionEnabledInt; set { EncryptionEnabledInt = value; } } | |
private bool EncryptionEnabledInt; | |
public double EncryptionVersion { get => EncryptionVersionInt; set { EncryptionVersionInt = value; } } | |
private double EncryptionVersionInt; | |
public string BodyHash { get => BodyHashInt; set { BodyHashInt = value; } } | |
private string BodyHashInt; | |
public EncryptionType FileEncryptionType { get => FileEncryptionTypeInt; set { FileEncryptionTypeInt = value; } } | |
private EncryptionType FileEncryptionTypeInt; | |
public string EncryptionSerial { get => EncryptionSerialInt; set { EncryptionSerialInt = value; } } | |
private string EncryptionSerialInt; | |
public X509Certificate2 EncryptionCertificate { get => EncryptionCertificateInt; set { EncryptionCertificateInt = value; } } | |
private X509Certificate2 EncryptionCertificateInt; | |
public string EncryptionCertificateKey { get => EncryptionCertificateKeyInt; set { EncryptionCertificateKeyInt = value; } } | |
private string EncryptionCertificateKeyInt; | |
public string EncryptionKey { get => EncryptionKeyInt; set { EncryptionKeyInt = value; } } | |
private string EncryptionKeyInt; | |
public bool EncryptionProvisioning { get => EncryptionProvisioningInt; set { EncryptionProvisioningInt = value; } } | |
private bool EncryptionProvisioningInt; | |
public string EncryptionPassOverride { get => EncryptionPassOverrideInt; set { EncryptionPassOverrideInt = value; } } | |
private string EncryptionPassOverrideInt; | |
public bool HasBeenDecrypted { get => HasBeenDecryptedInt; set { HasBeenDecryptedInt = value; } } | |
private bool HasBeenDecryptedInt; | |
public string FriendlyName { get => FriendlyNameInt; set { FriendlyNameInt = value; } } | |
private string FriendlyNameInt; | |
public string SecurityLevel { get => SecurityLevelInt; set { SecurityLevelInt = value; } } | |
private string SecurityLevelInt; | |
public bool Redactions { get => RedactionsInt; set { RedactionsInt = value; } } | |
private bool RedactionsInt; | |
public string[] Flags { get => FlagsInt; set { FlagsInt = value; } } | |
private string[] FlagsInt; | |
public string Setting { get => SettingInt; set { SettingInt = value; } } | |
private string SettingInt; | |
public string Date { get => DateInt; set { DateInt = value; } } | |
private string DateInt; | |
public string Status { get => StatusInt; set { StatusInt = value; } } | |
private string StatusInt; | |
public int Vividity { get => VividityInt; set { VividityInt = value; } } | |
private int VividityInt = -1; | |
public int Remembrance { get => RemembranceInt; set { RemembranceInt = value; } } | |
private int RemembranceInt = -1; | |
public int Emotion { get => EmotionInt; set { EmotionInt = value; } } | |
private int EmotionInt = -1; | |
public int Length { get => LengthInt; set { LengthInt = value; } } | |
private int LengthInt = -1; | |
public string EncryptionPassword { get; set; } | |
private IServiceProvider serviceProvider; | |
private IDatabaseProvider databaseProvider; | |
private IApplicationProvider applicationProvider; | |
private Stream documentFileStream = null; | |
/// <summary> | |
/// Class constructor for the DRXFile class. | |
/// </summary> | |
/// <param name="serviceProvider">Service provider to use for DI.</param> | |
/// <param name="documentFile">File instance in the local filesystem to load from.</param> | |
public FileProvider(IServiceProvider serviceProvider, IFile documentFile) | |
{ | |
Initialize(serviceProvider); | |
Task.Run(async () => | |
{ | |
await LoadFromFile(documentFile); | |
}).Wait(); | |
} | |
public FileProvider(IServiceProvider serviceProvider) | |
{ | |
Initialize(serviceProvider); | |
} | |
public async Task LoadFromFile(IFile documentFile) | |
{ | |
// Open the database file for reading and writing, and feed the stream into our XmlReader. | |
documentFileStream = await documentFile.OpenAsync(PCLStorage.FileAccess.ReadAndWrite); | |
fileReader = XmlReader.Create(documentFileStream); | |
} | |
/// <summary> | |
/// Class constructor for the DRXFile class. | |
/// </summary> | |
/// <param name="serviceProvider">Service collection to use for DI.</param> | |
/// <param name="fileStream">A file stream containing the file.</param> | |
public FileProvider(IServiceProvider serviceProvider, Stream fileStream) | |
{ | |
Initialize(serviceProvider); | |
documentFileStream = fileStream; | |
fileReader = XmlReader.Create(documentFileStream); | |
} | |
private void Initialize(IServiceProvider serviceProvider) | |
{ | |
this.serviceProvider = serviceProvider; | |
applicationProvider = serviceProvider.GetRequiredService<IApplicationProvider>(); | |
databaseProvider = serviceProvider.GetRequiredService<IDatabaseProvider>(); | |
// Create a new XmlDocument to hold XML information, and set options on it. | |
activeDocument = new XmlDocument() | |
{ | |
PreserveWhitespace = true | |
}; | |
// Attempt to load the DRX schema. | |
try | |
{ | |
// Load the schema. | |
activeDocument.Schemas.Add(XmlSchema.Read(new MemoryStream(Encoding.UTF8.GetBytes(schemas.DRXSchema)), ValidationCallback)); | |
// Create a new namespace manager for the schema. | |
activeNamespace = new XmlNamespaceManager(activeDocument.NameTable); | |
// Add the schema namespace to the namespace manager. | |
activeNamespace.AddNamespace("xns", "core://drx.schema.core/schemas/DRXSchema.xsd"); | |
} | |
// Something went wrong while loading the schema. | |
catch (Exception e) | |
{ | |
throw new DRXLoadException(e.Message, e); | |
} | |
} | |
public Dictionary<string, dynamic> GetMetadata() | |
{ | |
try | |
{ | |
if (fileReader.ReadState == ReadState.Initial) | |
activeDocument.Load(fileReader); | |
activeDocument.Validate(ValidationCallback); | |
string friendlyName = QuerySingleNode("//xns:drx/xns:header/xns:friendlyName").InnerText; | |
string securityLevel = QuerySingleNode("//xns:drx/xns:header/xns:secLevel").InnerText; | |
string redactions = QuerySingleNode("//xns:drx/xns:header/xns:redactions").InnerText; | |
string setting = QuerySingleNode("//xns:drx/xns:header/xns:setting").InnerText; | |
string date = QuerySingleNode("//xns:drx/xns:header/xns:date").InnerText; | |
string status = QuerySingleNode("//xns:drx/xns:header/xns:status").InnerText; | |
XmlNode bodyHash_node = activeDocument.DocumentElement.SelectSingleNode("//xns:drx/@bodyHash", activeNamespace); | |
XmlNode encryptionWrapper_node = activeDocument.DocumentElement.SelectSingleNode("//xns:drx/@encryptionWrapper", activeNamespace); | |
XmlNode vividity_node = QuerySingleNode("//xns:drx/@vividity"); | |
XmlNode remembrance_node = QuerySingleNode("//xns:drx/@remembrance"); | |
XmlNode emotion_node = QuerySingleNode("//xns:drx/@emotion"); | |
XmlNode length_node = QuerySingleNode("//xns:drx/@length"); | |
int vividity = -1; | |
int remembrance = -1; | |
int emotion = -1; | |
int length = -1; | |
string bodyHash = null; | |
if (bodyHash_node != null) { bodyHash = bodyHash_node.InnerText; } else if (encryptionWrapper_node != null) { bodyHash = encryptionWrapper_node.InnerText; } | |
if (vividity_node != null) { vividity = Convert.ToInt32(vividity_node.InnerText); } | |
if (remembrance_node != null) { remembrance = Convert.ToInt32(remembrance_node.InnerText); } | |
if (emotion_node != null) { emotion = Convert.ToInt32(emotion_node.InnerText); } | |
if (length_node != null) { length = Convert.ToInt32(length_node.InnerText); } | |
XmlNodeList documentFlags = activeDocument.SelectNodes("//xns:drx/xns:header/xns:flags/xns:flag", activeNamespace); | |
List<string> temporaryFlags = new List<string>(); | |
List<string> nonDescriptiveFlags = new List<string>(); | |
foreach (XmlNode DataFlag in documentFlags) | |
{ | |
temporaryFlags.Add(DataFlag.InnerText); | |
nonDescriptiveFlags.Add(DRXBaseClass.FlagToNonDescriptive(DataFlag.InnerText)); | |
} | |
Dictionary<string, dynamic> metadata = new Dictionary<string, dynamic>() | |
{ | |
{ "Identifier", Convert.ToInt32(QuerySingleNode("//xns:drx/@id").InnerText) }, | |
{ "FriendlyName", friendlyName }, | |
{ "SecurityLevel", securityLevel }, | |
{ "Redactions", redactions }, | |
{ "Flags", temporaryFlags }, | |
{ "FlagsNonDescriptive", nonDescriptiveFlags }, | |
{ "Setting", setting }, | |
{ "Date", date }, | |
{ "Status", status }, | |
{ "BodyHash", bodyHash }, | |
{ "VRELScore", DRXBaseClass.VRELToString(vividity, remembrance, emotion, length) }, | |
{ "RetrievalLocation", "DirectFile" } | |
}; | |
return metadata; | |
} | |
catch (Exception e) | |
{ | |
throw new DRXLoadException("Fatal error: " + e.Message + " Could not query file metadata.", e); | |
} | |
} | |
/// <summary> | |
/// Attempts to load the specified DRX document. | |
/// </summary> | |
/// <returns>A boolean value indicating whether the load was successful.</returns> | |
public bool LoadDocument() | |
{ | |
if (fileReader == null) | |
throw new DRXLoadException("File provider does not yet contain a reader or document to read from."); | |
// Attempt to read the DRX. | |
try | |
{ | |
#region ReaderInitialization | |
try | |
{ | |
// Load the data into the XmlDocument using the XmlReader. | |
if (fileReader.ReadState == ReadState.Initial && documentFileStream != null) | |
{ | |
//string testString = fileReader.ReadContentAsString(); | |
activeDocument.Load(fileReader); | |
documentFileStream.Close(); | |
} | |
} | |
catch (XmlException e) | |
{ | |
throw new DRXLoadException("DRX XML parsing exception: " + e.Message, e); | |
} | |
// Close the stream to make sure the file is not still open. | |
fileReader.Close(); | |
// Check the validity. | |
activeDocument.Validate(ValidationCallback); | |
if (errorsOccured == true) | |
{ | |
// Errors occured! | |
return false; | |
} | |
#endregion | |
#region VariableAssignment | |
// Query all namespaces and assign to variables. | |
XmlElement rootElement = (XmlElement)QuerySingleNode("//xns:drx"); | |
XmlNode header = QuerySingleNode("//xns:drx/xns:header"); | |
XmlNode friendlyName = QuerySingleNode("//xns:drx/xns:header/xns:friendlyName"); | |
XmlNode securityLevel = QuerySingleNode("//xns:drx/xns:header/xns:secLevel"); | |
XmlNode redactions = QuerySingleNode("//xns:drx/xns:header/xns:redactions"); | |
XmlNode flags = QuerySingleNode("//xns:drx/xns:header/xns:flags"); | |
XmlNode setting = QuerySingleNode("//xns:drx/xns:header/xns:setting"); | |
XmlNode date = QuerySingleNode("//xns:drx/xns:header/xns:date"); | |
XmlNode status = QuerySingleNode("//xns:drx/xns:header/xns:status"); | |
FriendlyName = friendlyName.InnerText; | |
SecurityLevel = securityLevel.InnerText; | |
Redactions = Convert.ToBoolean(redactions.InnerText); | |
// Get a list of all flags in the current DRX document. | |
XmlNodeList documentFlags = activeDocument.SelectNodes("//xns:drx/xns:header/xns:flags/xns:flag", activeNamespace); | |
// Create a new list to store the flags. | |
List<string> temporaryFlags = new List<string>(); | |
// Loop for each XmlNode in the DataFlags XmlNodeList. | |
foreach (XmlNode flag in documentFlags) | |
{ | |
// Add the flag to the FlagsTemp list. | |
temporaryFlags.Add(flag.InnerText); | |
} | |
// Convert the FlagsTemp list to the Flags global variable. | |
Flags = temporaryFlags.ToArray(); | |
Setting = setting.InnerText; | |
Date = date.InnerText; | |
Status = status.InnerText; | |
// Query other attributes. | |
XmlNode vividity = this.QuerySingleNode("//xns:drx/@vividity"); | |
XmlNode remembrance = this.QuerySingleNode("//xns:drx/@remembrance"); | |
XmlNode emotion = this.QuerySingleNode("//xns:drx/@emotion"); | |
XmlNode length = this.QuerySingleNode("//xns:drx/@length"); | |
// Check if the document already has "vividity" and "remembrance" attributes. | |
if (vividity == null || remembrance == null) | |
{ | |
// Set values to zero. | |
Vividity = 0; | |
Remembrance = 0; | |
// Initialize the element. | |
rootElement.SetAttribute("vividity", "0"); | |
rootElement.SetAttribute("remembrance", "0"); | |
} | |
else | |
{ | |
// Set them from existing values. | |
Vividity = Convert.ToInt32(vividity.InnerText); | |
Remembrance = Convert.ToInt32(remembrance.InnerText); | |
} | |
// Check if the document already has "emotion" and "length" attributes. | |
if (emotion == null || length == null) | |
{ | |
// Set values to zero. | |
Emotion = 0; | |
Length = 0; | |
// Initialize the element. | |
rootElement.SetAttribute("emotion", "0"); | |
rootElement.SetAttribute("length", "0"); | |
} | |
else | |
{ | |
// Set them from existing values. | |
Emotion = Convert.ToInt32(emotion.InnerText); | |
Length = Convert.ToInt32(length.InnerText); | |
} | |
// Query file attributes, and convert them to globals. | |
XmlNode id = QuerySingleNode("//xns:drx/@id"); | |
XmlNode fileRevision = QuerySingleNode("//xns:drx/@fileRevision"); | |
XmlNode series = QuerySingleNode("//xns:drx/@series"); | |
Id = Convert.ToInt32(id.InnerText); | |
FileRevision = Convert.ToDouble(fileRevision.InnerText); | |
Series = Convert.ToInt32(series.InnerText); | |
// Query the body namespace. | |
XmlNode documentBody = QuerySingleNode("//xns:drx/xns:body"); | |
XmlNode bodyHash = QuerySingleNode("//xns:drx/@bodyHash"); | |
if (bodyHash != null) | |
{ | |
BodyHash = bodyHash.InnerText; | |
} | |
else | |
{ | |
BodyHash = null; | |
} | |
#endregion | |
#region EncryptionVariables | |
// If encryption is enabled, set the respective attributes. | |
if (Convert.ToBoolean(QuerySingleNode("//xns:drx/@encryptionEnabled").InnerText) == true && | |
Convert.ToDecimal(QuerySingleNode("//xns:drx/@encryptionVersion").InnerText) != 0) | |
{ | |
// Query the encryption XML nodes. | |
XmlNode encryptionEnabled = QuerySingleNode("//xns:drx/@encryptionEnabled"); | |
XmlNode encryptionVersion = QuerySingleNode("//xns:drx/@encryptionVersion"); | |
XmlNode encryptionWrapper = QuerySingleNode("//xns:drx/@encryptionWrapper"); | |
XmlNode encryptionType = QuerySingleNode("//xns:drx/@encryptionType"); | |
XmlNode encryptionKey = QuerySingleNode("//xns:drx/@encryptionKey"); | |
XmlNode encryptionSerial = QuerySingleNode("//xns:drx/@encryptionSerial"); | |
if (BodyHash == null) | |
{ | |
BodyHash = encryptionWrapper.InnerText; | |
} | |
// Set the globals. | |
EncryptionEnabled = Convert.ToBoolean(encryptionEnabled.InnerText); | |
EncryptionVersion = Convert.ToDouble(encryptionVersion.InnerText); | |
FileEncryptionType = (EncryptionType)Enum.Parse(typeof(EncryptionType), encryptionType.InnerText, true); | |
if (encryptionSerial == null) | |
{ | |
// Initialize the element. | |
rootElement.SetAttribute("encryptionSerial", "0"); | |
} | |
else | |
{ | |
EncryptionSerial = encryptionSerial.InnerText; | |
} | |
} | |
else | |
// Encryption isn't enabled, so no hard work to do! :) | |
{ | |
bodyText = documentBody.InnerText; | |
} | |
#endregion | |
bodyText = documentBody.InnerText; | |
// Set the document as valid. | |
IsValid = true; | |
// Set the filename (because if we loaded from a stream we need to save in the filesystem) | |
CurrentFilePath = databaseProvider.GetDRXFilePath(Id); | |
// Save the document in the local cache. | |
Task.Run(async () => { await SaveChanges(true, true); }); | |
// Return true. | |
return true; | |
} | |
// The DRX file wasn't found in the filesystem. | |
catch (FileNotFoundException) | |
{ | |
// Invalidate the document. | |
IsValid = false; | |
// Return false. | |
return false; | |
} | |
} | |
public bool DeleteDocument() | |
{ | |
try | |
{ | |
fileReader.Close(); | |
File.Delete(CurrentFilePath); | |
return true; | |
} | |
catch (Exception e) | |
{ | |
throw new DRXLoadException("Could not delete the specified DRX: " + e.Message, e); | |
} | |
} | |
/// <summary> | |
/// Creates or updates a new DRX document with the provided parameters. | |
/// </summary> | |
/// <param name="Document">The XmlDocument instance you'd like to create or update the document with.</param> | |
/// <returns>A boolean value indicating whether the creation or update was successful.</returns> | |
public bool UpdateDocument(bool creating = false, int id = 0) | |
{ | |
// Check whether all document data is present. | |
if (Id != 0 && | |
FileRevision != 0 && | |
Series != 0 && | |
FriendlyName != null && | |
SecurityLevel != null && | |
Flags != null && | |
Setting != null && | |
Date != null && | |
Status != null && | |
bodyText != null) | |
{ | |
// Create a variable to store a temporary flag. | |
XmlElement temporaryFlag; | |
// Query all namespaces and assign to variables. | |
XmlNode friendlyName = QuerySingleNode("//xns:drx/xns:header/xns:friendlyName"); | |
XmlNode securityLevel = QuerySingleNode("//xns:drx/xns:header/xns:secLevel"); | |
XmlNode redactions = QuerySingleNode("//xns:drx/xns:header/xns:redactions"); | |
XmlNode flags = QuerySingleNode("//xns:drx/xns:header/xns:flags"); | |
XmlNode setting = QuerySingleNode("//xns:drx/xns:header/xns:setting"); | |
XmlNode date = QuerySingleNode("//xns:drx/xns:header/xns:date"); | |
XmlNode status = QuerySingleNode("//xns:drx/xns:header/xns:status"); | |
// Set the DRX header. | |
friendlyName.InnerText = FriendlyName; | |
securityLevel.InnerText = SecurityLevel; | |
// Create an element to store our flags. | |
XmlElement temporaryFlags = activeDocument.CreateElement("flags", "core://drx.schema.core/schemas/DRXSchema.xsd"); | |
// Do for each flag in the Flags global. | |
foreach (string flag in Flags) | |
{ | |
// Create a new flag element for each DataFlag. | |
temporaryFlag = activeDocument.CreateElement("flag", "core://drx.schema.core/schemas/DRXSchema.xsd"); | |
temporaryFlag.InnerText = flag; | |
SaveNewNode(temporaryFlags, temporaryFlag); | |
flags.InnerXml = temporaryFlags.InnerXml; | |
} | |
// Set values of other parameters. | |
setting.InnerText = Setting; | |
date.InnerText = Date; | |
status.InnerText = Status; | |
redactions.InnerText = BooleanToString(Redactions); | |
// Set the DRX file versioning and metadata. | |
SetAttribute("//xns:drx/@id", Convert.ToString(Id)); | |
SetAttribute("//xns:drx/@fileRevision", Convert.ToString(FileRevision + 1)); | |
SetAttribute("//xns:drx/@series", Convert.ToString(Series.ToString())); | |
// Set vividity and remembrance. | |
if (Vividity != -1 && Remembrance != -1) | |
{ | |
SetAttribute("//xns:drx/@vividity", Convert.ToString(Vividity)); | |
SetAttribute("//xns:drx/@remembrance", Convert.ToString(Remembrance)); | |
} | |
// Set emotion and length. | |
if (Emotion != -1 && Length != -1) | |
{ | |
SetAttribute("//xns:drx/@emotion", Convert.ToString(Emotion)); | |
SetAttribute("//xns:drx/@length", Convert.ToString(Length)); | |
} | |
string bodyShaSum = EncryptionProvider.Sha512(bodyText); | |
BodyHash = Convert.ToBase64String(Encoding.UTF8.GetBytes(bodyShaSum)); | |
// DELETE the encryption wrapper because it's deprecated. | |
if (activeDocument.DocumentElement.GetAttribute("encryptionWrapper") != null) | |
activeDocument.DocumentElement.RemoveAttribute("encryptionWrapper"); | |
// Set the new attribute, the "body hash". | |
activeDocument.DocumentElement.SetAttribute("bodyHash", BodyHash); | |
activeDocument.DocumentElement.SetAttribute("encryptionEnabled", BooleanToString(EncryptionEnabled)); | |
XmlNode body = QuerySingleNode("//xns:drx/xns:body"); | |
if (EncryptionProvisioning == true) | |
{ | |
// Make sure the body is empty before doing this, just in case. | |
if (String.IsNullOrEmpty(bodyText) == true) | |
{ | |
// Set the body to a blank RTF notation. | |
bodyText = @"{\rtf1\ansi\ansicpg1252\deff0\deflang2057{\fonttbl{\f0\fnil\fcharset0 Microsoft Sans Serif;}}\viewkind4\uc1\pard\f0\fs17\par}"; | |
} | |
} | |
// Do this only if encryption is enabled and the encryption wrapper isn't null OR we're provisioning encryption. | |
if (EncryptionEnabled == true && EncryptionVersion != 0 && BodyHash != null || EncryptionProvisioning == true) | |
{ | |
EncryptionProvisioning = false; | |
EncryptionProvider Crypto; | |
if (FileEncryptionType == EncryptionType.Password) | |
{ | |
Crypto = new EncryptionProvider(EncryptionProvider.Sha512(EncryptionPassword)); | |
body.InnerText = Crypto.Encrypt(bodyText); | |
} | |
else if (FileEncryptionType == EncryptionType.Certificate) | |
{ | |
Crypto = new EncryptionProvider(EncryptionKey); | |
body.InnerText = EncryptionProvider.EncryptBody(bodyText, Convert.ToBase64String(encryptionDecryptedKey)); | |
EncryptionCertificateKey = Convert.ToBase64String(EncryptionProvider.EncryptWithCert(EncryptionCertificate, encryptionDecryptedKey)); | |
activeDocument.DocumentElement.SetAttribute("encryptionKey", EncryptionCertificateKey); | |
activeDocument.DocumentElement.SetAttribute("encryptionSerial", EncryptionSerial); | |
} | |
} | |
// We're not using encryption. | |
else | |
{ | |
body.InnerText = bodyText; | |
} | |
// Validate the document. | |
activeDocument.Validate(ValidationCallback); | |
if (errorsOccured == true) | |
{ | |
IsValid = false; | |
return false; | |
} | |
else | |
{ | |
IsValid = true; | |
return true; | |
} | |
} | |
// Not all document params are set. | |
else | |
{ | |
throw new Exception(strings.DRXNotEnoughParameters); | |
} | |
} | |
public string GetBodyContents() | |
{ | |
return GetBodyContents(false); | |
} | |
private string GetBodyContents(bool cryptoOverride = false) | |
{ | |
if (EncryptionEnabled && (!HasBeenDecrypted && !cryptoOverride)) | |
throw new CryptographicException("Document has not yet been decrypted."); | |
return bodyText; | |
} | |
public void SetBodyContents(string body) | |
{ | |
// Strip any null bytes from the body. | |
body = body.Replace("\0", String.Empty); | |
if (EncryptionEnabled && !HasBeenDecrypted) | |
throw new CryptographicException("Document has not yet been decrypted."); | |
if (EncryptionEnabled) | |
{ | |
// If encryption is enabled, set the respective attributes, and additionally encrypt the body. | |
if (EncryptionEnabled == true && EncryptionVersion != 0 && BodyHash != null || EncryptionProvisioning == true) | |
{ | |
// Set encryption attributes. | |
activeDocument.DocumentElement.SetAttribute("encryptionEnabled", BooleanToString(EncryptionEnabled)); | |
activeDocument.DocumentElement.SetAttribute("encryptionVersion", Convert.ToString(EncryptionVersion)); | |
activeDocument.DocumentElement.SetAttribute("encryptionType", Enum.GetName(typeof(EncryptionType), FileEncryptionType)); | |
if (FileEncryptionType == EncryptionType.Certificate) | |
{ | |
// Generate an encryption key if one dosen't exist already. | |
// This is used when switching from encryption to a password. | |
if (EncryptionCertificateKey == null) | |
{ | |
encryptionDecryptedKey = EncryptionProvider.GenerateEncryptionKey(EncryptionCertificate); | |
} | |
} | |
} | |
} | |
bodyText = body; | |
} | |
public void DecryptBodyPassword(string password) | |
{ | |
if (!EncryptionEnabled) | |
throw new CryptographicException("Encryption is not enabled."); | |
if (FileEncryptionType != EncryptionType.Password) | |
throw new CryptographicException("Encryption type does not match the decryption method."); | |
XmlNode bodyNode = QuerySingleNode("//xns:drx/xns:body"); | |
string bodyShaSum = null; | |
string decryptionKey = EncryptionProvider.Sha512(password); | |
EncryptionProvider CryptoProvider = new EncryptionProvider(decryptionKey); | |
string bodyText_old = GetBodyContents(true); | |
// Attempt to decrypt the body text. | |
try | |
{ | |
bodyText = CryptoProvider.Decrypt(bodyText_old); | |
// Create another SHA512 object to validate the hash of the body. | |
using (SHA512 SHA = new SHA512Managed()) | |
{ | |
byte[] bodyByte = Encoding.UTF8.GetBytes(bodyText); | |
byte[] bodyBytes = SHA.ComputeHash(bodyByte); | |
string comparisonHash = Encoding.UTF8.GetString(bodyBytes, 0, bodyBytes.Length); | |
bodyShaSum = Convert.ToBase64String(Encoding.UTF8.GetBytes(comparisonHash)); | |
} | |
} | |
// The password is incorrect, or something bad happened. | |
catch (CryptographicException) { } | |
// Check whether the SHA512 hash of the body matches the EncryptionWrapper hash we stored in the file header | |
// while we were creating the DRX. | |
if (bodyShaSum != BodyHash) | |
{ | |
// Set the body contents back to the original value. | |
bodyText = bodyText_old; | |
throw new CryptographicException("File hash does not match. Decryption error or incorrect password?"); | |
} | |
// Set the EncryptionPassword global, just in case we need to use it later. | |
EncryptionPassword = password; | |
HasBeenDecrypted = true; | |
} | |
public void DecryptBodyCertificate(X509Certificate2 certificate = null) | |
{ | |
if (!EncryptionEnabled) | |
throw new CryptographicException("Encryption is not enabled."); | |
if (FileEncryptionType != EncryptionType.Certificate) | |
throw new CryptographicException("Encryption type does not match the decryption method."); | |
XmlNode bodyNode = QuerySingleNode("//xns:drx/xns:body"); | |
XmlNode encryptionKey = QuerySingleNode("//xns:drx/@encryptionKey"); | |
if (EncryptionSerial == null) | |
throw new CryptographicException("The encryption serial is null!"); | |
if (certificate == null) | |
{ | |
certificate = EncryptionProvider.GetCertificateFromSerial(EncryptionSerial); | |
if (certificate == null) | |
throw new CryptographicException("No matching certificates were found in the local user store to decrypt the document."); | |
} | |
EncryptionCertificateKey = encryptionKey.InnerText; | |
// Create a string to store our sha512 string. | |
string bodyShaSum = null; | |
string cryptoException = null; | |
// Attempt to decrypt the body text. | |
try | |
{ | |
// Decrypt the encrypted encryption key with the certificate. | |
encryptionDecryptedKey = EncryptionProvider.DecryptWithCert(certificate, Convert.FromBase64String(EncryptionCertificateKey)); | |
string DecryptedKey = Convert.ToBase64String(encryptionDecryptedKey); | |
// Create a new encryption provider using the decrypted encryption key to decrypt the body. | |
EncryptionProvider enc = new EncryptionProvider(DecryptedKey); | |
// Decrypt the body using the decryption key and pass it to the editor. | |
bodyText = enc.Decrypt(GetBodyContents(true)); | |
// Set the serial of the certificate, if decryption succeeds. | |
EncryptionSerial = certificate.SerialNumber; | |
} | |
// The password is incorrect, or something bad happened. | |
catch (CryptographicException e) | |
{ | |
// Make sure we set the BodyText to something invalid, so the next hash won't succeed. | |
bodyText = "IncorrectPassword"; | |
cryptoException = e.Message; | |
} | |
// Create another SHA512 object to validate the hash of the body. | |
using (SHA512 SHA = new SHA512Managed()) | |
{ | |
byte[] bodyByte = Encoding.UTF8.GetBytes(bodyText); | |
byte[] bodyBytes = SHA.ComputeHash(bodyByte); | |
string comparisonHash = Encoding.UTF8.GetString(bodyBytes, 0, bodyBytes.Length); | |
bodyShaSum = Convert.ToBase64String(Encoding.UTF8.GetBytes(comparisonHash)); | |
} | |
if (bodyShaSum != null) | |
{ | |
// Check whether the SHA512 hash of the body matches the EncryptionWrapper hash we stored in the file header | |
// while we were creating the DRX. | |
if (bodyShaSum == BodyHash) | |
{ | |
EncryptionCertificate = certificate; | |
HasBeenDecrypted = true; | |
return; | |
} | |
else | |
// The password is incorrect, or the hash or body is somehow corrupt. | |
{ | |
if (cryptoException == "Padding is invalid and cannot be removed.") { cryptoException = "The decryption key is incorrect."; }; | |
throw new DRXLoadException(strings.DRXCorrupted + cryptoException ?? "Unknown error."); | |
} | |
} | |
throw new DRXLoadException(strings.DRXCorrupted + "Unknown error."); | |
} | |
/// <summary> | |
/// Sets the encryption key to the specified password. | |
/// </summary> | |
/// <param name="Password">The password.</param> | |
public bool SetEncryptionKey(string Password) | |
{ | |
// Make sure encryption is actually enabled. | |
if (EncryptionEnabled == true && EncryptionVersion != 0) | |
{ | |
// Create a new SHA512 object, and hash the encryption key with it. | |
EncryptionKey = EncryptionProvider.Sha512(Password); | |
// Return true. | |
return true; | |
} | |
// Encryption isn't enabled. | |
else | |
{ | |
// Return false. | |
return false; | |
} | |
} | |
public void HashBody() | |
{ | |
// Do this only if we're not provisioning encryption this cycle, because otherwise we'd be hashing nothing. | |
if (EncryptionProvisioning != true) | |
{ | |
// Set the encryption wrapper to the hash of the body. | |
BodyHash = EncryptionProvider.Sha512(bodyText); | |
} | |
} | |
/// <summary> | |
/// Initializes a new DRX document with baseline XML. | |
/// </summary> | |
/// <param name="Document">The XmlDocument instance you'd like to initialize.</param> | |
public bool InitializeDocument() | |
{ | |
// Initialize the root namespace. | |
XmlDeclaration declaration = activeDocument.CreateXmlDeclaration("1.0", "UTF-8", null); | |
activeDocument.AppendChild(declaration); | |
// Add all elements to the root. | |
XmlElement rootElement = activeDocument.CreateElement("drx", "core://drx.schema.core/schemas/DRXSchema.xsd"); | |
XmlElement header = activeDocument.CreateElement("header", "core://drx.schema.core/schemas/DRXSchema.xsd"); | |
XmlElement body = activeDocument.CreateElement("body", "core://drx.schema.core/schemas/DRXSchema.xsd"); | |
// Set all root elements to empty or default values. | |
rootElement.SetAttribute("id", "0"); | |
rootElement.SetAttribute("fileRevision", "0"); | |
rootElement.SetAttribute("series", "0"); | |
rootElement.SetAttribute("encryptionEnabled", "false"); | |
rootElement.SetAttribute("vividity", "0"); | |
rootElement.SetAttribute("remembrance", "0"); | |
rootElement.SetAttribute("emotion", "0"); | |
rootElement.SetAttribute("length", "0"); | |
// Initialize the header namespace. | |
XmlElement friendlyName = activeDocument.CreateElement("friendlyName", "core://drx.schema.core/schemas/DRXSchema.xsd"); | |
XmlElement securityLevel = activeDocument.CreateElement("secLevel", "core://drx.schema.core/schemas/DRXSchema.xsd"); | |
XmlElement redactions = activeDocument.CreateElement("redactions", "core://drx.schema.core/schemas/DRXSchema.xsd"); | |
XmlElement flags = activeDocument.CreateElement("flags", "core://drx.schema.core/schemas/DRXSchema.xsd"); | |
XmlElement setting = activeDocument.CreateElement("setting", "core://drx.schema.core/schemas/DRXSchema.xsd"); | |
XmlElement date = activeDocument.CreateElement("date", "core://drx.schema.core/schemas/DRXSchema.xsd"); | |
XmlElement status = activeDocument.CreateElement("status", "core://drx.schema.core/schemas/DRXSchema.xsd"); | |
// Save all of the nodes. | |
SaveNewNode(header, friendlyName); | |
SaveNewNode(header, securityLevel); | |
SaveNewNode(header, redactions); | |
SaveNewNode(header, flags); | |
SaveNewNode(header, setting); | |
SaveNewNode(header, date); | |
SaveNewNode(header, status); | |
SaveNewNode(rootElement, header); | |
SaveNewNode(rootElement, body); | |
// Write it all to the root. | |
activeDocument.AppendChild(rootElement); | |
// Return true. | |
return true; | |
} | |
/// <summary> | |
/// Sets an attribute using the given XPath query string and value. | |
/// </summary> | |
/// <param name="XPath">The XPath query you'd like to run.</param> | |
/// <param name="Value">The value to set the attribute to.</param> | |
private void SetAttribute(string XPath, string Value) | |
{ | |
XmlNode temporaryElement = QuerySingleNode(XPath); | |
if (temporaryElement != null) | |
temporaryElement.Value = Value; | |
} | |
/// <summary> | |
/// Apply some nice formatting to the node. It's not the best, but at least everything's not on one line... | |
/// </summary> | |
/// <param name="ParentNode">The node that is the direct parent of the child node.</param> | |
/// <param name="ChildNode">The node you'd like to save.</param> | |
private void SaveNewNode(XmlNode ParentNode, XmlElement ChildNode) | |
{ | |
ParentNode.AppendChild(ChildNode); | |
ParentNode.InnerXml = ParentNode.InnerXml.Replace(ChildNode.OuterXml, ChildNode.OuterXml + Environment.NewLine); | |
} | |
/// <summary> | |
/// Apply some nice formatting to the declaration. See the method SaveNewNode(). | |
/// </summary> | |
/// <param name="ParentNode">The node that is the direct parent of the child node.</param> | |
/// <param name="ChildNode">The node you'd like to save.</param> | |
private void SaveNewDeclaration(XmlNode T_ParentNode, XmlDeclaration T_ChildNode) | |
{ | |
activeDocument.InsertBefore(T_ChildNode, T_ParentNode); | |
} | |
/// <summary> | |
/// Method to validate and save the DRX document. | |
/// </summary> | |
/// <param name="filePath"></param> | |
public async Task<bool> SaveChanges(bool skipCoreServer = false, bool skipUpdate = false) | |
{ | |
if (!skipUpdate) | |
UpdateDocument(); | |
// Validate the document. | |
activeDocument.Validate(ValidationCallback); | |
// Make sure no errors were set by the callback function. | |
if (errorsOccured == true) | |
{ | |
throw new Exception(strings.DRXSaveErrors); | |
} | |
else | |
{ | |
try | |
{ | |
RemoteDRXController controller = new RemoteDRXController(serviceProvider); | |
await controller.SaveFile(this, Id, skipCoreServer); | |
return true; | |
} | |
catch (Exception e) | |
{ | |
throw new Exception(strings.DRXSaveErrorsException + e, e); | |
} | |
} | |
} | |
/// <summary> | |
/// Method to send an XPath query. | |
/// </summary> | |
/// <param name="QueryString">The XPath query string.</param> | |
/// <returns></returns> | |
private XmlNode QuerySingleNode(string QueryString) | |
{ | |
return activeDocument.DocumentElement.SelectSingleNode(QueryString, activeNamespace); | |
} | |
/// <summary> | |
/// Method to load a XML schema from disk. | |
/// </summary> | |
/// <param name="SchemaName">The filename of the XML schema.</param> | |
private void LoadSchema(string SchemaName) | |
{ | |
try | |
{ | |
StreamReader schemaFile = File.OpenText(SchemaName); | |
XmlSchema documentSchema = XmlSchema.Read(schemaFile, ValidationCallback); | |
schemaFile.Close(); | |
activeDocument.Schemas.Add(documentSchema); | |
} | |
catch (FileNotFoundException e) | |
{ | |
throw new Exception(strings.DRXSchemaLoadError, e); | |
} | |
} | |
private string BooleanToString(bool boolean) | |
{ | |
if (boolean) { return "true"; } else { return "false"; } | |
} | |
/// <summary> | |
/// Event to catch errors from XmlDocument.Validate(). | |
/// </summary> | |
/// <param name="sender"></param> | |
/// <param name="a"></param> | |
private void ValidationCallback(object sender, ValidationEventArgs a) | |
{ | |
if (a.Severity == XmlSeverityType.Warning) | |
{ | |
//MessageBox.Show(strings.DRXLoadWarning + a.Message, strings.DRXEditor, MessageBoxButtons.OK, MessageBoxIcon.Warning); | |
} | |
else if (a.Severity == XmlSeverityType.Error) | |
{ | |
//MessageBox.Show(strings.DRXLoadCorrupt + a.Message, strings.DRXEditor, MessageBoxButtons.OK, MessageBoxIcon.Error); | |
errorsOccured = true; | |
} | |
} | |
#region IDisposable Support | |
protected bool disposedValue = false; // To detect redundant calls | |
protected virtual void Dispose(bool disposing) | |
{ | |
if (!disposedValue) | |
{ | |
if (disposing) | |
{ | |
fileReader.Close(); | |
fileReader.Dispose(); | |
} | |
// TODO: free unmanaged resources (unmanaged objects) and override a finalizer below. | |
// TODO: set large fields to null. | |
disposedValue = true; | |
} | |
} | |
// TODO: override a finalizer only if Dispose(bool disposing) above has code to free unmanaged resources. | |
// ~IFileProvider() { | |
// // Do not change this code. Put cleanup code in Dispose(bool disposing) above. | |
// Dispose(false); | |
// } | |
// This code added to correctly implement the disposable pattern. | |
public void Dispose() | |
{ | |
// Do not change this code. Put cleanup code in Dispose(bool disposing) above. | |
Dispose(true); | |
// TODO: uncomment the following line if the finalizer is overridden above. | |
// GC.SuppressFinalize(this); | |
} | |
#endregion | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment