support for Keyfiles like in Keepass 2.47
This commit is contained in:
@@ -72,6 +72,7 @@
|
||||
<Compile Include="Cryptography\KeyDerivation\KdfParameters.cs" />
|
||||
<Compile Include="Cryptography\KeyDerivation\KdfPool.cs" />
|
||||
<Compile Include="IDatabaseFormat.cs" />
|
||||
<Compile Include="Keys\KcpKeyFile.Xml.cs" />
|
||||
<Compile Include="Kp2aLog.cs" />
|
||||
<Compile Include="Resources\Resource.designer.cs" />
|
||||
<Compile Include="Resources\KLRes.Generated.cs" />
|
||||
@@ -161,6 +162,7 @@
|
||||
<Compile Include="Utility\UrlUtil.cs" />
|
||||
<Compile Include="Utility\TimeUtil.cs" />
|
||||
<Compile Include="Delegates\Handlers.cs" />
|
||||
<Compile Include="Utility\XmlUtilEx.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
|
||||
281
src/KeePassLib2Android/Keys/KcpKeyFile.Xml.cs
Normal file
281
src/KeePassLib2Android/Keys/KcpKeyFile.Xml.cs
Normal file
@@ -0,0 +1,281 @@
|
||||
/*
|
||||
KeePass Password Safe - The Open-Source Password Manager
|
||||
Copyright (C) 2003-2021 Dominik Reichl <dominik.reichl@t-online.de>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Xml.Serialization;
|
||||
|
||||
using KeePassLib.Cryptography;
|
||||
using KeePassLib.Resources;
|
||||
using KeePassLib.Serialization;
|
||||
using KeePassLib.Utility;
|
||||
|
||||
namespace KeePassLib.Keys
|
||||
{
|
||||
[XmlType("KeyFile")]
|
||||
public sealed class KfxFile
|
||||
{
|
||||
private const ulong KfxVersionCriticalMask = 0xFFFF000000000000UL;
|
||||
private const int KfxDataHashLength = 4;
|
||||
|
||||
private KfxMeta m_meta = new KfxMeta();
|
||||
public KfxMeta Meta
|
||||
{
|
||||
get { return m_meta; }
|
||||
set
|
||||
{
|
||||
if(value == null) throw new ArgumentNullException("value");
|
||||
m_meta = value;
|
||||
}
|
||||
}
|
||||
|
||||
private KfxKey m_key = new KfxKey();
|
||||
public KfxKey Key
|
||||
{
|
||||
get { return m_key; }
|
||||
set
|
||||
{
|
||||
if(value == null) throw new ArgumentNullException("value");
|
||||
m_key = value;
|
||||
}
|
||||
}
|
||||
|
||||
public static KfxFile Create(ulong uVersion, byte[] pbKey, byte[] pbHash)
|
||||
{
|
||||
if(pbKey == null) throw new ArgumentNullException("pbKey");
|
||||
if(pbKey.Length == 0) throw new ArgumentOutOfRangeException("pbKey");
|
||||
|
||||
if(uVersion == 0) uVersion = 0x0002000000000000;
|
||||
|
||||
// Null hash: generate one, empty hash: store no hash
|
||||
if(pbHash == null) pbHash = HashData(pbKey);
|
||||
VerifyHash(pbKey, pbHash);
|
||||
|
||||
KfxFile kf = new KfxFile();
|
||||
|
||||
if(uVersion == 0x0001000000000000)
|
||||
kf.Meta.Version = "1.00"; // KeePass <= 2.46 used two zeros
|
||||
else kf.Meta.Version = StrUtil.VersionToString(uVersion, 2);
|
||||
|
||||
if(uVersion == 0x0001000000000000)
|
||||
kf.Key.Data.Value = Convert.ToBase64String(pbKey);
|
||||
else if(uVersion == 0x0002000000000000)
|
||||
{
|
||||
kf.Key.Data.Value = FormatKeyHex(pbKey, 3);
|
||||
|
||||
if(pbHash.Length != 0)
|
||||
kf.Key.Data.Hash = MemUtil.ByteArrayToHexString(pbHash);
|
||||
}
|
||||
else throw new NotSupportedException(KLRes.FileVersionUnsupported);
|
||||
|
||||
return kf;
|
||||
}
|
||||
|
||||
internal static KfxFile Create(ulong uVersion, string strKey, string strHash)
|
||||
{
|
||||
byte[] pbKey = ParseKey(uVersion, strKey);
|
||||
byte[] pbHash = ((strHash != null) ? ParseHash(strHash) : null);
|
||||
|
||||
return Create(uVersion, pbKey, pbHash);
|
||||
}
|
||||
|
||||
internal static bool CanLoad(string strFilePath)
|
||||
{
|
||||
if(string.IsNullOrEmpty(strFilePath)) { Debug.Assert(false); return false; }
|
||||
|
||||
try
|
||||
{
|
||||
IOConnectionInfo ioc = IOConnectionInfo.FromPath(strFilePath);
|
||||
using(Stream s = IOConnection.OpenRead(ioc))
|
||||
{
|
||||
return (Load(s) != null);
|
||||
}
|
||||
}
|
||||
catch(Exception) { }
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static KfxFile Load(Stream s)
|
||||
{
|
||||
return XmlUtilEx.Deserialize<KfxFile>(s);
|
||||
}
|
||||
|
||||
public void Save(Stream s)
|
||||
{
|
||||
XmlUtilEx.Serialize<KfxFile>(s, this, true);
|
||||
}
|
||||
|
||||
private static string FormatKeyHex(byte[] pb, int cTabs)
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
string str = MemUtil.ByteArrayToHexString(pb);
|
||||
|
||||
for(int i = 0; i < str.Length; ++i)
|
||||
{
|
||||
if((i & 0x1F) == 0)
|
||||
{
|
||||
sb.AppendLine();
|
||||
sb.Append('\t', cTabs);
|
||||
}
|
||||
else if((i & 0x07) == 0) sb.Append(' ');
|
||||
|
||||
sb.Append(str[i]);
|
||||
}
|
||||
|
||||
sb.AppendLine();
|
||||
if(cTabs > 0) sb.Append('\t', cTabs - 1);
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
private ulong GetVersion()
|
||||
{
|
||||
string str = m_meta.Version;
|
||||
if(string.IsNullOrEmpty(str)) return 0;
|
||||
|
||||
return StrUtil.ParseVersion(str);
|
||||
}
|
||||
|
||||
public byte[] GetKey()
|
||||
{
|
||||
ulong uVersion = GetVersion();
|
||||
|
||||
byte[] pbKey = ParseKey(uVersion, m_key.Data.Value);
|
||||
if((pbKey == null) || (pbKey.Length == 0))
|
||||
throw new FormatException(KLRes.FileCorrupted);
|
||||
|
||||
byte[] pbHash = ParseHash(m_key.Data.Hash);
|
||||
VerifyHash(pbKey, pbHash);
|
||||
|
||||
return pbKey;
|
||||
}
|
||||
|
||||
private static byte[] HashData(byte[] pb)
|
||||
{
|
||||
return MemUtil.Mid(CryptoUtil.HashSha256(pb), 0, KfxDataHashLength);
|
||||
}
|
||||
|
||||
private static void VerifyHash(byte[] pbKey, byte[] pbHash)
|
||||
{
|
||||
// The hash is optional; empty hash means success
|
||||
if((pbHash == null) || (pbHash.Length == 0)) return;
|
||||
|
||||
byte[] pbHashCmp = HashData(pbKey);
|
||||
if(!MemUtil.ArraysEqual(pbHash, pbHashCmp))
|
||||
throw new Exception("Keyfile hash mismatch!");
|
||||
}
|
||||
|
||||
private static byte[] ParseKey(ulong uVersion, string strKey)
|
||||
{
|
||||
if(strKey == null) throw new ArgumentNullException("strKey");
|
||||
|
||||
strKey = StrUtil.RemoveWhiteSpace(strKey);
|
||||
if(string.IsNullOrEmpty(strKey)) return MemUtil.EmptyByteArray;
|
||||
|
||||
uVersion &= KfxVersionCriticalMask;
|
||||
|
||||
byte[] pbKey;
|
||||
if(uVersion == 0x0001000000000000)
|
||||
pbKey = Convert.FromBase64String(strKey);
|
||||
else if(uVersion == 0x0002000000000000)
|
||||
pbKey = ParseHex(strKey);
|
||||
else throw new NotSupportedException(KLRes.FileVersionUnsupported);
|
||||
|
||||
return pbKey;
|
||||
}
|
||||
|
||||
private static byte[] ParseHash(string strHash)
|
||||
{
|
||||
return ParseHex(strHash);
|
||||
}
|
||||
|
||||
private static byte[] ParseHex(string str)
|
||||
{
|
||||
if(str == null) throw new ArgumentNullException("str");
|
||||
if(str.Length == 0) return MemUtil.EmptyByteArray;
|
||||
|
||||
if(((str.Length & 1) != 0) || !StrUtil.IsHexString(str, true))
|
||||
throw new FormatException();
|
||||
|
||||
return MemUtil.HexStringToByteArray(str);
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class KfxMeta
|
||||
{
|
||||
private string m_strVersion = string.Empty;
|
||||
[DefaultValue("")]
|
||||
public string Version
|
||||
{
|
||||
get { return m_strVersion; }
|
||||
set
|
||||
{
|
||||
if(value == null) throw new ArgumentNullException("value");
|
||||
m_strVersion = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class KfxKey
|
||||
{
|
||||
private KfxData m_data = new KfxData();
|
||||
public KfxData Data
|
||||
{
|
||||
get { return m_data; }
|
||||
set
|
||||
{
|
||||
if(value == null) throw new ArgumentNullException("value");
|
||||
m_data = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class KfxData
|
||||
{
|
||||
private string m_strHash = string.Empty;
|
||||
[DefaultValue("")]
|
||||
[XmlAttribute("Hash")]
|
||||
public string Hash
|
||||
{
|
||||
get { return m_strHash; }
|
||||
set
|
||||
{
|
||||
if(value == null) throw new ArgumentNullException("value");
|
||||
m_strHash = value;
|
||||
}
|
||||
}
|
||||
|
||||
private string m_strValue = string.Empty;
|
||||
[DefaultValue("")]
|
||||
[XmlText]
|
||||
public string Value
|
||||
{
|
||||
get { return m_strValue; }
|
||||
set
|
||||
{
|
||||
if(value == null) throw new ArgumentNullException("value");
|
||||
m_strValue = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -127,10 +127,9 @@ namespace KeePassLib.Keys
|
||||
#endif
|
||||
}
|
||||
|
||||
byte[] pbKey = LoadXmlKeyFile(pbFileData);
|
||||
if(pbKey == null) pbKey = LoadKeyFile(pbFileData);
|
||||
byte[] pbKey = LoadKeyFile(pbFileData);
|
||||
|
||||
if(pbKey == null) throw new InvalidOperationException();
|
||||
if (pbKey == null) throw new InvalidOperationException();
|
||||
|
||||
m_ioc = iocKeyFile;
|
||||
m_pbKeyData = new ProtectedBinary(true, pbKey);
|
||||
@@ -150,54 +149,60 @@ namespace KeePassLib.Keys
|
||||
// }
|
||||
|
||||
|
||||
private static byte[] LoadKeyFile(byte[] pbFileData)
|
||||
{
|
||||
if(pbFileData == null) { Debug.Assert(false); return null; }
|
||||
private static byte[] LoadKeyFile(byte[] pbFileData)
|
||||
{
|
||||
if (pbFileData == null) throw new ArgumentNullException("pbFileData");
|
||||
|
||||
int iLength = pbFileData.Length;
|
||||
byte[] pbKey = LoadKeyFileXml(pbFileData);
|
||||
if (pbKey != null) return pbKey;
|
||||
|
||||
byte[] pbKey = null;
|
||||
if(iLength == 32) pbKey = LoadBinaryKey32(pbFileData);
|
||||
else if(iLength == 64) pbKey = LoadHexKey32(pbFileData);
|
||||
int cb = pbFileData.Length;
|
||||
if (cb == 32) return pbFileData;
|
||||
|
||||
if(pbKey == null)
|
||||
pbKey = CryptoUtil.HashSha256(pbFileData);
|
||||
if (cb == 64)
|
||||
{
|
||||
pbKey = LoadKeyFileHex(pbFileData);
|
||||
if (pbKey != null) return pbKey;
|
||||
}
|
||||
|
||||
return pbKey;
|
||||
}
|
||||
return CryptoUtil.HashSha256(pbFileData);
|
||||
}
|
||||
|
||||
private static byte[] LoadBinaryKey32(byte[] pbFileData)
|
||||
{
|
||||
if(pbFileData == null) { Debug.Assert(false); return null; }
|
||||
if(pbFileData.Length != 32) { Debug.Assert(false); return null; }
|
||||
private static byte[] LoadKeyFileXml(byte[] pbFileData)
|
||||
{
|
||||
KfxFile kf;
|
||||
try
|
||||
{
|
||||
using (MemoryStream ms = new MemoryStream(pbFileData, false))
|
||||
{
|
||||
kf = KfxFile.Load(ms);
|
||||
}
|
||||
}
|
||||
catch (Exception) { return null; }
|
||||
|
||||
return pbFileData;
|
||||
}
|
||||
// We have a syntactically valid XML key file;
|
||||
// failing to verify the key should throw an exception
|
||||
return ((kf != null) ? kf.GetKey() : null);
|
||||
}
|
||||
|
||||
private static byte[] LoadHexKey32(byte[] pbFileData)
|
||||
{
|
||||
if(pbFileData == null) { Debug.Assert(false); return null; }
|
||||
if(pbFileData.Length != 64) { Debug.Assert(false); return null; }
|
||||
private static byte[] LoadKeyFileHex(byte[] pbFileData)
|
||||
{
|
||||
if (pbFileData == null) { Debug.Assert(false); return null; }
|
||||
|
||||
try
|
||||
{
|
||||
if(!StrUtil.IsHexString(pbFileData, true)) return null;
|
||||
try
|
||||
{
|
||||
int cc = pbFileData.Length;
|
||||
if ((cc & 1) != 0) { Debug.Assert(false); return null; }
|
||||
|
||||
string strHex = StrUtil.Utf8.GetString(pbFileData);
|
||||
byte[] pbKey = MemUtil.HexStringToByteArray(strHex);
|
||||
if((pbKey == null) || (pbKey.Length != 32))
|
||||
{
|
||||
Debug.Assert(false);
|
||||
return null;
|
||||
}
|
||||
if (!StrUtil.IsHexString(pbFileData, true)) return null;
|
||||
|
||||
return pbKey;
|
||||
}
|
||||
catch(Exception) { Debug.Assert(false); }
|
||||
|
||||
return null;
|
||||
}
|
||||
string strHex = StrUtil.Utf8.GetString(pbFileData);
|
||||
return MemUtil.HexStringToByteArray(strHex);
|
||||
}
|
||||
catch (Exception) { Debug.Assert(false); }
|
||||
|
||||
return null;
|
||||
}
|
||||
/// <summary>
|
||||
/// Create a new, random key-file.
|
||||
/// </summary>
|
||||
|
||||
@@ -1734,5 +1734,24 @@ namespace KeePassLib.Utility
|
||||
// behavior of Notepad (on Windows 10)
|
||||
return str.Replace('\0', ' ');
|
||||
}
|
||||
|
||||
internal static string RemoveWhiteSpace(string str)
|
||||
{
|
||||
if (str == null) { Debug.Assert(false); return string.Empty; }
|
||||
|
||||
int cc = str.Length;
|
||||
if (cc == 0) return string.Empty;
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
for (int i = 0; i < cc; ++i)
|
||||
{
|
||||
char ch = str[i];
|
||||
if (char.IsWhiteSpace(ch)) continue;
|
||||
sb.Append(ch);
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
290
src/KeePassLib2Android/Utility/XmlUtilEx.cs
Normal file
290
src/KeePassLib2Android/Utility/XmlUtilEx.cs
Normal file
@@ -0,0 +1,290 @@
|
||||
/*
|
||||
KeePass Password Safe - The Open-Source Password Manager
|
||||
Copyright (C) 2003-2021 Dominik Reichl <dominik.reichl@t-online.de>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Xml;
|
||||
using System.Xml.Serialization;
|
||||
using System.Xml.XPath;
|
||||
|
||||
using KeePassLib.Delegates;
|
||||
using KeePassLib.Interfaces;
|
||||
using KeePassLib.Serialization;
|
||||
|
||||
namespace KeePassLib.Utility
|
||||
{
|
||||
public static class XmlUtilEx
|
||||
{
|
||||
public static XmlDocument CreateXmlDocument()
|
||||
{
|
||||
XmlDocument d = new XmlDocument();
|
||||
|
||||
// .NET 4.5.2 and newer do not resolve external XML resources
|
||||
// by default; for older .NET versions, we explicitly
|
||||
// prevent resolving
|
||||
d.XmlResolver = null; // Default in old .NET: XmlUrlResolver object
|
||||
|
||||
return d;
|
||||
}
|
||||
|
||||
public static XmlReaderSettings CreateXmlReaderSettings()
|
||||
{
|
||||
XmlReaderSettings xrs = new XmlReaderSettings();
|
||||
|
||||
xrs.CloseInput = false;
|
||||
xrs.IgnoreComments = true;
|
||||
xrs.IgnoreProcessingInstructions = true;
|
||||
xrs.IgnoreWhitespace = true;
|
||||
|
||||
#if KeePassUAP
|
||||
xrs.DtdProcessing = DtdProcessing.Prohibit;
|
||||
#else
|
||||
// Also see PrepMonoDev.sh script
|
||||
xrs.ProhibitDtd = true; // Obsolete in .NET 4, but still there
|
||||
// xrs.DtdProcessing = DtdProcessing.Prohibit; // .NET 4 only
|
||||
#endif
|
||||
|
||||
xrs.ValidationType = ValidationType.None;
|
||||
xrs.XmlResolver = null;
|
||||
|
||||
return xrs;
|
||||
}
|
||||
|
||||
public static XmlReader CreateXmlReader(Stream s)
|
||||
{
|
||||
if(s == null) { Debug.Assert(false); throw new ArgumentNullException("s"); }
|
||||
|
||||
return XmlReader.Create(s, CreateXmlReaderSettings());
|
||||
}
|
||||
|
||||
public static XmlWriterSettings CreateXmlWriterSettings()
|
||||
{
|
||||
XmlWriterSettings xws = new XmlWriterSettings();
|
||||
|
||||
xws.CloseOutput = false;
|
||||
xws.Encoding = StrUtil.Utf8;
|
||||
xws.Indent = true;
|
||||
xws.IndentChars = "\t";
|
||||
xws.NewLineOnAttributes = false;
|
||||
|
||||
return xws;
|
||||
}
|
||||
|
||||
public static XmlWriter CreateXmlWriter(Stream s)
|
||||
{
|
||||
if(s == null) { Debug.Assert(false); throw new ArgumentNullException("s"); }
|
||||
|
||||
return XmlWriter.Create(s, CreateXmlWriterSettings());
|
||||
}
|
||||
|
||||
public static T Deserialize<T>(Stream s)
|
||||
{
|
||||
if(s == null) { Debug.Assert(false); throw new ArgumentNullException("s"); }
|
||||
|
||||
XmlSerializer xs = new XmlSerializer(typeof(T));
|
||||
|
||||
T t = default(T);
|
||||
using(XmlReader xr = CreateXmlReader(s))
|
||||
{
|
||||
t = (T)xs.Deserialize(xr);
|
||||
}
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
public static void Serialize<T>(Stream s, T t)
|
||||
{
|
||||
if(s == null) { Debug.Assert(false); throw new ArgumentNullException("s"); }
|
||||
|
||||
XmlSerializer xs = new XmlSerializer(typeof(T));
|
||||
using(XmlWriter xw = CreateXmlWriter(s))
|
||||
{
|
||||
xs.Serialize(xw, t);
|
||||
}
|
||||
}
|
||||
|
||||
internal static void Serialize<T>(Stream s, T t, bool bRemoveXsdXsi)
|
||||
{
|
||||
// One way to remove the "xsd" and "xsi" namespace declarations
|
||||
// is to use an XmlSerializerNamespaces object containing only
|
||||
// a ""/"" pair; this seems to work, but Microsoft's
|
||||
// documentation explicitly states that it isn't supported:
|
||||
// https://docs.microsoft.com/en-us/dotnet/api/system.xml.serialization.xmlserializernamespaces
|
||||
// There are other, more complex ways, but these either rely on
|
||||
// undocumented details or require the type T to be modified.
|
||||
|
||||
string str;
|
||||
using(MemoryStream ms = new MemoryStream())
|
||||
{
|
||||
Serialize<T>(ms, t);
|
||||
|
||||
str = StrUtil.Utf8.GetString(ms.ToArray());
|
||||
}
|
||||
|
||||
Func<string, string, bool> fFindPfx = delegate(string strText, string strSub)
|
||||
{
|
||||
int i = strText.IndexOf(strSub, StringComparison.Ordinal);
|
||||
if(i < 0) return false;
|
||||
if(i == 0) return true;
|
||||
return char.IsWhiteSpace(strText[i - 1]);
|
||||
};
|
||||
|
||||
if(bRemoveXsdXsi)
|
||||
{
|
||||
if(!fFindPfx(str, "xsd:") && !fFindPfx(str, "xsi:"))
|
||||
{
|
||||
Debug.Assert(str.IndexOf("xmlns:xsd") > 0);
|
||||
str = str.Replace(" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\"", string.Empty);
|
||||
Debug.Assert(str.IndexOf("xmlns:xsd") < 0);
|
||||
|
||||
Debug.Assert(str.IndexOf("xmlns:xsi") > 0);
|
||||
str = str.Replace(" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"", string.Empty);
|
||||
Debug.Assert(str.IndexOf("xmlns:xsi") < 0);
|
||||
}
|
||||
else { Debug.Assert(false); } // "xsd"/"xsi" decl. may be required
|
||||
}
|
||||
|
||||
MemUtil.Write(s, StrUtil.Utf8.GetBytes(str));
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
internal static void ValidateXml(string strXml, bool bReplaceStdEntities)
|
||||
{
|
||||
if(strXml == null) throw new ArgumentNullException("strXml");
|
||||
if(strXml.Length == 0) { Debug.Assert(false); return; }
|
||||
|
||||
string str = strXml;
|
||||
|
||||
if(bReplaceStdEntities)
|
||||
str = str.Replace(" ", " ");
|
||||
|
||||
XmlDocument d = new XmlDocument();
|
||||
d.LoadXml(str);
|
||||
}
|
||||
#endif
|
||||
|
||||
internal static XPathNodeIterator FindNodes(PwDatabase pd, string strXPath,
|
||||
IStatusLogger sl, out XmlDocument xd)
|
||||
{
|
||||
if(pd == null) throw new ArgumentNullException("pd");
|
||||
if(strXPath == null) { Debug.Assert(false); strXPath = string.Empty; }
|
||||
|
||||
KdbxFile kdbx = new KdbxFile(pd);
|
||||
|
||||
byte[] pbXml;
|
||||
using(MemoryStream ms = new MemoryStream())
|
||||
{
|
||||
kdbx.Save(ms, null, KdbxFormat.PlainXml, sl);
|
||||
pbXml = ms.ToArray();
|
||||
}
|
||||
string strXml = StrUtil.Utf8.GetString(pbXml);
|
||||
|
||||
xd = CreateXmlDocument();
|
||||
xd.LoadXml(strXml);
|
||||
|
||||
XPathNavigator xpNav = xd.CreateNavigator();
|
||||
return xpNav.Select(strXPath);
|
||||
// XPathExpression xpExpr = xpNav.Compile(strXPath);
|
||||
// xpExpr.SetContext(new XuXsltContext());
|
||||
// return xpNav.Select(xpExpr);
|
||||
}
|
||||
|
||||
/* private sealed class XuFnMatches : IXsltContextFunction
|
||||
{
|
||||
private readonly XPathResultType[] m_vArgTypes = new XPathResultType[] {
|
||||
XPathResultType.String, XPathResultType.String, XPathResultType.String
|
||||
};
|
||||
public XPathResultType[] ArgTypes { get { return m_vArgTypes; } }
|
||||
|
||||
public int Maxargs { get { return 3; } }
|
||||
public int Minargs { get { return 2; } }
|
||||
|
||||
public XPathResultType ReturnType { get { return XPathResultType.Boolean; } }
|
||||
|
||||
private static string GetArgString(object[] args, int i, string strDefault)
|
||||
{
|
||||
if(args == null) { Debug.Assert(false); return strDefault; }
|
||||
if(i >= args.Length) return strDefault;
|
||||
|
||||
object o = args[i];
|
||||
if(o == null) return strDefault;
|
||||
|
||||
XPathNodeIterator it = (o as XPathNodeIterator);
|
||||
if(it != null) o = it.Current.Value;
|
||||
|
||||
return (o.ToString() ?? strDefault);
|
||||
}
|
||||
|
||||
public object Invoke(XsltContext xsltContext, object[] args,
|
||||
XPathNavigator docContext)
|
||||
{
|
||||
string strInput = GetArgString(args, 0, string.Empty);
|
||||
string strPattern = GetArgString(args, 1, string.Empty);
|
||||
string strFlags = GetArgString(args, 2, null);
|
||||
|
||||
RegexOptions ro = RegexOptions.None;
|
||||
if(!string.IsNullOrEmpty(strFlags))
|
||||
{
|
||||
if(strFlags.IndexOf('s') >= 0) ro |= RegexOptions.Singleline;
|
||||
if(strFlags.IndexOf('m') >= 0) ro |= RegexOptions.Multiline;
|
||||
if(strFlags.IndexOf('i') >= 0) ro |= RegexOptions.IgnoreCase;
|
||||
if(strFlags.IndexOf('x') >= 0) ro |= RegexOptions.IgnorePatternWhitespace;
|
||||
}
|
||||
|
||||
return Regex.IsMatch(strInput, strPattern, ro);
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class XuXsltContext : XsltContext
|
||||
{
|
||||
public override bool Whitespace { get { return false; } }
|
||||
|
||||
public override int CompareDocument(string baseUri, string nextbaseUri)
|
||||
{
|
||||
return string.CompareOrdinal(baseUri, nextbaseUri);
|
||||
}
|
||||
|
||||
public override bool PreserveWhitespace(XPathNavigator node)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public override IXsltContextFunction ResolveFunction(string prefix,
|
||||
string name, XPathResultType[] ArgTypes)
|
||||
{
|
||||
if(prefix != "kp") { Debug.Assert(false); return null; }
|
||||
|
||||
if(name == "matches") return new XuFnMatches();
|
||||
|
||||
Debug.Assert(false);
|
||||
return null;
|
||||
}
|
||||
|
||||
public override IXsltContextVariable ResolveVariable(string prefix,
|
||||
string name)
|
||||
{
|
||||
Debug.Assert(false);
|
||||
return null;
|
||||
}
|
||||
} */
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user