mirror of
https://github.com/wismna/ModernKeePass.git
synced 2025-10-04 08:00:16 -04:00
KeePassLib finally portable (but version 2.19)
Windows Store project stub
This commit is contained in:
63
ModernKeePassLib/Serialization/BinaryReaderEx.cs
Normal file
63
ModernKeePassLib/Serialization/BinaryReaderEx.cs
Normal file
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
KeePass Password Safe - The Open-Source Password Manager
|
||||
Copyright (C) 2003-2012 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.Text;
|
||||
using System.IO;
|
||||
|
||||
namespace ModernKeePassLib.Serialization
|
||||
{
|
||||
public sealed class BinaryReaderEx : BinaryReader
|
||||
{
|
||||
private string m_strReadExcp = null;
|
||||
public string ReadExceptionText
|
||||
{
|
||||
get { return m_strReadExcp; }
|
||||
set { m_strReadExcp = value; }
|
||||
}
|
||||
|
||||
public BinaryReaderEx(Stream input, Encoding encoding,
|
||||
string strReadExceptionText) :
|
||||
base(input, encoding)
|
||||
{
|
||||
m_strReadExcp = strReadExceptionText;
|
||||
}
|
||||
|
||||
public override byte[] ReadBytes(int count)
|
||||
{
|
||||
try
|
||||
{
|
||||
byte[] pb = base.ReadBytes(count);
|
||||
if((pb == null) || (pb.Length != count))
|
||||
{
|
||||
if(m_strReadExcp != null) throw new IOException(m_strReadExcp);
|
||||
else throw new EndOfStreamException();
|
||||
}
|
||||
|
||||
return pb;
|
||||
}
|
||||
catch(Exception)
|
||||
{
|
||||
if(m_strReadExcp != null) throw new IOException(m_strReadExcp);
|
||||
else throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
140
ModernKeePassLib/Serialization/CryptoStream.cs
Normal file
140
ModernKeePassLib/Serialization/CryptoStream.cs
Normal file
@@ -0,0 +1,140 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Windows.Security.Cryptography;
|
||||
using Windows.Security.Cryptography.Core;
|
||||
using Windows.Storage.Streams;
|
||||
|
||||
namespace ModernKeePassLib.Serialization
|
||||
{
|
||||
// An adaptor function to provide a stream interface from an IBuffer.
|
||||
|
||||
class CryptoStream : Stream
|
||||
{
|
||||
|
||||
private int m_blockSize = 16 ;
|
||||
private byte[] m_decoded;
|
||||
private IEnumerator<byte> m_enumerator = null;
|
||||
|
||||
public CryptoStream(Stream s, String strAlgName, bool bEncrypt, byte[] pbKey, byte[] pbIV)
|
||||
: base()
|
||||
{
|
||||
IBuffer iv = CryptographicBuffer.CreateFromByteArray(pbIV);
|
||||
SymmetricKeyAlgorithmProvider objAlg = SymmetricKeyAlgorithmProvider.OpenAlgorithm(strAlgName);
|
||||
CryptographicKey key = objAlg.CreateSymmetricKey( CryptographicBuffer.CreateFromByteArray(pbKey) );
|
||||
if (bEncrypt)
|
||||
{
|
||||
Debug.Assert(false, "Not implemented yet");
|
||||
}
|
||||
else
|
||||
{
|
||||
// For the time being, WinRT CryptographicEngine doesn't support stream decoding. Bummer.
|
||||
// Copy the file to a memory buffer, then decode all at once.
|
||||
|
||||
|
||||
byte[] block = new byte[s.Length]; // We are not at the beginning of the stream
|
||||
// There is always less than s.Lenght bytes remaining to be read.
|
||||
int readItemCount = s.Read(block, 0, (int) s.Length);
|
||||
Array.Resize(ref block, readItemCount);
|
||||
|
||||
IBuffer input = CryptographicBuffer.CreateFromByteArray(block);
|
||||
IBuffer decoded = null;
|
||||
|
||||
try
|
||||
{
|
||||
decoded = CryptographicEngine.Decrypt(key, input, iv);
|
||||
} catch (System.Exception)
|
||||
{
|
||||
throw new Keys.InvalidCompositeKeyException();
|
||||
}
|
||||
CryptographicBuffer.CopyToByteArray(decoded, out m_decoded);
|
||||
m_enumerator = m_decoded.AsEnumerable().GetEnumerator();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public override bool CanRead { get { return true; } }
|
||||
public override bool CanSeek { get { return false; } }
|
||||
public override bool CanTimeout { get { return false; } }
|
||||
public override bool CanWrite { get { return false; } }
|
||||
public override long Length
|
||||
{
|
||||
get
|
||||
{
|
||||
throw new System.NotSupportedException();
|
||||
}
|
||||
}
|
||||
|
||||
public override long Position {
|
||||
get
|
||||
{
|
||||
throw new System.NotSupportedException();
|
||||
}
|
||||
set
|
||||
{
|
||||
throw new System.NotSupportedException();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public override void Flush()
|
||||
{
|
||||
Debug.Assert(false, "Not yet implemented");
|
||||
}
|
||||
|
||||
|
||||
public override int Read(byte[] buffer, int offset, int count)
|
||||
{
|
||||
// Exceptions:
|
||||
// System.ArgumentException:
|
||||
// The sum of offset and count is larger than the buffer length.
|
||||
//
|
||||
// System.ArgumentNullException:
|
||||
// buffer is null.
|
||||
//
|
||||
// System.ArgumentOutOfRangeException:
|
||||
// offset or count is negative.
|
||||
//
|
||||
// System.IO.IOException:
|
||||
// An I/O error occurs.
|
||||
//
|
||||
// System.NotSupportedException:
|
||||
// The stream does not support reading.
|
||||
//
|
||||
// System.ObjectDisposedException:
|
||||
// Methods were called after the stream was closed.
|
||||
|
||||
if ((count <0) || (offset <0))
|
||||
throw new System.ArgumentOutOfRangeException();
|
||||
if (buffer == null)
|
||||
throw new System.ArgumentNullException();
|
||||
if (m_enumerator == null)
|
||||
throw new System.ArgumentNullException();
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
if (!m_enumerator.MoveNext())
|
||||
return i;
|
||||
buffer[i + offset] = m_enumerator.Current;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
public override long Seek(long offset, SeekOrigin origin)
|
||||
{
|
||||
throw new System.NotSupportedException();
|
||||
}
|
||||
|
||||
public override void SetLength(long value)
|
||||
{
|
||||
throw new System.NotSupportedException();
|
||||
}
|
||||
|
||||
public override void Write(byte[] buffer, int offset, int count)
|
||||
{
|
||||
throw new System.NotSupportedException();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
280
ModernKeePassLib/Serialization/FileLock.cs
Normal file
280
ModernKeePassLib/Serialization/FileLock.cs
Normal file
@@ -0,0 +1,280 @@
|
||||
/*
|
||||
KeePass Password Safe - The Open-Source Password Manager
|
||||
Copyright (C) 2003-2012 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.Text;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Diagnostics;
|
||||
|
||||
using ModernKeePassLib.Cryptography;
|
||||
using ModernKeePassLib.Resources;
|
||||
using ModernKeePassLib.Utility;
|
||||
|
||||
namespace ModernKeePassLib.Serialization
|
||||
{
|
||||
public sealed class FileLockException : Exception
|
||||
{
|
||||
private readonly string m_strMsg;
|
||||
|
||||
public override string Message
|
||||
{
|
||||
get { return m_strMsg; }
|
||||
}
|
||||
|
||||
public FileLockException(string strBaseFile, string strUser)
|
||||
{
|
||||
Debug.Assert(false, "Not yet implemented");
|
||||
return;
|
||||
#if TODO
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
if(!string.IsNullOrEmpty(strBaseFile))
|
||||
{
|
||||
sb.Append(strBaseFile);
|
||||
sb.Append(MessageService.NewParagraph);
|
||||
}
|
||||
|
||||
sb.Append(KLRes.FileLockedWrite);
|
||||
sb.Append(MessageService.NewLine);
|
||||
|
||||
if(!string.IsNullOrEmpty(strUser)) sb.Append(strUser);
|
||||
else sb.Append("?");
|
||||
|
||||
sb.Append(MessageService.NewParagraph);
|
||||
sb.Append(KLRes.TryAgainSecs);
|
||||
|
||||
m_strMsg = sb.ToString();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class FileLock : IDisposable
|
||||
{
|
||||
private const string LockFileExt = ".lock";
|
||||
private const string LockFileHeader = "KeePass Lock File";
|
||||
|
||||
private IOConnectionInfo m_iocLockFile;
|
||||
|
||||
private sealed class LockFileInfo
|
||||
{
|
||||
public readonly string ID;
|
||||
public readonly DateTime Time;
|
||||
public readonly string UserName;
|
||||
public readonly string Machine;
|
||||
public readonly string Domain;
|
||||
|
||||
private LockFileInfo(string strID, string strTime, string strUserName,
|
||||
string strMachine, string strDomain)
|
||||
{
|
||||
this.ID = (strID ?? string.Empty).Trim();
|
||||
|
||||
DateTime dt;
|
||||
if(TimeUtil.TryDeserializeUtc(strTime.Trim(), out dt))
|
||||
this.Time = dt;
|
||||
else
|
||||
{
|
||||
Debug.Assert(false);
|
||||
this.Time = DateTime.UtcNow;
|
||||
}
|
||||
|
||||
this.UserName = (strUserName ?? string.Empty).Trim();
|
||||
this.Machine = (strMachine ?? string.Empty).Trim();
|
||||
this.Domain = (strDomain ?? string.Empty).Trim();
|
||||
|
||||
if(this.Domain.Equals(this.Machine, StrUtil.CaseIgnoreCmp))
|
||||
this.Domain = string.Empty;
|
||||
}
|
||||
|
||||
public string GetOwner()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.Append((this.UserName.Length > 0) ? this.UserName : "?");
|
||||
|
||||
bool bMachine = (this.Machine.Length > 0);
|
||||
bool bDomain = (this.Domain.Length > 0);
|
||||
if(bMachine || bDomain)
|
||||
{
|
||||
sb.Append(" (");
|
||||
sb.Append(this.Machine);
|
||||
if(bMachine && bDomain) sb.Append(" @ ");
|
||||
sb.Append(this.Domain);
|
||||
sb.Append(")");
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public static LockFileInfo Load(IOConnectionInfo iocLockFile)
|
||||
{
|
||||
Debug.Assert(false, "not yet implemented");
|
||||
return null;
|
||||
#if TODO
|
||||
Stream s = null;
|
||||
try
|
||||
{
|
||||
s = IOConnection.OpenRead(iocLockFile);
|
||||
if(s == null) return null;
|
||||
StreamReader sr = new StreamReader(s, StrUtil.Utf8);
|
||||
string str = sr.ReadToEnd();
|
||||
sr.Close();
|
||||
if(str == null) { Debug.Assert(false); return null; }
|
||||
|
||||
str = StrUtil.NormalizeNewLines(str, false);
|
||||
string[] v = str.Split('\n');
|
||||
if((v == null) || (v.Length < 6)) { Debug.Assert(false); return null; }
|
||||
|
||||
if(!v[0].StartsWith(LockFileHeader)) { Debug.Assert(false); return null; }
|
||||
return new LockFileInfo(v[1], v[2], v[3], v[4], v[5]);
|
||||
}
|
||||
catch(FileNotFoundException) { }
|
||||
catch(Exception) { Debug.Assert(false); }
|
||||
finally { if(s != null) s.Close(); }
|
||||
|
||||
return null;
|
||||
#endif
|
||||
}
|
||||
|
||||
// Throws on error
|
||||
public static LockFileInfo Create(IOConnectionInfo iocLockFile)
|
||||
{
|
||||
Debug.Assert(false, "not yet implemented");
|
||||
return null;
|
||||
#if TODO
|
||||
LockFileInfo lfi;
|
||||
Stream s = null;
|
||||
try
|
||||
{
|
||||
byte[] pbID = CryptoRandom.Instance.GetRandomBytes(16);
|
||||
string strTime = TimeUtil.SerializeUtc(DateTime.Now);
|
||||
|
||||
#if !KeePassLibSD && TODO
|
||||
lfi = new LockFileInfo(Convert.ToBase64String(pbID), strTime,
|
||||
Environment.UserName, Environment.MachineName,
|
||||
Environment.UserDomainName);
|
||||
#else
|
||||
lfi = new LockFileInfo(Convert.ToBase64String(pbID), strTime,
|
||||
string.Empty, string.Empty, string.Empty);
|
||||
#endif
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
#if !KeePassLibSD && TODO
|
||||
sb.AppendLine(LockFileHeader);
|
||||
sb.AppendLine(lfi.ID);
|
||||
sb.AppendLine(strTime);
|
||||
sb.AppendLine(lfi.UserName);
|
||||
sb.AppendLine(lfi.Machine);
|
||||
sb.AppendLine(lfi.Domain);
|
||||
#else
|
||||
sb.Append(LockFileHeader + MessageService.NewLine);
|
||||
sb.Append(lfi.ID + MessageService.NewLine);
|
||||
sb.Append(strTime + MessageService.NewLine);
|
||||
sb.Append(lfi.UserName + MessageService.NewLine);
|
||||
sb.Append(lfi.Machine + MessageService.NewLine);
|
||||
sb.Append(lfi.Domain + MessageService.NewLine);
|
||||
#endif
|
||||
|
||||
byte[] pbFile = StrUtil.Utf8.GetBytes(sb.ToString());
|
||||
|
||||
s = IOConnection.OpenWrite(iocLockFile);
|
||||
if(s == null) throw new IOException(iocLockFile.GetDisplayName());
|
||||
s.Write(pbFile, 0, pbFile.Length);
|
||||
}
|
||||
finally { if(s != null) s.Close(); }
|
||||
|
||||
return lfi;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
public FileLock(IOConnectionInfo iocBaseFile)
|
||||
{
|
||||
Debug.Assert(false, "not yet implemented");
|
||||
return ;
|
||||
#if TODO
|
||||
if(iocBaseFile == null) throw new ArgumentNullException("strBaseFile");
|
||||
|
||||
m_iocLockFile = iocBaseFile.CloneDeep();
|
||||
m_iocLockFile.Path += LockFileExt;
|
||||
|
||||
LockFileInfo lfiEx = LockFileInfo.Load(m_iocLockFile);
|
||||
if(lfiEx != null)
|
||||
{
|
||||
m_iocLockFile = null; // Otherwise Dispose deletes the existing one
|
||||
throw new FileLockException(iocBaseFile.GetDisplayName(),
|
||||
lfiEx.GetOwner());
|
||||
}
|
||||
|
||||
LockFileInfo.Create(m_iocLockFile);
|
||||
#endif
|
||||
}
|
||||
|
||||
~FileLock()
|
||||
{
|
||||
Dispose(false);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
private void Dispose(bool bDisposing)
|
||||
{
|
||||
Debug.Assert(false, "not yet implemented");
|
||||
return ;
|
||||
#if TODO
|
||||
if(m_iocLockFile == null) return;
|
||||
|
||||
bool bFileDeleted = false;
|
||||
for(int r = 0; r < 5; ++r)
|
||||
{
|
||||
// if(!OwnLockFile()) { bFileDeleted = true; break; }
|
||||
|
||||
try
|
||||
{
|
||||
IOConnection.DeleteFile(m_iocLockFile);
|
||||
bFileDeleted = true;
|
||||
}
|
||||
catch(Exception) { Debug.Assert(false); }
|
||||
|
||||
if(bFileDeleted) break;
|
||||
|
||||
if(bDisposing) Thread.Sleep(50);
|
||||
}
|
||||
|
||||
if(bDisposing && !bFileDeleted)
|
||||
IOConnection.DeleteFile(m_iocLockFile); // Possibly with exception
|
||||
|
||||
m_iocLockFile = null;
|
||||
#endif
|
||||
}
|
||||
|
||||
// private bool OwnLockFile()
|
||||
// {
|
||||
// if(m_iocLockFile == null) { Debug.Assert(false); return false; }
|
||||
// if(m_strLockID == null) { Debug.Assert(false); return false; }
|
||||
// LockFileInfo lfi = LockFileInfo.Load(m_iocLockFile);
|
||||
// if(lfi == null) return false;
|
||||
// return m_strLockID.Equals(lfi.ID);
|
||||
// }
|
||||
}
|
||||
}
|
148
ModernKeePassLib/Serialization/FileTransactionEx.cs
Normal file
148
ModernKeePassLib/Serialization/FileTransactionEx.cs
Normal file
@@ -0,0 +1,148 @@
|
||||
/*
|
||||
KeePass Password Safe - The Open-Source Password Manager
|
||||
Copyright (C) 2003-2012 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.Text;
|
||||
using System.IO;
|
||||
using System.Diagnostics;
|
||||
|
||||
// Bert TODO: For now, remove the accesscontrol from this class.
|
||||
// In WinRT, the security of file has changed, and something could potentially be done
|
||||
// http://msdn.microsoft.com/en-us/library/windows/desktop/aa364399%28v=vs.85%29.aspx
|
||||
|
||||
#if !KeePassLibSD && TODO
|
||||
using System.Security.AccessControl;
|
||||
#endif
|
||||
|
||||
using ModernKeePassLib.Utility;
|
||||
|
||||
namespace ModernKeePassLib.Serialization
|
||||
{
|
||||
public sealed class FileTransactionEx
|
||||
{
|
||||
private bool m_bTransacted;
|
||||
private IOConnectionInfo m_iocBase;
|
||||
private IOConnectionInfo m_iocTemp;
|
||||
|
||||
private bool m_bMadeUnhidden = false;
|
||||
|
||||
private const string StrTempSuffix = ".tmp";
|
||||
|
||||
public FileTransactionEx(IOConnectionInfo iocBaseFile)
|
||||
{
|
||||
Initialize(iocBaseFile, true);
|
||||
}
|
||||
|
||||
public FileTransactionEx(IOConnectionInfo iocBaseFile, bool bTransacted)
|
||||
{
|
||||
Initialize(iocBaseFile, bTransacted);
|
||||
}
|
||||
|
||||
private void Initialize(IOConnectionInfo iocBaseFile, bool bTransacted)
|
||||
{
|
||||
if(iocBaseFile == null) throw new ArgumentNullException("iocBaseFile");
|
||||
|
||||
m_bTransacted = bTransacted;
|
||||
m_iocBase = iocBaseFile.CloneDeep();
|
||||
|
||||
if(m_bTransacted)
|
||||
{
|
||||
m_iocTemp = m_iocBase.CloneDeep();
|
||||
m_iocTemp.Path += StrTempSuffix;
|
||||
}
|
||||
else m_iocTemp = m_iocBase;
|
||||
}
|
||||
|
||||
public Stream OpenWrite()
|
||||
{
|
||||
if(!m_bTransacted) m_bMadeUnhidden = UrlUtil.UnhideFile(m_iocTemp.Path);
|
||||
else // m_bTransacted
|
||||
{
|
||||
try { IOConnection.DeleteFile(m_iocTemp); }
|
||||
catch(Exception) { }
|
||||
}
|
||||
|
||||
return IOConnection.OpenWrite(m_iocTemp);
|
||||
}
|
||||
|
||||
public void CommitWrite()
|
||||
{
|
||||
if(m_bTransacted) CommitWriteTransaction();
|
||||
else // !m_bTransacted
|
||||
{
|
||||
if(m_bMadeUnhidden) UrlUtil.HideFile(m_iocTemp.Path, true); // Hide again
|
||||
}
|
||||
}
|
||||
|
||||
private void CommitWriteTransaction()
|
||||
{
|
||||
bool bMadeUnhidden = UrlUtil.UnhideFile(m_iocBase.Path);
|
||||
|
||||
#if !KeePassLibSD && TODO
|
||||
FileSecurity bkSecurity = null;
|
||||
bool bEfsEncrypted = false;
|
||||
#endif
|
||||
|
||||
if(IOConnection.FileExists(m_iocBase))
|
||||
{
|
||||
#if !KeePassLibSD && TODO
|
||||
if(m_iocBase.IsLocalFile())
|
||||
{
|
||||
try
|
||||
{
|
||||
FileAttributes faBase = File.GetAttributes(m_iocBase.Path);
|
||||
bEfsEncrypted = ((long)(faBase & FileAttributes.Encrypted) != 0);
|
||||
|
||||
DateTime tCreation = File.GetCreationTime(m_iocBase.Path);
|
||||
bkSecurity = File.GetAccessControl(m_iocBase.Path);
|
||||
|
||||
File.SetCreationTime(m_iocTemp.Path, tCreation);
|
||||
}
|
||||
catch(Exception) { Debug.Assert(false); }
|
||||
}
|
||||
#endif
|
||||
|
||||
IOConnection.DeleteFile(m_iocBase);
|
||||
}
|
||||
|
||||
IOConnection.RenameFile(m_iocTemp, m_iocBase);
|
||||
|
||||
#if !KeePassLibSD && TODO
|
||||
if(m_iocBase.IsLocalFile())
|
||||
{
|
||||
try
|
||||
{
|
||||
if(bEfsEncrypted)
|
||||
{
|
||||
try { File.Encrypt(m_iocBase.Path); }
|
||||
catch(Exception) { Debug.Assert(false); }
|
||||
}
|
||||
|
||||
if(bkSecurity != null)
|
||||
File.SetAccessControl(m_iocBase.Path, bkSecurity);
|
||||
}
|
||||
catch(Exception) { Debug.Assert(false); }
|
||||
}
|
||||
#endif
|
||||
|
||||
if(bMadeUnhidden) UrlUtil.HideFile(m_iocBase.Path, true); // Hide again
|
||||
}
|
||||
}
|
||||
}
|
321
ModernKeePassLib/Serialization/HashedBlockStream.cs
Normal file
321
ModernKeePassLib/Serialization/HashedBlockStream.cs
Normal file
@@ -0,0 +1,321 @@
|
||||
/*
|
||||
KeePass Password Safe - The Open-Source Password Manager
|
||||
Copyright (C) 2003-2012 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.IO;
|
||||
using System.Text;
|
||||
|
||||
using ModernKeePassLib.Native;
|
||||
using ModernKeePassLib.Utility;
|
||||
using ModernKeePassLib.Cryptography;
|
||||
|
||||
#if KeePassLibSD
|
||||
using ModernKeePassLibSD;
|
||||
#endif
|
||||
|
||||
namespace ModernKeePassLib.Serialization
|
||||
{
|
||||
public sealed class HashedBlockStream : Stream
|
||||
{
|
||||
private const int m_nDefaultBufferSize = 1024 * 1024; // 1 MB
|
||||
|
||||
private Stream m_sBaseStream;
|
||||
private bool m_bWriting;
|
||||
private bool m_bVerify;
|
||||
private bool m_bEos = false;
|
||||
|
||||
private BinaryReader m_brInput;
|
||||
private BinaryWriter m_bwOutput;
|
||||
|
||||
private byte[] m_pbBuffer;
|
||||
private int m_nBufferPos = 0;
|
||||
|
||||
private uint m_uBufferIndex = 0;
|
||||
|
||||
public override bool CanRead
|
||||
{
|
||||
get { return !m_bWriting; }
|
||||
}
|
||||
|
||||
public override bool CanSeek
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
public override bool CanWrite
|
||||
{
|
||||
get { return m_bWriting; }
|
||||
}
|
||||
|
||||
public override long Length
|
||||
{
|
||||
get { throw new NotSupportedException(); }
|
||||
}
|
||||
|
||||
public override long Position
|
||||
{
|
||||
get { throw new NotSupportedException(); }
|
||||
set { throw new NotSupportedException(); }
|
||||
}
|
||||
|
||||
public HashedBlockStream(Stream sBaseStream, bool bWriting)
|
||||
{
|
||||
Initialize(sBaseStream, bWriting, 0, true);
|
||||
}
|
||||
|
||||
public HashedBlockStream(Stream sBaseStream, bool bWriting, int nBufferSize)
|
||||
{
|
||||
Initialize(sBaseStream, bWriting, nBufferSize, true);
|
||||
}
|
||||
|
||||
public HashedBlockStream(Stream sBaseStream, bool bWriting, int nBufferSize,
|
||||
bool bVerify)
|
||||
{
|
||||
Initialize(sBaseStream, bWriting, nBufferSize, bVerify);
|
||||
}
|
||||
|
||||
private void Initialize(Stream sBaseStream, bool bWriting, int nBufferSize,
|
||||
bool bVerify)
|
||||
{
|
||||
if(sBaseStream == null) throw new ArgumentNullException("sBaseStream");
|
||||
if(nBufferSize < 0) throw new ArgumentOutOfRangeException("nBufferSize");
|
||||
|
||||
if(nBufferSize == 0) nBufferSize = m_nDefaultBufferSize;
|
||||
|
||||
m_sBaseStream = sBaseStream;
|
||||
m_bWriting = bWriting;
|
||||
m_bVerify = bVerify;
|
||||
|
||||
UTF8Encoding utf8 = StrUtil.Utf8;
|
||||
if(m_bWriting == false) // Reading mode
|
||||
{
|
||||
if(m_sBaseStream.CanRead == false)
|
||||
throw new InvalidOperationException();
|
||||
|
||||
m_brInput = new BinaryReader(sBaseStream, utf8);
|
||||
|
||||
m_pbBuffer = new byte[0];
|
||||
}
|
||||
else // Writing mode
|
||||
{
|
||||
if(m_sBaseStream.CanWrite == false)
|
||||
throw new InvalidOperationException();
|
||||
|
||||
m_bwOutput = new BinaryWriter(sBaseStream, utf8);
|
||||
|
||||
m_pbBuffer = new byte[nBufferSize];
|
||||
}
|
||||
}
|
||||
|
||||
public override void Flush()
|
||||
{
|
||||
if(m_bWriting) m_bwOutput.Flush();
|
||||
}
|
||||
|
||||
#if TODO
|
||||
public override void Close()
|
||||
{
|
||||
if(m_sBaseStream != null)
|
||||
{
|
||||
if(m_bWriting == false) // Reading mode
|
||||
{
|
||||
m_brInput.Close();
|
||||
m_brInput = null;
|
||||
}
|
||||
else // Writing mode
|
||||
{
|
||||
if(m_nBufferPos == 0) // No data left in buffer
|
||||
WriteHashedBlock(); // Write terminating block
|
||||
else
|
||||
{
|
||||
WriteHashedBlock(); // Write remaining buffered data
|
||||
WriteHashedBlock(); // Write terminating block
|
||||
}
|
||||
|
||||
Flush();
|
||||
m_bwOutput.Close();
|
||||
m_bwOutput = null;
|
||||
}
|
||||
|
||||
m_sBaseStream.Close();
|
||||
m_sBaseStream = null;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
public override long Seek(long lOffset, SeekOrigin soOrigin)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public override void SetLength(long lValue)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public override int Read(byte[] pbBuffer, int nOffset, int nCount)
|
||||
{
|
||||
if(m_bWriting) throw new InvalidOperationException();
|
||||
|
||||
int nRemaining = nCount;
|
||||
while(nRemaining > 0)
|
||||
{
|
||||
if(m_nBufferPos == m_pbBuffer.Length)
|
||||
{
|
||||
if(ReadHashedBlock() == false)
|
||||
return nCount - nRemaining; // Bytes actually read
|
||||
}
|
||||
|
||||
int nCopy = Math.Min(m_pbBuffer.Length - m_nBufferPos, nRemaining);
|
||||
|
||||
Array.Copy(m_pbBuffer, m_nBufferPos, pbBuffer, nOffset, nCopy);
|
||||
|
||||
nOffset += nCopy;
|
||||
m_nBufferPos += nCopy;
|
||||
|
||||
nRemaining -= nCopy;
|
||||
}
|
||||
|
||||
return nCount;
|
||||
}
|
||||
|
||||
private bool ReadHashedBlock()
|
||||
{
|
||||
if(m_bEos) return false; // End of stream reached already
|
||||
|
||||
m_nBufferPos = 0;
|
||||
|
||||
if(m_brInput.ReadUInt32() != m_uBufferIndex)
|
||||
throw new InvalidDataException();
|
||||
++m_uBufferIndex;
|
||||
|
||||
byte[] pbStoredHash = m_brInput.ReadBytes(32);
|
||||
if((pbStoredHash == null) || (pbStoredHash.Length != 32))
|
||||
throw new InvalidDataException();
|
||||
|
||||
int nBufferSize = 0;
|
||||
try { nBufferSize = m_brInput.ReadInt32(); }
|
||||
catch(NullReferenceException) // Mono bug workaround (LaunchPad 783268)
|
||||
{
|
||||
if(!NativeLib.IsUnix()) throw;
|
||||
}
|
||||
|
||||
if(nBufferSize < 0)
|
||||
throw new InvalidDataException();
|
||||
|
||||
if(nBufferSize == 0)
|
||||
{
|
||||
for(int iHash = 0; iHash < 32; ++iHash)
|
||||
{
|
||||
if(pbStoredHash[iHash] != 0)
|
||||
throw new InvalidDataException();
|
||||
}
|
||||
|
||||
m_bEos = true;
|
||||
m_pbBuffer = new byte[0];
|
||||
return false;
|
||||
}
|
||||
|
||||
m_pbBuffer = m_brInput.ReadBytes(nBufferSize);
|
||||
if((m_pbBuffer == null) || ((m_pbBuffer.Length != nBufferSize) && m_bVerify))
|
||||
throw new InvalidDataException();
|
||||
|
||||
if(m_bVerify)
|
||||
{
|
||||
byte[] pbComputedHash = SHA256Managed.Instance.ComputeHash(m_pbBuffer);
|
||||
if((pbComputedHash == null) || (pbComputedHash.Length != 32))
|
||||
throw new InvalidOperationException();
|
||||
|
||||
for(int iHashPos = 0; iHashPos < 32; ++iHashPos)
|
||||
{
|
||||
if(pbStoredHash[iHashPos] != pbComputedHash[iHashPos])
|
||||
throw new InvalidDataException();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override void Write(byte[] pbBuffer, int nOffset, int nCount)
|
||||
{
|
||||
if(!m_bWriting) throw new InvalidOperationException();
|
||||
|
||||
while(nCount > 0)
|
||||
{
|
||||
if(m_nBufferPos == m_pbBuffer.Length)
|
||||
WriteHashedBlock();
|
||||
|
||||
int nCopy = Math.Min(m_pbBuffer.Length - m_nBufferPos, nCount);
|
||||
|
||||
Array.Copy(pbBuffer, nOffset, m_pbBuffer, m_nBufferPos, nCopy);
|
||||
|
||||
nOffset += nCopy;
|
||||
m_nBufferPos += nCopy;
|
||||
|
||||
nCount -= nCopy;
|
||||
}
|
||||
}
|
||||
|
||||
private void WriteHashedBlock()
|
||||
{
|
||||
Debug.Assert(false, "not yet implemented");
|
||||
return ;
|
||||
#if TODO
|
||||
m_bwOutput.Write(m_uBufferIndex);
|
||||
++m_uBufferIndex;
|
||||
|
||||
if(m_nBufferPos > 0)
|
||||
{
|
||||
SHA256Managed sha256 = new SHA256Managed();
|
||||
|
||||
#if !KeePassLibSD
|
||||
byte[] pbHash = sha256.ComputeHash(m_pbBuffer, 0, m_nBufferPos);
|
||||
#else
|
||||
byte[] pbHash;
|
||||
if(m_nBufferPos == m_pbBuffer.Length)
|
||||
pbHash = sha256.ComputeHash(m_pbBuffer);
|
||||
else
|
||||
{
|
||||
byte[] pbData = new byte[m_nBufferPos];
|
||||
Array.Copy(m_pbBuffer, 0, pbData, 0, m_nBufferPos);
|
||||
pbHash = sha256.ComputeHash(pbData);
|
||||
}
|
||||
#endif
|
||||
|
||||
m_bwOutput.Write(pbHash);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_bwOutput.Write((ulong)0); // Zero hash
|
||||
m_bwOutput.Write((ulong)0);
|
||||
m_bwOutput.Write((ulong)0);
|
||||
m_bwOutput.Write((ulong)0);
|
||||
}
|
||||
|
||||
m_bwOutput.Write(m_nBufferPos);
|
||||
|
||||
if(m_nBufferPos > 0)
|
||||
m_bwOutput.Write(m_pbBuffer, 0, m_nBufferPos);
|
||||
|
||||
m_nBufferPos = 0;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
468
ModernKeePassLib/Serialization/IOConnection.cs
Normal file
468
ModernKeePassLib/Serialization/IOConnection.cs
Normal file
@@ -0,0 +1,468 @@
|
||||
/*
|
||||
KeePass Password Safe - The Open-Source Password Manager
|
||||
Copyright (C) 2003-2012 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.Net;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using System.IO;
|
||||
|
||||
using Windows.Storage.Streams;
|
||||
using Windows.Storage;
|
||||
// BERT TODO: For the time being, the web functionality is not available for WinRT
|
||||
|
||||
|
||||
#if !KeePassLibSD && TODO
|
||||
using System.Net.Cache;
|
||||
using System.Net.Security;
|
||||
#endif
|
||||
|
||||
using ModernKeePassLib.Utility;
|
||||
|
||||
namespace ModernKeePassLib.Serialization
|
||||
{
|
||||
#if !KeePassLibSD && false
|
||||
public sealed class IOWebClient : HttpClient
|
||||
{
|
||||
protected override WebRequest GetWebRequest(Uri address)
|
||||
{
|
||||
WebRequest request = base.GetWebRequest(address);
|
||||
IOConnection.ConfigureWebRequest(request);
|
||||
return request;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
public class IOConnection
|
||||
{
|
||||
#if !KeePassLibSD && TODO
|
||||
private static ProxyServerType m_pstProxyType = ProxyServerType.System;
|
||||
private static string m_strProxyAddr = string.Empty;
|
||||
private static string m_strProxyPort = string.Empty;
|
||||
private static string m_strProxyUserName = string.Empty;
|
||||
private static string m_strProxyPassword = string.Empty;
|
||||
#endif
|
||||
|
||||
// Web request methods
|
||||
public const string WrmDeleteFile = "DELETEFILE";
|
||||
public const string WrmMoveFile = "MOVEFILE";
|
||||
|
||||
// Web request headers
|
||||
public const string WrhMoveFileTo = "MoveFileTo";
|
||||
|
||||
#if !KeePassLibSD && TODO
|
||||
// Allow self-signed certificates, expired certificates, etc.
|
||||
private static bool ValidateServerCertificate(object sender,
|
||||
X509Certificate certificate, X509Chain chain,
|
||||
SslPolicyErrors sslPolicyErrors)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public static void SetProxy(ProxyServerType pst, string strAddr,
|
||||
string strPort, string strUserName, string strPassword)
|
||||
{
|
||||
m_pstProxyType = pst;
|
||||
m_strProxyAddr = (strAddr ?? string.Empty);
|
||||
m_strProxyPort = (strPort ?? string.Empty);
|
||||
m_strProxyUserName = (strUserName ?? string.Empty);
|
||||
m_strProxyPassword = (strPassword ?? string.Empty);
|
||||
}
|
||||
|
||||
internal static void ConfigureWebRequest(WebRequest request)
|
||||
{
|
||||
if(request == null) { Debug.Assert(false); return; } // No throw
|
||||
|
||||
// WebDAV support
|
||||
if(request is HttpWebRequest)
|
||||
{
|
||||
request.PreAuthenticate = true; // Also auth GET
|
||||
if(request.Method == WebRequestMethods.Http.Post)
|
||||
request.Method = WebRequestMethods.Http.Put;
|
||||
}
|
||||
// else if(request is FtpWebRequest)
|
||||
// {
|
||||
// Debug.Assert(((FtpWebRequest)request).UsePassive);
|
||||
// }
|
||||
|
||||
// Not implemented and ignored in Mono < 2.10
|
||||
try
|
||||
{
|
||||
request.CachePolicy = new RequestCachePolicy(RequestCacheLevel.NoCacheNoStore);
|
||||
}
|
||||
catch(NotImplementedException) { }
|
||||
catch(Exception) { Debug.Assert(false); }
|
||||
|
||||
try
|
||||
{
|
||||
IWebProxy prx;
|
||||
if(GetWebProxy(out prx)) request.Proxy = prx;
|
||||
}
|
||||
catch(Exception) { Debug.Assert(false); }
|
||||
}
|
||||
|
||||
internal static void ConfigureWebClient(WebClient wc)
|
||||
{
|
||||
// Not implemented and ignored in Mono < 2.10
|
||||
try
|
||||
{
|
||||
wc.CachePolicy = new RequestCachePolicy(RequestCacheLevel.NoCacheNoStore);
|
||||
}
|
||||
catch(NotImplementedException) { }
|
||||
catch(Exception) { Debug.Assert(false); }
|
||||
|
||||
try
|
||||
{
|
||||
IWebProxy prx;
|
||||
if(GetWebProxy(out prx)) wc.Proxy = prx;
|
||||
}
|
||||
catch(Exception) { Debug.Assert(false); }
|
||||
}
|
||||
|
||||
private static bool GetWebProxy(out IWebProxy prx)
|
||||
{
|
||||
prx = null;
|
||||
|
||||
if(m_pstProxyType == ProxyServerType.None)
|
||||
return true; // Use null proxy
|
||||
if(m_pstProxyType == ProxyServerType.Manual)
|
||||
{
|
||||
try
|
||||
{
|
||||
if(m_strProxyPort.Length > 0)
|
||||
prx = new WebProxy(m_strProxyAddr, int.Parse(m_strProxyPort));
|
||||
else prx = new WebProxy(m_strProxyAddr);
|
||||
|
||||
if((m_strProxyUserName.Length > 0) || (m_strProxyPassword.Length > 0))
|
||||
prx.Credentials = new NetworkCredential(m_strProxyUserName,
|
||||
m_strProxyPassword);
|
||||
|
||||
return true; // Use manual proxy
|
||||
}
|
||||
catch(Exception exProxy)
|
||||
{
|
||||
string strInfo = m_strProxyAddr;
|
||||
if(m_strProxyPort.Length > 0) strInfo += ":" + m_strProxyPort;
|
||||
MessageService.ShowWarning(strInfo, exProxy.Message);
|
||||
}
|
||||
|
||||
return false; // Use default
|
||||
}
|
||||
|
||||
if((m_strProxyUserName.Length == 0) && (m_strProxyPassword.Length == 0))
|
||||
return false; // Use default proxy, no auth
|
||||
|
||||
try
|
||||
{
|
||||
prx = WebRequest.DefaultWebProxy;
|
||||
if(prx == null) prx = WebRequest.GetSystemWebProxy();
|
||||
if(prx == null) throw new InvalidOperationException();
|
||||
|
||||
prx.Credentials = new NetworkCredential(m_strProxyUserName,
|
||||
m_strProxyPassword);
|
||||
return true;
|
||||
}
|
||||
catch(Exception) { Debug.Assert(false); }
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static void PrepareWebAccess()
|
||||
{
|
||||
ServicePointManager.ServerCertificateValidationCallback =
|
||||
ValidateServerCertificate;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
private static WebRequest CreateWebRequest(IOConnectionInfo ioc)
|
||||
{
|
||||
PrepareWebAccess();
|
||||
|
||||
WebRequest req = WebRequest.Create(ioc.Path);
|
||||
ConfigureWebRequest(req);
|
||||
|
||||
if((ioc.UserName.Length > 0) || (ioc.Password.Length > 0))
|
||||
req.Credentials = new NetworkCredential(ioc.UserName, ioc.Password);
|
||||
else if(NativeLib.IsUnix()) // Mono requires credentials
|
||||
req.Credentials = new NetworkCredential("anonymous", string.Empty);
|
||||
|
||||
return req;
|
||||
}
|
||||
#endif
|
||||
|
||||
private async Task<Stream> OpenReadHttp(IOConnectionInfo ioc)
|
||||
{
|
||||
|
||||
// TODO: Configure the httpClient
|
||||
// PrepareWebAccess();
|
||||
// ConfigureWebClient(wc);
|
||||
|
||||
HttpClient hc = new HttpClient();
|
||||
HttpResponseMessage response = await hc.GetAsync(ioc.Path);
|
||||
response.EnsureSuccessStatusCode();
|
||||
|
||||
// Read content into buffer
|
||||
// Not the most efficient thing to do,
|
||||
// but simplifies our life by allowing to use stream.length later on.
|
||||
await response.Content.LoadIntoBufferAsync();
|
||||
return await response.Content.ReadAsStreamAsync();
|
||||
#if false
|
||||
if((ioc.UserName.Length > 0) || (ioc.Password.Length > 0))
|
||||
wc.Credentials = new NetworkCredential(ioc.UserName, ioc.Password);
|
||||
else if(NativeLib.IsUnix()) // Mono requires credentials
|
||||
wc.Credentials = new NetworkCredential("anonymous", string.Empty);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
public async Task<Stream> OpenRead(IOConnectionInfo ioc)
|
||||
{
|
||||
if(StrUtil.IsDataUri(ioc.Path))
|
||||
{
|
||||
byte[] pbData = StrUtil.DataUriToData(ioc.Path);
|
||||
if(pbData != null) return new MemoryStream(pbData, false);
|
||||
}
|
||||
|
||||
if(ioc.IsLocalFile()) return await OpenReadLocal(ioc);
|
||||
|
||||
|
||||
return await OpenReadHttp(ioc);
|
||||
}
|
||||
|
||||
|
||||
private async Task<Stream> OpenReadLocal(IOConnectionInfo ioc)
|
||||
{
|
||||
try
|
||||
{
|
||||
var sampleFile = await StorageFile.GetFileFromPathAsync(ioc.Path);
|
||||
using (IRandomAccessStream stream = await sampleFile.OpenAsync(FileAccessMode.Read))
|
||||
{
|
||||
return stream.AsStream();
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
Debug.Assert(false, "Not implemented yet");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
#if !KeePassLibSD && TODO
|
||||
public static Stream OpenWrite(IOConnectionInfo ioc)
|
||||
{
|
||||
if(ioc == null) { Debug.Assert(false); return null; }
|
||||
|
||||
if(ioc.IsLocalFile()) return OpenWriteLocal(ioc);
|
||||
|
||||
Uri uri = new Uri(ioc.Path);
|
||||
|
||||
// Mono does not set HttpWebRequest.Method to POST for writes,
|
||||
// so one needs to set the method to PUT explicitly
|
||||
if(NativeLib.IsUnix() && (uri.Scheme.Equals(Uri.UriSchemeHttp,
|
||||
StrUtil.CaseIgnoreCmp) || uri.Scheme.Equals(Uri.UriSchemeHttps,
|
||||
StrUtil.CaseIgnoreCmp)))
|
||||
return CreateWebClient(ioc).OpenWrite(uri, WebRequestMethods.Http.Put);
|
||||
|
||||
return CreateWebClient(ioc).OpenWrite(uri);
|
||||
}
|
||||
#else
|
||||
public static Stream OpenWrite(IOConnectionInfo ioc)
|
||||
{
|
||||
return OpenWriteLocal(ioc);
|
||||
}
|
||||
#endif
|
||||
|
||||
private static Stream OpenWriteLocal(IOConnectionInfo ioc)
|
||||
{
|
||||
Debug.Assert(false, "Not implemented yet");
|
||||
return null;
|
||||
// return new FileStream(ioc.Path, FileMode.Create, FileAccess.Write,
|
||||
// FileShare.None);
|
||||
}
|
||||
|
||||
public static bool FileExists(IOConnectionInfo ioc)
|
||||
{
|
||||
return FileExists(ioc, false);
|
||||
}
|
||||
|
||||
public static bool FileExists(IOConnectionInfo ioc, bool bThrowErrors)
|
||||
{
|
||||
Debug.Assert(false, "Not implemented yet");
|
||||
return false;
|
||||
#if TODO
|
||||
|
||||
if(ioc == null) { Debug.Assert(false); return false; }
|
||||
|
||||
if(ioc.IsLocalFile()) return File.Exists(ioc.Path);
|
||||
|
||||
try
|
||||
{
|
||||
Stream s = OpenRead(ioc);
|
||||
if(s == null) throw new FileNotFoundException();
|
||||
|
||||
// For FTP clients we called RETR to get the file, but we never
|
||||
// followed-up and downloaded the file; close may produce a
|
||||
// 550 error -- that's okay
|
||||
try { s.Close(); }
|
||||
catch(Exception) { }
|
||||
}
|
||||
catch(Exception)
|
||||
{
|
||||
if(bThrowErrors) throw;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
public static void DeleteFile(IOConnectionInfo ioc)
|
||||
{
|
||||
Debug.Assert(false, "Not implemented yet");
|
||||
return ;
|
||||
#if TODO
|
||||
if(ioc.IsLocalFile()) { File.Delete(ioc.Path); return; }
|
||||
|
||||
#if !KeePassLibSD && TODO
|
||||
WebRequest req = CreateWebRequest(ioc);
|
||||
if(req != null)
|
||||
{
|
||||
if(req is HttpWebRequest) req.Method = "DELETE";
|
||||
else if(req is FtpWebRequest) req.Method = WebRequestMethods.Ftp.DeleteFile;
|
||||
else if(req is FileWebRequest)
|
||||
{
|
||||
File.Delete(UrlUtil.FileUrlToPath(ioc.Path));
|
||||
return;
|
||||
}
|
||||
else req.Method = WrmDeleteFile;
|
||||
|
||||
DisposeResponse(req.GetResponse(), true);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Rename/move a file. For local file system and WebDAV, the
|
||||
/// specified file is moved, i.e. the file destination can be
|
||||
/// in a different directory/path. In contrast, for FTP the
|
||||
/// file is renamed, i.e. its destination must be in the same
|
||||
/// directory/path.
|
||||
/// </summary>
|
||||
/// <param name="iocFrom">Source file path.</param>
|
||||
/// <param name="iocTo">Target file path.</param>
|
||||
public static void RenameFile(IOConnectionInfo iocFrom, IOConnectionInfo iocTo)
|
||||
{
|
||||
return;
|
||||
#if TODO
|
||||
if(iocFrom.IsLocalFile()) { File.Move(iocFrom.Path, iocTo.Path); return; }
|
||||
|
||||
#if !KeePassLibSD && TODO
|
||||
WebRequest req = CreateWebRequest(iocFrom);
|
||||
if(req != null)
|
||||
{
|
||||
if(req is HttpWebRequest)
|
||||
{
|
||||
req.Method = "MOVE";
|
||||
req.Headers.Set("Destination", iocTo.Path); // Full URL supported
|
||||
}
|
||||
else if(req is FtpWebRequest)
|
||||
{
|
||||
req.Method = WebRequestMethods.Ftp.Rename;
|
||||
((FtpWebRequest)req).RenameTo = UrlUtil.GetFileName(iocTo.Path);
|
||||
}
|
||||
else if(req is FileWebRequest)
|
||||
{
|
||||
File.Move(UrlUtil.FileUrlToPath(iocFrom.Path),
|
||||
UrlUtil.FileUrlToPath(iocTo.Path));
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
req.Method = WrmMoveFile;
|
||||
req.Headers.Set(WrhMoveFileTo, iocTo.Path);
|
||||
}
|
||||
|
||||
DisposeResponse(req.GetResponse(), true);
|
||||
}
|
||||
#endif
|
||||
|
||||
// using(Stream sIn = IOConnection.OpenRead(iocFrom))
|
||||
// {
|
||||
// using(Stream sOut = IOConnection.OpenWrite(iocTo))
|
||||
// {
|
||||
// MemUtil.CopyStream(sIn, sOut);
|
||||
// sOut.Close();
|
||||
// }
|
||||
//
|
||||
// sIn.Close();
|
||||
// }
|
||||
// DeleteFile(iocFrom);
|
||||
#endif
|
||||
}
|
||||
|
||||
private static void DisposeResponse(WebResponse wr, bool bGetStream)
|
||||
{
|
||||
Debug.Assert(false, "Not implemented yet");
|
||||
return;
|
||||
#if TODO
|
||||
if(wr == null) return;
|
||||
|
||||
try
|
||||
{
|
||||
if(bGetStream)
|
||||
{
|
||||
Stream s = wr.GetResponseStream();
|
||||
if(s != null) s.Close();
|
||||
}
|
||||
}
|
||||
catch(Exception) { Debug.Assert(false); }
|
||||
|
||||
try { wr.Close(); }
|
||||
catch(Exception) { Debug.Assert(false); }
|
||||
}
|
||||
|
||||
public static byte[] ReadFile(IOConnectionInfo ioc)
|
||||
{
|
||||
Stream sIn = null;
|
||||
MemoryStream ms = null;
|
||||
try
|
||||
{
|
||||
sIn = IOConnection.OpenRead(ioc);
|
||||
if(sIn == null) return null;
|
||||
|
||||
ms = new MemoryStream();
|
||||
MemUtil.CopyStream(sIn, ms);
|
||||
|
||||
return ms.ToArray();
|
||||
}
|
||||
catch(Exception) { }
|
||||
finally
|
||||
{
|
||||
if(sIn != null) sIn.Close();
|
||||
if(ms != null) ms.Close();
|
||||
}
|
||||
|
||||
return null;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
355
ModernKeePassLib/Serialization/IOConnectionInfo.cs
Normal file
355
ModernKeePassLib/Serialization/IOConnectionInfo.cs
Normal file
@@ -0,0 +1,355 @@
|
||||
/*
|
||||
KeePass Password Safe - The Open-Source Password Manager
|
||||
Copyright (C) 2003-2012 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.Text;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
|
||||
using ModernKeePassLib.Interfaces;
|
||||
using ModernKeePassLib.Utility;
|
||||
|
||||
namespace ModernKeePassLib.Serialization
|
||||
{
|
||||
public enum IOCredSaveMode
|
||||
{
|
||||
/// <summary>
|
||||
/// Do not remember user name or password.
|
||||
/// </summary>
|
||||
NoSave = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Remember the user name only, not the password.
|
||||
/// </summary>
|
||||
UserNameOnly,
|
||||
|
||||
/// <summary>
|
||||
/// Save both user name and password.
|
||||
/// </summary>
|
||||
SaveCred
|
||||
}
|
||||
|
||||
public enum IOCredProtMode
|
||||
{
|
||||
None = 0,
|
||||
Obf
|
||||
}
|
||||
|
||||
/* public enum IOFileFormatHint
|
||||
{
|
||||
None = 0,
|
||||
Deprecated
|
||||
} */
|
||||
|
||||
public sealed class IOConnectionInfo : IDeepCloneable<IOConnectionInfo>
|
||||
{
|
||||
// private IOFileFormatHint m_ioHint = IOFileFormatHint.None;
|
||||
|
||||
private string m_strUrl = string.Empty;
|
||||
public string Path
|
||||
{
|
||||
get { return m_strUrl; }
|
||||
set
|
||||
{
|
||||
Debug.Assert(value != null);
|
||||
if(value == null) throw new ArgumentNullException("value");
|
||||
|
||||
m_strUrl = value;
|
||||
}
|
||||
}
|
||||
|
||||
private string m_strUser = string.Empty;
|
||||
[DefaultValue("")]
|
||||
public string UserName
|
||||
{
|
||||
get { return m_strUser; }
|
||||
set
|
||||
{
|
||||
Debug.Assert(value != null);
|
||||
if(value == null) throw new ArgumentNullException("value");
|
||||
|
||||
m_strUser = value;
|
||||
}
|
||||
}
|
||||
|
||||
private string m_strPassword = string.Empty;
|
||||
[DefaultValue("")]
|
||||
public string Password
|
||||
{
|
||||
get { return m_strPassword; }
|
||||
set
|
||||
{
|
||||
Debug.Assert(value != null);
|
||||
if(value == null) throw new ArgumentNullException("value");
|
||||
|
||||
m_strPassword = value;
|
||||
}
|
||||
}
|
||||
|
||||
private IOCredProtMode m_ioCredProtMode = IOCredProtMode.None;
|
||||
public IOCredProtMode CredProtMode
|
||||
{
|
||||
get { return m_ioCredProtMode; }
|
||||
set { m_ioCredProtMode = value; }
|
||||
}
|
||||
|
||||
private IOCredSaveMode m_ioCredSaveMode = IOCredSaveMode.NoSave;
|
||||
public IOCredSaveMode CredSaveMode
|
||||
{
|
||||
get { return m_ioCredSaveMode; }
|
||||
set { m_ioCredSaveMode = value; }
|
||||
}
|
||||
|
||||
/* public IOFileFormatHint FileFormatHint
|
||||
{
|
||||
get { return m_ioHint; }
|
||||
set { m_ioHint = value; }
|
||||
} */
|
||||
|
||||
public IOConnectionInfo CloneDeep()
|
||||
{
|
||||
return (IOConnectionInfo)this.MemberwiseClone();
|
||||
}
|
||||
|
||||
/*
|
||||
/// <summary>
|
||||
/// Serialize the current connection info to a string. Credentials
|
||||
/// are only serialized if the <c>SaveCredentials</c> property
|
||||
/// is <c>true</c>.
|
||||
/// </summary>
|
||||
/// <param name="iocToCompile">Input object to be serialized.</param>
|
||||
/// <returns>Serialized object as string.</returns>
|
||||
public static string SerializeToString(IOConnectionInfo iocToCompile)
|
||||
{
|
||||
Debug.Assert(iocToCompile != null);
|
||||
if(iocToCompile == null) throw new ArgumentNullException("iocToCompile");
|
||||
|
||||
string strUrl = iocToCompile.Path;
|
||||
string strUser = TransformUnreadable(iocToCompile.UserName, true);
|
||||
string strPassword = TransformUnreadable(iocToCompile.Password, true);
|
||||
string strAll = strUrl + strUser + strPassword;
|
||||
char chSep = char.MinValue;
|
||||
|
||||
char[] vPrefSeps = new char[]{ '@', '#', '!', '$', '*' };
|
||||
foreach(char ch in vPrefSeps)
|
||||
{
|
||||
if(strAll.IndexOf(ch) < 0)
|
||||
{
|
||||
chSep = ch;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(chSep == char.MinValue)
|
||||
{
|
||||
for(char chEnum = '!'; chEnum < char.MaxValue; ++chEnum)
|
||||
{
|
||||
if(strAll.IndexOf(chEnum) < 0)
|
||||
{
|
||||
chSep = chEnum;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(chSep == char.MinValue) throw new FormatException();
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.Append(chSep);
|
||||
sb.Append(strUrl);
|
||||
sb.Append(chSep);
|
||||
|
||||
if(iocToCompile.CredSaveMode == IOCredSaveMode.SaveCred)
|
||||
{
|
||||
sb.Append('C');
|
||||
sb.Append(chSep);
|
||||
sb.Append(strUser);
|
||||
sb.Append(chSep);
|
||||
sb.Append(strPassword);
|
||||
}
|
||||
else if(iocToCompile.CredSaveMode == IOCredSaveMode.UserNameOnly)
|
||||
{
|
||||
sb.Append('U');
|
||||
sb.Append(chSep);
|
||||
sb.Append(strUser);
|
||||
sb.Append(chSep);
|
||||
}
|
||||
else // Don't remember credentials
|
||||
{
|
||||
sb.Append('N');
|
||||
sb.Append(chSep);
|
||||
sb.Append(chSep);
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public static IOConnectionInfo UnserializeFromString(string strToDecompile)
|
||||
{
|
||||
Debug.Assert(strToDecompile != null);
|
||||
if(strToDecompile == null) throw new ArgumentNullException("strToDecompile");
|
||||
if(strToDecompile.Length <= 1) throw new ArgumentException();
|
||||
|
||||
char chSep = strToDecompile[0];
|
||||
string[] vParts = strToDecompile.Substring(1, strToDecompile.Length -
|
||||
1).Split(new char[]{ chSep });
|
||||
if(vParts.Length < 4) throw new ArgumentException();
|
||||
|
||||
IOConnectionInfo s = new IOConnectionInfo();
|
||||
s.Path = vParts[0];
|
||||
|
||||
if(vParts[1] == "C")
|
||||
s.CredSaveMode = IOCredSaveMode.SaveCred;
|
||||
else if(vParts[1] == "U")
|
||||
s.CredSaveMode = IOCredSaveMode.UserNameOnly;
|
||||
else
|
||||
s.CredSaveMode = IOCredSaveMode.NoSave;
|
||||
|
||||
s.UserName = TransformUnreadable(vParts[2], false);
|
||||
s.Password = TransformUnreadable(vParts[3], false);
|
||||
return s;
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
/// <summary>
|
||||
/// Very simple string protection. Doesn't really encrypt the input
|
||||
/// string, only encodes it that it's not readable on the first glance.
|
||||
/// </summary>
|
||||
/// <param name="strToEncode">The string to encode/decode.</param>
|
||||
/// <param name="bEncode">If <c>true</c>, the string will be encoded,
|
||||
/// otherwise it'll be decoded.</param>
|
||||
/// <returns>Encoded/decoded string.</returns>
|
||||
private static string TransformUnreadable(string strToEncode, bool bEncode)
|
||||
{
|
||||
Debug.Assert(strToEncode != null);
|
||||
if(strToEncode == null) throw new ArgumentNullException("strToEncode");
|
||||
|
||||
if(bEncode)
|
||||
{
|
||||
byte[] pbUtf8 = StrUtil.Utf8.GetBytes(strToEncode);
|
||||
|
||||
unchecked
|
||||
{
|
||||
for(int iPos = 0; iPos < pbUtf8.Length; ++iPos)
|
||||
pbUtf8[iPos] += (byte)(iPos * 11);
|
||||
}
|
||||
|
||||
return Convert.ToBase64String(pbUtf8);
|
||||
}
|
||||
else // Decode
|
||||
{
|
||||
byte[] pbBase = Convert.FromBase64String(strToEncode);
|
||||
|
||||
unchecked
|
||||
{
|
||||
for(int iPos = 0; iPos < pbBase.Length; ++iPos)
|
||||
pbBase[iPos] -= (byte)(iPos * 11);
|
||||
}
|
||||
|
||||
return StrUtil.Utf8.GetString(pbBase, 0, pbBase.Length);
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
public string GetDisplayName()
|
||||
{
|
||||
string str = m_strUrl;
|
||||
|
||||
if(m_strUser.Length > 0)
|
||||
str += @": " + m_strUser;
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
public bool IsEmpty()
|
||||
{
|
||||
return (m_strUrl.Length > 0);
|
||||
}
|
||||
|
||||
public static IOConnectionInfo FromPath(string strPath)
|
||||
{
|
||||
IOConnectionInfo ioc = new IOConnectionInfo();
|
||||
|
||||
ioc.Path = strPath;
|
||||
ioc.CredSaveMode = IOCredSaveMode.NoSave;
|
||||
|
||||
return ioc;
|
||||
}
|
||||
|
||||
public bool CanProbablyAccess()
|
||||
{
|
||||
Debug.Assert(false, "not yet implemented");
|
||||
return false;
|
||||
#if TODO
|
||||
if(IsLocalFile()) return File.Exists(m_strUrl);
|
||||
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
public bool IsLocalFile()
|
||||
{
|
||||
return (m_strUrl.IndexOf(@"://") < 0);
|
||||
}
|
||||
|
||||
public void ClearCredentials(bool bDependingOnRememberMode)
|
||||
{
|
||||
if((bDependingOnRememberMode == false) ||
|
||||
(m_ioCredSaveMode == IOCredSaveMode.NoSave))
|
||||
{
|
||||
m_strUser = string.Empty;
|
||||
}
|
||||
|
||||
if((bDependingOnRememberMode == false) ||
|
||||
(m_ioCredSaveMode == IOCredSaveMode.NoSave) ||
|
||||
(m_ioCredSaveMode == IOCredSaveMode.UserNameOnly))
|
||||
{
|
||||
m_strPassword = string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
public void Obfuscate(bool bObf)
|
||||
{
|
||||
if(bObf && (m_ioCredProtMode == IOCredProtMode.None))
|
||||
{
|
||||
m_strPassword = StrUtil.Obfuscate(m_strPassword);
|
||||
m_ioCredProtMode = IOCredProtMode.Obf;
|
||||
}
|
||||
else if(!bObf && (m_ioCredProtMode == IOCredProtMode.Obf))
|
||||
{
|
||||
m_strPassword = StrUtil.Deobfuscate(m_strPassword);
|
||||
m_ioCredProtMode = IOCredProtMode.None;
|
||||
}
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (!(obj is IOConnectionInfo)) return false;
|
||||
IOConnectionInfo ioc = obj as IOConnectionInfo;
|
||||
if (ioc.Path != this.Path) return false;
|
||||
if (ioc.UserName != this.UserName) return false;
|
||||
if (ioc.Password != this.Password) return false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
862
ModernKeePassLib/Serialization/Kdb4File.Read.Streamed.cs
Normal file
862
ModernKeePassLib/Serialization/Kdb4File.Read.Streamed.cs
Normal file
@@ -0,0 +1,862 @@
|
||||
/*
|
||||
KeePass Password Safe - The Open-Source Password Manager
|
||||
Copyright (C) 2003-2012 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.Drawing;
|
||||
using System.Xml;
|
||||
using System.IO;
|
||||
using ModernKeePassLib.Collections;
|
||||
using ModernKeePassLib.Interfaces;
|
||||
using ModernKeePassLib.Security;
|
||||
using ModernKeePassLib.Utility;
|
||||
|
||||
namespace ModernKeePassLib.Serialization
|
||||
{
|
||||
public sealed partial class Kdb4File
|
||||
{
|
||||
private enum KdbContext
|
||||
{
|
||||
Null,
|
||||
KeePassFile,
|
||||
Meta,
|
||||
Root,
|
||||
MemoryProtection,
|
||||
CustomIcons,
|
||||
CustomIcon,
|
||||
Binaries,
|
||||
CustomData,
|
||||
CustomDataItem,
|
||||
RootDeletedObjects,
|
||||
DeletedObject,
|
||||
Group,
|
||||
GroupTimes,
|
||||
Entry,
|
||||
EntryTimes,
|
||||
EntryString,
|
||||
EntryBinary,
|
||||
EntryAutoType,
|
||||
EntryAutoTypeItem,
|
||||
EntryHistory
|
||||
}
|
||||
|
||||
private bool m_bReadNextNode = true;
|
||||
private Stack<PwGroup> m_ctxGroups = new Stack<PwGroup>();
|
||||
private PwGroup m_ctxGroup = null;
|
||||
private PwEntry m_ctxEntry = null;
|
||||
private string m_ctxStringName = null;
|
||||
private ProtectedString m_ctxStringValue = null;
|
||||
private string m_ctxBinaryName = null;
|
||||
private ProtectedBinary m_ctxBinaryValue = null;
|
||||
private string m_ctxATName = null;
|
||||
private string m_ctxATSeq = null;
|
||||
private bool m_bEntryInHistory = false;
|
||||
private PwEntry m_ctxHistoryBase = null;
|
||||
private PwDeletedObject m_ctxDeletedObject = null;
|
||||
private PwUuid m_uuidCustomIconID = PwUuid.Zero;
|
||||
private byte[] m_pbCustomIconData = null;
|
||||
private string m_strCustomDataKey = null;
|
||||
private string m_strCustomDataValue = null;
|
||||
|
||||
private void ReadXmlStreamed(Stream readerStream, Stream sParentStream)
|
||||
{
|
||||
ReadDocumentStreamed(CreateXmlReader(readerStream), sParentStream);
|
||||
}
|
||||
|
||||
internal static XmlReaderSettings CreateStdXmlReaderSettings()
|
||||
{
|
||||
XmlReaderSettings xrs = new XmlReaderSettings();
|
||||
|
||||
xrs.CloseInput = true;
|
||||
xrs.IgnoreComments = true;
|
||||
xrs.IgnoreProcessingInstructions = true;
|
||||
xrs.IgnoreWhitespace = true;
|
||||
|
||||
#if !KeePassLibSD && TODO
|
||||
xrs.ProhibitDtd = true;
|
||||
#endif
|
||||
|
||||
#if TODO
|
||||
xrs.ValidationType = ValidationType.None;
|
||||
#endif
|
||||
return xrs;
|
||||
}
|
||||
|
||||
private static XmlReader CreateXmlReader(Stream readerStream)
|
||||
{
|
||||
XmlReaderSettings xrs = CreateStdXmlReaderSettings();
|
||||
return XmlReader.Create(readerStream, xrs);
|
||||
}
|
||||
|
||||
private void ReadDocumentStreamed(XmlReader xr, Stream sParentStream)
|
||||
{
|
||||
Debug.Assert(xr != null);
|
||||
if(xr == null) throw new ArgumentNullException("xr");
|
||||
|
||||
m_ctxGroups.Clear();
|
||||
m_dictBinPool = new Dictionary<string, ProtectedBinary>();
|
||||
|
||||
KdbContext ctx = KdbContext.Null;
|
||||
|
||||
uint uTagCounter = 0;
|
||||
|
||||
bool bSupportsStatus = (m_slLogger != null);
|
||||
long lStreamLength = 1;
|
||||
try
|
||||
{
|
||||
sParentStream.Position.ToString(); // Test Position support
|
||||
lStreamLength = sParentStream.Length;
|
||||
}
|
||||
catch(Exception) { bSupportsStatus = false; }
|
||||
if(lStreamLength <= 0) { Debug.Assert(false); lStreamLength = 1; }
|
||||
|
||||
m_bReadNextNode = true;
|
||||
|
||||
while(true)
|
||||
{
|
||||
if(m_bReadNextNode)
|
||||
{
|
||||
if(!xr.Read()) break;
|
||||
}
|
||||
else m_bReadNextNode = true;
|
||||
|
||||
switch(xr.NodeType)
|
||||
{
|
||||
case XmlNodeType.Element:
|
||||
ctx = ReadXmlElement(ctx, xr);
|
||||
break;
|
||||
|
||||
case XmlNodeType.EndElement:
|
||||
ctx = EndXmlElement(ctx, xr);
|
||||
break;
|
||||
|
||||
case XmlNodeType.XmlDeclaration:
|
||||
break; // Ignore
|
||||
|
||||
default:
|
||||
Debug.Assert(false);
|
||||
break;
|
||||
}
|
||||
|
||||
++uTagCounter;
|
||||
if(((uTagCounter % 256) == 0) && bSupportsStatus)
|
||||
{
|
||||
Debug.Assert(lStreamLength == sParentStream.Length);
|
||||
uint uPct = (uint)((sParentStream.Position * 100) /
|
||||
lStreamLength);
|
||||
|
||||
// Clip percent value in case the stream reports incorrect
|
||||
// position/length values (M120413)
|
||||
if(uPct > 100) { Debug.Assert(false); uPct = 100; }
|
||||
|
||||
m_slLogger.SetProgress(uPct);
|
||||
}
|
||||
}
|
||||
|
||||
Debug.Assert(ctx == KdbContext.Null);
|
||||
if(ctx != KdbContext.Null) throw new FormatException();
|
||||
|
||||
Debug.Assert(m_ctxGroups.Count == 0);
|
||||
if(m_ctxGroups.Count != 0) throw new FormatException();
|
||||
}
|
||||
|
||||
private KdbContext ReadXmlElement(KdbContext ctx, XmlReader xr)
|
||||
{
|
||||
Debug.Assert(xr.NodeType == XmlNodeType.Element);
|
||||
|
||||
switch(ctx)
|
||||
{
|
||||
case KdbContext.Null:
|
||||
if(xr.Name == ElemDocNode)
|
||||
return SwitchContext(ctx, KdbContext.KeePassFile, xr);
|
||||
else ReadUnknown(xr);
|
||||
break;
|
||||
|
||||
case KdbContext.KeePassFile:
|
||||
if(xr.Name == ElemMeta)
|
||||
return SwitchContext(ctx, KdbContext.Meta, xr);
|
||||
else if(xr.Name == ElemRoot)
|
||||
return SwitchContext(ctx, KdbContext.Root, xr);
|
||||
else ReadUnknown(xr);
|
||||
break;
|
||||
|
||||
case KdbContext.Meta:
|
||||
if(xr.Name == ElemGenerator)
|
||||
ReadString(xr); // Ignore
|
||||
else if(xr.Name == ElemDbName)
|
||||
m_pwDatabase.Name = ReadString(xr);
|
||||
else if(xr.Name == ElemDbNameChanged)
|
||||
m_pwDatabase.NameChanged = ReadTime(xr);
|
||||
else if(xr.Name == ElemDbDesc)
|
||||
m_pwDatabase.Description = ReadString(xr);
|
||||
else if(xr.Name == ElemDbDescChanged)
|
||||
m_pwDatabase.DescriptionChanged = ReadTime(xr);
|
||||
else if(xr.Name == ElemDbDefaultUser)
|
||||
m_pwDatabase.DefaultUserName = ReadString(xr);
|
||||
else if(xr.Name == ElemDbDefaultUserChanged)
|
||||
m_pwDatabase.DefaultUserNameChanged = ReadTime(xr);
|
||||
else if(xr.Name == ElemDbMntncHistoryDays)
|
||||
m_pwDatabase.MaintenanceHistoryDays = ReadUInt(xr, 365);
|
||||
else if(xr.Name == ElemDbColor)
|
||||
{
|
||||
string strColor = ReadString(xr);
|
||||
#if TODO
|
||||
if(!string.IsNullOrEmpty(strColor))
|
||||
m_pwDatabase.Color = ColorTranslator.FromHtml(strColor);
|
||||
#endif
|
||||
}
|
||||
else if(xr.Name == ElemDbKeyChanged)
|
||||
m_pwDatabase.MasterKeyChanged = ReadTime(xr);
|
||||
else if(xr.Name == ElemDbKeyChangeRec)
|
||||
m_pwDatabase.MasterKeyChangeRec = ReadLong(xr, -1);
|
||||
else if(xr.Name == ElemDbKeyChangeForce)
|
||||
m_pwDatabase.MasterKeyChangeForce = ReadLong(xr, -1);
|
||||
else if(xr.Name == ElemMemoryProt)
|
||||
return SwitchContext(ctx, KdbContext.MemoryProtection, xr);
|
||||
else if(xr.Name == ElemCustomIcons)
|
||||
return SwitchContext(ctx, KdbContext.CustomIcons, xr);
|
||||
else if(xr.Name == ElemRecycleBinEnabled)
|
||||
m_pwDatabase.RecycleBinEnabled = ReadBool(xr, true);
|
||||
else if(xr.Name == ElemRecycleBinUuid)
|
||||
m_pwDatabase.RecycleBinUuid = ReadUuid(xr);
|
||||
else if(xr.Name == ElemRecycleBinChanged)
|
||||
m_pwDatabase.RecycleBinChanged = ReadTime(xr);
|
||||
else if(xr.Name == ElemEntryTemplatesGroup)
|
||||
m_pwDatabase.EntryTemplatesGroup = ReadUuid(xr);
|
||||
else if(xr.Name == ElemEntryTemplatesGroupChanged)
|
||||
m_pwDatabase.EntryTemplatesGroupChanged = ReadTime(xr);
|
||||
else if(xr.Name == ElemHistoryMaxItems)
|
||||
m_pwDatabase.HistoryMaxItems = ReadInt(xr, -1);
|
||||
else if(xr.Name == ElemHistoryMaxSize)
|
||||
m_pwDatabase.HistoryMaxSize = ReadLong(xr, -1);
|
||||
else if(xr.Name == ElemLastSelectedGroup)
|
||||
m_pwDatabase.LastSelectedGroup = ReadUuid(xr);
|
||||
else if(xr.Name == ElemLastTopVisibleGroup)
|
||||
m_pwDatabase.LastTopVisibleGroup = ReadUuid(xr);
|
||||
else if(xr.Name == ElemBinaries)
|
||||
return SwitchContext(ctx, KdbContext.Binaries, xr);
|
||||
else if(xr.Name == ElemCustomData)
|
||||
return SwitchContext(ctx, KdbContext.CustomData, xr);
|
||||
else ReadUnknown(xr);
|
||||
break;
|
||||
|
||||
case KdbContext.MemoryProtection:
|
||||
if(xr.Name == ElemProtTitle)
|
||||
m_pwDatabase.MemoryProtection.ProtectTitle = ReadBool(xr, false);
|
||||
else if(xr.Name == ElemProtUserName)
|
||||
m_pwDatabase.MemoryProtection.ProtectUserName = ReadBool(xr, false);
|
||||
else if(xr.Name == ElemProtPassword)
|
||||
m_pwDatabase.MemoryProtection.ProtectPassword = ReadBool(xr, true);
|
||||
else if(xr.Name == ElemProtUrl)
|
||||
m_pwDatabase.MemoryProtection.ProtectUrl = ReadBool(xr, false);
|
||||
else if(xr.Name == ElemProtNotes)
|
||||
m_pwDatabase.MemoryProtection.ProtectNotes = ReadBool(xr, false);
|
||||
// else if(xr.Name == ElemProtAutoHide)
|
||||
// m_pwDatabase.MemoryProtection.AutoEnableVisualHiding = ReadBool(xr, true);
|
||||
else ReadUnknown(xr);
|
||||
break;
|
||||
|
||||
case KdbContext.CustomIcons:
|
||||
if(xr.Name == ElemCustomIconItem)
|
||||
return SwitchContext(ctx, KdbContext.CustomIcon, xr);
|
||||
else ReadUnknown(xr);
|
||||
break;
|
||||
|
||||
case KdbContext.CustomIcon:
|
||||
if(xr.Name == ElemCustomIconItemID)
|
||||
m_uuidCustomIconID = ReadUuid(xr);
|
||||
else if(xr.Name == ElemCustomIconItemData)
|
||||
{
|
||||
string strData = ReadString(xr);
|
||||
if(!string.IsNullOrEmpty(strData))
|
||||
m_pbCustomIconData = Convert.FromBase64String(strData);
|
||||
else { Debug.Assert(false); }
|
||||
}
|
||||
else ReadUnknown(xr);
|
||||
break;
|
||||
|
||||
case KdbContext.Binaries:
|
||||
if(xr.Name == ElemBinary)
|
||||
{
|
||||
if(xr.MoveToAttribute(AttrId))
|
||||
{
|
||||
string strKey = xr.Value;
|
||||
ProtectedBinary pbData = ReadProtectedBinary(xr);
|
||||
|
||||
m_dictBinPool[strKey ?? string.Empty] = pbData;
|
||||
}
|
||||
else ReadUnknown(xr);
|
||||
}
|
||||
else ReadUnknown(xr);
|
||||
break;
|
||||
|
||||
case KdbContext.CustomData:
|
||||
if(xr.Name == ElemStringDictExItem)
|
||||
return SwitchContext(ctx, KdbContext.CustomDataItem, xr);
|
||||
else ReadUnknown(xr);
|
||||
break;
|
||||
|
||||
case KdbContext.CustomDataItem:
|
||||
if(xr.Name == ElemKey)
|
||||
m_strCustomDataKey = ReadString(xr);
|
||||
else if(xr.Name == ElemValue)
|
||||
m_strCustomDataValue = ReadString(xr);
|
||||
else ReadUnknown(xr);
|
||||
break;
|
||||
|
||||
case KdbContext.Root:
|
||||
if(xr.Name == ElemGroup)
|
||||
{
|
||||
Debug.Assert(m_ctxGroups.Count == 0);
|
||||
if(m_ctxGroups.Count != 0) throw new FormatException();
|
||||
|
||||
m_pwDatabase.RootGroup = new PwGroup(false, false);
|
||||
m_ctxGroups.Push(m_pwDatabase.RootGroup);
|
||||
m_ctxGroup = m_ctxGroups.Peek();
|
||||
|
||||
return SwitchContext(ctx, KdbContext.Group, xr);
|
||||
}
|
||||
else if(xr.Name == ElemDeletedObjects)
|
||||
return SwitchContext(ctx, KdbContext.RootDeletedObjects, xr);
|
||||
else ReadUnknown(xr);
|
||||
break;
|
||||
|
||||
case KdbContext.Group:
|
||||
if(xr.Name == ElemUuid)
|
||||
m_ctxGroup.Uuid = ReadUuid(xr);
|
||||
else if(xr.Name == ElemName)
|
||||
m_ctxGroup.Name = ReadString(xr);
|
||||
else if(xr.Name == ElemNotes)
|
||||
m_ctxGroup.Notes = ReadString(xr);
|
||||
else if(xr.Name == ElemIcon)
|
||||
m_ctxGroup.IconId = (PwIcon)ReadInt(xr, (int)PwIcon.Folder);
|
||||
else if(xr.Name == ElemCustomIconID)
|
||||
m_ctxGroup.CustomIconUuid = ReadUuid(xr);
|
||||
else if(xr.Name == ElemTimes)
|
||||
return SwitchContext(ctx, KdbContext.GroupTimes, xr);
|
||||
else if(xr.Name == ElemIsExpanded)
|
||||
m_ctxGroup.IsExpanded = ReadBool(xr, true);
|
||||
else if(xr.Name == ElemGroupDefaultAutoTypeSeq)
|
||||
m_ctxGroup.DefaultAutoTypeSequence = ReadString(xr);
|
||||
else if(xr.Name == ElemEnableAutoType)
|
||||
m_ctxGroup.EnableAutoType = StrUtil.StringToBoolEx(ReadString(xr));
|
||||
else if(xr.Name == ElemEnableSearching)
|
||||
m_ctxGroup.EnableSearching = StrUtil.StringToBoolEx(ReadString(xr));
|
||||
else if(xr.Name == ElemLastTopVisibleEntry)
|
||||
m_ctxGroup.LastTopVisibleEntry = ReadUuid(xr);
|
||||
else if(xr.Name == ElemGroup)
|
||||
{
|
||||
m_ctxGroup = new PwGroup(false, false);
|
||||
m_ctxGroups.Peek().AddGroup(m_ctxGroup, true);
|
||||
|
||||
m_ctxGroups.Push(m_ctxGroup);
|
||||
|
||||
return SwitchContext(ctx, KdbContext.Group, xr);
|
||||
}
|
||||
else if(xr.Name == ElemEntry)
|
||||
{
|
||||
m_ctxEntry = new PwEntry(false, false);
|
||||
m_ctxGroup.AddEntry(m_ctxEntry, true);
|
||||
|
||||
m_bEntryInHistory = false;
|
||||
return SwitchContext(ctx, KdbContext.Entry, xr);
|
||||
}
|
||||
else ReadUnknown(xr);
|
||||
break;
|
||||
|
||||
case KdbContext.Entry:
|
||||
if(xr.Name == ElemUuid)
|
||||
m_ctxEntry.Uuid = ReadUuid(xr);
|
||||
else if(xr.Name == ElemIcon)
|
||||
m_ctxEntry.IconId = (PwIcon)ReadInt(xr, (int)PwIcon.Key);
|
||||
else if(xr.Name == ElemCustomIconID)
|
||||
m_ctxEntry.CustomIconUuid = ReadUuid(xr);
|
||||
else if(xr.Name == ElemFgColor)
|
||||
{
|
||||
string strColor = ReadString(xr);
|
||||
#if TODO
|
||||
if(!string.IsNullOrEmpty(strColor))
|
||||
m_ctxEntry.ForegroundColor = ColorTranslator.FromHtml(strColor);
|
||||
#endif
|
||||
}
|
||||
else if(xr.Name == ElemBgColor)
|
||||
{
|
||||
|
||||
string strColor = ReadString(xr);
|
||||
#if TODO
|
||||
if(!string.IsNullOrEmpty(strColor))
|
||||
m_ctxEntry.BackgroundColor = ColorTranslator.FromHtml(strColor);
|
||||
#endif
|
||||
}
|
||||
else if(xr.Name == ElemOverrideUrl)
|
||||
m_ctxEntry.OverrideUrl = ReadString(xr);
|
||||
else if(xr.Name == ElemTags)
|
||||
m_ctxEntry.Tags = StrUtil.StringToTags(ReadString(xr));
|
||||
else if(xr.Name == ElemTimes)
|
||||
return SwitchContext(ctx, KdbContext.EntryTimes, xr);
|
||||
else if(xr.Name == ElemString)
|
||||
return SwitchContext(ctx, KdbContext.EntryString, xr);
|
||||
else if(xr.Name == ElemBinary)
|
||||
return SwitchContext(ctx, KdbContext.EntryBinary, xr);
|
||||
else if(xr.Name == ElemAutoType)
|
||||
return SwitchContext(ctx, KdbContext.EntryAutoType, xr);
|
||||
else if(xr.Name == ElemHistory)
|
||||
{
|
||||
Debug.Assert(m_bEntryInHistory == false);
|
||||
|
||||
if(m_bEntryInHistory == false)
|
||||
{
|
||||
m_ctxHistoryBase = m_ctxEntry;
|
||||
return SwitchContext(ctx, KdbContext.EntryHistory, xr);
|
||||
}
|
||||
else ReadUnknown(xr);
|
||||
}
|
||||
else ReadUnknown(xr);
|
||||
break;
|
||||
|
||||
case KdbContext.GroupTimes:
|
||||
case KdbContext.EntryTimes:
|
||||
ITimeLogger tl = ((ctx == KdbContext.GroupTimes) ?
|
||||
(ITimeLogger)m_ctxGroup : (ITimeLogger)m_ctxEntry);
|
||||
Debug.Assert(tl != null);
|
||||
|
||||
if(xr.Name == ElemLastModTime)
|
||||
tl.LastModificationTime = ReadTime(xr);
|
||||
else if(xr.Name == ElemCreationTime)
|
||||
tl.CreationTime = ReadTime(xr);
|
||||
else if(xr.Name == ElemLastAccessTime)
|
||||
tl.LastAccessTime = ReadTime(xr);
|
||||
else if(xr.Name == ElemExpiryTime)
|
||||
tl.ExpiryTime = ReadTime(xr);
|
||||
else if(xr.Name == ElemExpires)
|
||||
tl.Expires = ReadBool(xr, false);
|
||||
else if(xr.Name == ElemUsageCount)
|
||||
tl.UsageCount = ReadULong(xr, 0);
|
||||
else if(xr.Name == ElemLocationChanged)
|
||||
tl.LocationChanged = ReadTime(xr);
|
||||
else ReadUnknown(xr);
|
||||
break;
|
||||
|
||||
case KdbContext.EntryString:
|
||||
if(xr.Name == ElemKey)
|
||||
m_ctxStringName = ReadString(xr);
|
||||
else if(xr.Name == ElemValue)
|
||||
m_ctxStringValue = ReadProtectedString(xr);
|
||||
else ReadUnknown(xr);
|
||||
break;
|
||||
|
||||
case KdbContext.EntryBinary:
|
||||
if(xr.Name == ElemKey)
|
||||
m_ctxBinaryName = ReadString(xr);
|
||||
else if(xr.Name == ElemValue)
|
||||
m_ctxBinaryValue = ReadProtectedBinary(xr);
|
||||
else ReadUnknown(xr);
|
||||
break;
|
||||
|
||||
case KdbContext.EntryAutoType:
|
||||
if(xr.Name == ElemAutoTypeEnabled)
|
||||
m_ctxEntry.AutoType.Enabled = ReadBool(xr, true);
|
||||
else if(xr.Name == ElemAutoTypeObfuscation)
|
||||
m_ctxEntry.AutoType.ObfuscationOptions =
|
||||
(AutoTypeObfuscationOptions)ReadInt(xr, 0);
|
||||
else if(xr.Name == ElemAutoTypeDefaultSeq)
|
||||
m_ctxEntry.AutoType.DefaultSequence = ReadString(xr);
|
||||
else if(xr.Name == ElemAutoTypeItem)
|
||||
return SwitchContext(ctx, KdbContext.EntryAutoTypeItem, xr);
|
||||
else ReadUnknown(xr);
|
||||
break;
|
||||
|
||||
case KdbContext.EntryAutoTypeItem:
|
||||
if(xr.Name == ElemWindow)
|
||||
m_ctxATName = ReadString(xr);
|
||||
else if(xr.Name == ElemKeystrokeSequence)
|
||||
m_ctxATSeq = ReadString(xr);
|
||||
else ReadUnknown(xr);
|
||||
break;
|
||||
|
||||
case KdbContext.EntryHistory:
|
||||
if(xr.Name == ElemEntry)
|
||||
{
|
||||
m_ctxEntry = new PwEntry(false, false);
|
||||
m_ctxHistoryBase.History.Add(m_ctxEntry);
|
||||
|
||||
m_bEntryInHistory = true;
|
||||
return SwitchContext(ctx, KdbContext.Entry, xr);
|
||||
}
|
||||
else ReadUnknown(xr);
|
||||
break;
|
||||
|
||||
case KdbContext.RootDeletedObjects:
|
||||
if(xr.Name == ElemDeletedObject)
|
||||
{
|
||||
m_ctxDeletedObject = new PwDeletedObject();
|
||||
m_pwDatabase.DeletedObjects.Add(m_ctxDeletedObject);
|
||||
|
||||
return SwitchContext(ctx, KdbContext.DeletedObject, xr);
|
||||
}
|
||||
else ReadUnknown(xr);
|
||||
break;
|
||||
|
||||
case KdbContext.DeletedObject:
|
||||
if(xr.Name == ElemUuid)
|
||||
m_ctxDeletedObject.Uuid = ReadUuid(xr);
|
||||
else if(xr.Name == ElemDeletionTime)
|
||||
m_ctxDeletedObject.DeletionTime = ReadTime(xr);
|
||||
else ReadUnknown(xr);
|
||||
break;
|
||||
|
||||
default:
|
||||
ReadUnknown(xr);
|
||||
break;
|
||||
}
|
||||
|
||||
return ctx;
|
||||
}
|
||||
|
||||
private KdbContext EndXmlElement(KdbContext ctx, XmlReader xr)
|
||||
{
|
||||
Debug.Assert(xr.NodeType == XmlNodeType.EndElement);
|
||||
|
||||
if((ctx == KdbContext.KeePassFile) && (xr.Name == ElemDocNode))
|
||||
return KdbContext.Null;
|
||||
else if((ctx == KdbContext.Meta) && (xr.Name == ElemMeta))
|
||||
return KdbContext.KeePassFile;
|
||||
else if((ctx == KdbContext.Root) && (xr.Name == ElemRoot))
|
||||
return KdbContext.KeePassFile;
|
||||
else if((ctx == KdbContext.MemoryProtection) && (xr.Name == ElemMemoryProt))
|
||||
return KdbContext.Meta;
|
||||
else if((ctx == KdbContext.CustomIcons) && (xr.Name == ElemCustomIcons))
|
||||
return KdbContext.Meta;
|
||||
else if((ctx == KdbContext.CustomIcon) && (xr.Name == ElemCustomIconItem))
|
||||
{
|
||||
if((m_uuidCustomIconID != PwUuid.Zero) && (m_pbCustomIconData != null))
|
||||
m_pwDatabase.CustomIcons.Add(new PwCustomIcon(
|
||||
m_uuidCustomIconID, m_pbCustomIconData));
|
||||
else { Debug.Assert(false); }
|
||||
|
||||
m_uuidCustomIconID = PwUuid.Zero;
|
||||
m_pbCustomIconData = null;
|
||||
|
||||
return KdbContext.CustomIcons;
|
||||
}
|
||||
else if((ctx == KdbContext.Binaries) && (xr.Name == ElemBinaries))
|
||||
return KdbContext.Meta;
|
||||
else if((ctx == KdbContext.CustomData) && (xr.Name == ElemCustomData))
|
||||
return KdbContext.Meta;
|
||||
else if((ctx == KdbContext.CustomDataItem) && (xr.Name == ElemStringDictExItem))
|
||||
{
|
||||
if((m_strCustomDataKey != null) && (m_strCustomDataValue != null))
|
||||
m_pwDatabase.CustomData.Set(m_strCustomDataKey, m_strCustomDataValue);
|
||||
else { Debug.Assert(false); }
|
||||
|
||||
m_strCustomDataKey = null;
|
||||
m_strCustomDataValue = null;
|
||||
|
||||
return KdbContext.CustomData;
|
||||
}
|
||||
else if((ctx == KdbContext.Group) && (xr.Name == ElemGroup))
|
||||
{
|
||||
if(PwUuid.Zero.EqualsValue(m_ctxGroup.Uuid))
|
||||
m_ctxGroup.Uuid = new PwUuid(true); // No assert (import)
|
||||
|
||||
m_ctxGroups.Pop();
|
||||
|
||||
if(m_ctxGroups.Count == 0)
|
||||
{
|
||||
m_ctxGroup = null;
|
||||
return KdbContext.Root;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_ctxGroup = m_ctxGroups.Peek();
|
||||
return KdbContext.Group;
|
||||
}
|
||||
}
|
||||
else if((ctx == KdbContext.GroupTimes) && (xr.Name == ElemTimes))
|
||||
return KdbContext.Group;
|
||||
else if((ctx == KdbContext.Entry) && (xr.Name == ElemEntry))
|
||||
{
|
||||
// Create new UUID if absent
|
||||
if(PwUuid.Zero.EqualsValue(m_ctxEntry.Uuid))
|
||||
m_ctxEntry.Uuid = new PwUuid(true); // No assert (import)
|
||||
|
||||
if(m_bEntryInHistory)
|
||||
{
|
||||
m_ctxEntry = m_ctxHistoryBase;
|
||||
return KdbContext.EntryHistory;
|
||||
}
|
||||
|
||||
return KdbContext.Group;
|
||||
}
|
||||
else if((ctx == KdbContext.EntryTimes) && (xr.Name == ElemTimes))
|
||||
return KdbContext.Entry;
|
||||
else if((ctx == KdbContext.EntryString) && (xr.Name == ElemString))
|
||||
{
|
||||
m_ctxEntry.Strings.Set(m_ctxStringName, m_ctxStringValue);
|
||||
m_ctxStringName = null;
|
||||
m_ctxStringValue = null;
|
||||
return KdbContext.Entry;
|
||||
}
|
||||
else if((ctx == KdbContext.EntryBinary) && (xr.Name == ElemBinary))
|
||||
{
|
||||
if(string.IsNullOrEmpty(m_strDetachBins))
|
||||
m_ctxEntry.Binaries.Set(m_ctxBinaryName, m_ctxBinaryValue);
|
||||
else
|
||||
{
|
||||
SaveBinary(m_ctxBinaryName, m_ctxBinaryValue, m_strDetachBins);
|
||||
|
||||
m_ctxBinaryValue = null;
|
||||
GC.Collect();
|
||||
}
|
||||
|
||||
m_ctxBinaryName = null;
|
||||
m_ctxBinaryValue = null;
|
||||
return KdbContext.Entry;
|
||||
}
|
||||
else if((ctx == KdbContext.EntryAutoType) && (xr.Name == ElemAutoType))
|
||||
return KdbContext.Entry;
|
||||
else if((ctx == KdbContext.EntryAutoTypeItem) && (xr.Name == ElemAutoTypeItem))
|
||||
{
|
||||
AutoTypeAssociation atAssoc = new AutoTypeAssociation(m_ctxATName,
|
||||
m_ctxATSeq);
|
||||
m_ctxEntry.AutoType.Add(atAssoc);
|
||||
m_ctxATName = null;
|
||||
m_ctxATSeq = null;
|
||||
return KdbContext.EntryAutoType;
|
||||
}
|
||||
else if((ctx == KdbContext.EntryHistory) && (xr.Name == ElemHistory))
|
||||
{
|
||||
m_bEntryInHistory = false;
|
||||
return KdbContext.Entry;
|
||||
}
|
||||
else if((ctx == KdbContext.RootDeletedObjects) && (xr.Name == ElemDeletedObjects))
|
||||
return KdbContext.Root;
|
||||
else if((ctx == KdbContext.DeletedObject) && (xr.Name == ElemDeletedObject))
|
||||
{
|
||||
m_ctxDeletedObject = null;
|
||||
return KdbContext.RootDeletedObjects;
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.Assert(false);
|
||||
throw new FormatException();
|
||||
}
|
||||
}
|
||||
|
||||
private string ReadString(XmlReader xr)
|
||||
{
|
||||
XorredBuffer xb = ProcessNode(xr);
|
||||
if(xb != null)
|
||||
{
|
||||
byte[] pb = xb.ReadPlainText();
|
||||
if(pb.Length == 0) return string.Empty;
|
||||
return StrUtil.Utf8.GetString(pb, 0, pb.Length);
|
||||
}
|
||||
|
||||
m_bReadNextNode = false; // ReadElementString skips end tag
|
||||
return xr.ReadElementContentAsString();
|
||||
}
|
||||
|
||||
private string ReadStringRaw(XmlReader xr)
|
||||
{
|
||||
m_bReadNextNode = false; // ReadElementString skips end tag
|
||||
return xr.ReadElementContentAsString();
|
||||
}
|
||||
|
||||
private bool ReadBool(XmlReader xr, bool bDefault)
|
||||
{
|
||||
string str = ReadString(xr);
|
||||
if(str == ValTrue) return true;
|
||||
else if(str == ValFalse) return false;
|
||||
|
||||
Debug.Assert(false);
|
||||
return bDefault;
|
||||
}
|
||||
|
||||
private PwUuid ReadUuid(XmlReader xr)
|
||||
{
|
||||
string str = ReadString(xr);
|
||||
if(string.IsNullOrEmpty(str)) return PwUuid.Zero;
|
||||
return new PwUuid(Convert.FromBase64String(str));
|
||||
}
|
||||
|
||||
private int ReadInt(XmlReader xr, int nDefault)
|
||||
{
|
||||
string str = ReadString(xr);
|
||||
|
||||
int n;
|
||||
if(StrUtil.TryParseInt(str, out n)) return n;
|
||||
|
||||
Debug.Assert(false);
|
||||
return nDefault;
|
||||
}
|
||||
|
||||
private uint ReadUInt(XmlReader xr, uint uDefault)
|
||||
{
|
||||
string str = ReadString(xr);
|
||||
|
||||
uint u;
|
||||
if(StrUtil.TryParseUInt(str, out u)) return u;
|
||||
|
||||
Debug.Assert(false);
|
||||
return uDefault;
|
||||
}
|
||||
|
||||
private long ReadLong(XmlReader xr, long lDefault)
|
||||
{
|
||||
string str = ReadString(xr);
|
||||
|
||||
long l;
|
||||
if(StrUtil.TryParseLong(str, out l)) return l;
|
||||
|
||||
Debug.Assert(false);
|
||||
return lDefault;
|
||||
}
|
||||
|
||||
private ulong ReadULong(XmlReader xr, ulong uDefault)
|
||||
{
|
||||
string str = ReadString(xr);
|
||||
|
||||
ulong u;
|
||||
if(StrUtil.TryParseULong(str, out u)) return u;
|
||||
|
||||
Debug.Assert(false);
|
||||
return uDefault;
|
||||
}
|
||||
|
||||
private DateTime ReadTime(XmlReader xr)
|
||||
{
|
||||
string str = ReadString(xr);
|
||||
|
||||
DateTime dt;
|
||||
if(TimeUtil.TryDeserializeUtc(str, out dt)) return dt;
|
||||
|
||||
Debug.Assert(false);
|
||||
return m_dtNow;
|
||||
}
|
||||
|
||||
private ProtectedString ReadProtectedString(XmlReader xr)
|
||||
{
|
||||
XorredBuffer xb = ProcessNode(xr);
|
||||
if(xb != null) return new ProtectedString(true, xb);
|
||||
|
||||
bool bProtect = false;
|
||||
if(m_format == Kdb4Format.PlainXml)
|
||||
{
|
||||
if(xr.MoveToAttribute(AttrProtectedInMemPlainXml))
|
||||
{
|
||||
string strProtect = xr.Value;
|
||||
bProtect = ((strProtect != null) && (strProtect == ValTrue));
|
||||
}
|
||||
}
|
||||
|
||||
ProtectedString ps = new ProtectedString(bProtect, ReadString(xr));
|
||||
return ps;
|
||||
}
|
||||
|
||||
private ProtectedBinary ReadProtectedBinary(XmlReader xr)
|
||||
{
|
||||
if(xr.MoveToAttribute(AttrRef))
|
||||
{
|
||||
string strRef = xr.Value;
|
||||
if(strRef != null)
|
||||
{
|
||||
ProtectedBinary pb = BinPoolGet(strRef);
|
||||
if(pb != null) return pb;
|
||||
else { Debug.Assert(false); }
|
||||
}
|
||||
else { Debug.Assert(false); }
|
||||
}
|
||||
|
||||
bool bCompressed = false;
|
||||
if(xr.MoveToAttribute(AttrCompressed))
|
||||
bCompressed = (xr.Value == ValTrue);
|
||||
|
||||
XorredBuffer xb = ProcessNode(xr);
|
||||
if(xb != null)
|
||||
{
|
||||
Debug.Assert(!bCompressed); // See SubWriteValue(ProtectedBinary value)
|
||||
return new ProtectedBinary(true, xb);
|
||||
}
|
||||
|
||||
string strValue = ReadString(xr);
|
||||
if(strValue.Length == 0) return new ProtectedBinary();
|
||||
|
||||
byte[] pbData = Convert.FromBase64String(strValue);
|
||||
if(bCompressed) pbData = MemUtil.Decompress(pbData);
|
||||
return new ProtectedBinary(false, pbData);
|
||||
}
|
||||
|
||||
private void ReadUnknown(XmlReader xr)
|
||||
{
|
||||
Debug.Assert(false); // Unknown node!
|
||||
|
||||
if(xr.IsEmptyElement) return;
|
||||
|
||||
string strUnknownName = xr.Name;
|
||||
ProcessNode(xr);
|
||||
|
||||
while(xr.Read())
|
||||
{
|
||||
if(xr.NodeType == XmlNodeType.EndElement) break;
|
||||
if(xr.NodeType != XmlNodeType.Element) continue;
|
||||
|
||||
ReadUnknown(xr);
|
||||
}
|
||||
|
||||
Debug.Assert(xr.Name == strUnknownName);
|
||||
}
|
||||
|
||||
private XorredBuffer ProcessNode(XmlReader xr)
|
||||
{
|
||||
// Debug.Assert(xr.NodeType == XmlNodeType.Element);
|
||||
|
||||
XorredBuffer xb = null;
|
||||
if(xr.HasAttributes)
|
||||
{
|
||||
if(xr.MoveToAttribute(AttrProtected))
|
||||
{
|
||||
if(xr.Value == ValTrue)
|
||||
{
|
||||
xr.MoveToElement();
|
||||
string strEncrypted = ReadStringRaw(xr);
|
||||
|
||||
byte[] pbEncrypted;
|
||||
if(strEncrypted.Length > 0)
|
||||
pbEncrypted = Convert.FromBase64String(strEncrypted);
|
||||
else pbEncrypted = new byte[0];
|
||||
|
||||
byte[] pbPad = m_randomStream.GetRandomBytes((uint)pbEncrypted.Length);
|
||||
|
||||
xb = new XorredBuffer(pbEncrypted, pbPad);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return xb;
|
||||
}
|
||||
|
||||
private static KdbContext SwitchContext(KdbContext ctxCurrent,
|
||||
KdbContext ctxNew, XmlReader xr)
|
||||
{
|
||||
if(xr.IsEmptyElement) return ctxCurrent;
|
||||
return ctxNew;
|
||||
}
|
||||
}
|
||||
}
|
408
ModernKeePassLib/Serialization/Kdb4File.Read.cs
Normal file
408
ModernKeePassLib/Serialization/Kdb4File.Read.cs
Normal file
@@ -0,0 +1,408 @@
|
||||
/*
|
||||
KeePass Password Safe - The Open-Source Password Manager
|
||||
Copyright (C) 2003-2012 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.Text;
|
||||
using System.IO;
|
||||
using System.Security;
|
||||
|
||||
#if !KeePassLibSD
|
||||
using System.IO.Compression;
|
||||
#else
|
||||
using ModernKeePassLibSD;
|
||||
#endif
|
||||
|
||||
using ModernKeePassLib.Cryptography;
|
||||
using ModernKeePassLib.Cryptography.Cipher;
|
||||
using ModernKeePassLib.Interfaces;
|
||||
using ModernKeePassLib.Keys;
|
||||
using ModernKeePassLib.Resources;
|
||||
using ModernKeePassLib.Utility;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace ModernKeePassLib.Serialization
|
||||
{
|
||||
/// <summary>
|
||||
/// Serialization to KeePass KDBX files.
|
||||
/// </summary>
|
||||
public sealed partial class Kdb4File
|
||||
{
|
||||
private IOConnection m_connection;
|
||||
|
||||
/// <summary>
|
||||
/// Load a KDB file from a file.
|
||||
/// </summary>
|
||||
/// <param name="strFilePath">File to load.</param>
|
||||
/// <param name="kdbFormat">Format specifier.</param>
|
||||
/// <param name="slLogger">Status logger (optional).</param>
|
||||
public async Task Load(string strFilePath, Kdb4Format kdbFormat, IStatusLogger slLogger)
|
||||
{
|
||||
IOConnectionInfo ioc = IOConnectionInfo.FromPath(strFilePath);
|
||||
IOConnection ioConnection = new IOConnection();
|
||||
Stream abc = await ioConnection.OpenRead(ioc);
|
||||
await Load(abc, kdbFormat, slLogger);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Load a KDB file from a stream.
|
||||
/// </summary>
|
||||
/// <param name="sSource">Stream to read the data from. Must contain
|
||||
/// a KDB4 stream.</param>
|
||||
/// <param name="kdbFormat">Format specifier.</param>
|
||||
/// <param name="slLogger">Status logger (optional).</param>
|
||||
public async Task Load(Stream sSource, Kdb4Format kdbFormat, IStatusLogger slLogger)
|
||||
{
|
||||
|
||||
Debug.Assert(sSource != null);
|
||||
if(sSource == null) throw new ArgumentNullException("sSource");
|
||||
|
||||
m_format = kdbFormat;
|
||||
m_slLogger = slLogger;
|
||||
|
||||
HashingStreamEx hashedStream = new HashingStreamEx(sSource, false, null);
|
||||
|
||||
UTF8Encoding encNoBom = StrUtil.Utf8;
|
||||
try
|
||||
{
|
||||
BinaryReaderEx br = null;
|
||||
BinaryReaderEx brDecrypted = null;
|
||||
Stream readerStream = null;
|
||||
|
||||
if(kdbFormat == Kdb4Format.Default)
|
||||
{
|
||||
br = new BinaryReaderEx(hashedStream, encNoBom, KLRes.FileCorrupted);
|
||||
ReadHeader(br);
|
||||
|
||||
Stream sDecrypted = await AttachStreamDecryptor(hashedStream);
|
||||
if((sDecrypted == null) || (sDecrypted == hashedStream))
|
||||
throw new SecurityException(KLRes.CryptoStreamFailed);
|
||||
|
||||
brDecrypted = new BinaryReaderEx(sDecrypted, encNoBom, KLRes.FileCorrupted);
|
||||
byte[] pbStoredStartBytes = brDecrypted.ReadBytes(32);
|
||||
|
||||
if((m_pbStreamStartBytes == null) || (m_pbStreamStartBytes.Length != 32))
|
||||
throw new InvalidDataException();
|
||||
|
||||
for(int iStart = 0; iStart < 32; ++iStart)
|
||||
{
|
||||
if(pbStoredStartBytes[iStart] != m_pbStreamStartBytes[iStart])
|
||||
throw new InvalidCompositeKeyException();
|
||||
}
|
||||
|
||||
Stream sHashed = new HashedBlockStream(sDecrypted, false, 0,
|
||||
!m_bRepairMode);
|
||||
|
||||
if(m_pwDatabase.Compression == PwCompressionAlgorithm.GZip)
|
||||
readerStream = new GZipStream(sHashed, CompressionMode.Decompress);
|
||||
else readerStream = sHashed;
|
||||
}
|
||||
else if(kdbFormat == Kdb4Format.PlainXml)
|
||||
readerStream = hashedStream;
|
||||
else { Debug.Assert(false); throw new FormatException("KdbFormat"); }
|
||||
|
||||
if(kdbFormat != Kdb4Format.PlainXml) // Is an encrypted format
|
||||
{
|
||||
if(m_pbProtectedStreamKey == null)
|
||||
{
|
||||
Debug.Assert(false);
|
||||
throw new SecurityException("Invalid protected stream key!");
|
||||
}
|
||||
|
||||
m_randomStream = new CryptoRandomStream(m_craInnerRandomStream,
|
||||
m_pbProtectedStreamKey);
|
||||
}
|
||||
else m_randomStream = null; // No random stream for plain-text files
|
||||
|
||||
ReadXmlStreamed(readerStream, hashedStream);
|
||||
// ReadXmlDom(readerStream);
|
||||
|
||||
#if !KeePassWinRT
|
||||
readerStream.Close();
|
||||
#endif
|
||||
GC.KeepAlive(brDecrypted);
|
||||
GC.KeepAlive(br);
|
||||
}
|
||||
#if !KeePassWinRT
|
||||
catch(CryptographicException) // Thrown on invalid padding
|
||||
{
|
||||
throw new CryptographicException(KLRes.FileCorrupted);
|
||||
}
|
||||
#endif
|
||||
finally { CommonCleanUpRead(sSource, hashedStream); }
|
||||
|
||||
}
|
||||
|
||||
private void CommonCleanUpRead(Stream sSource, HashingStreamEx hashedStream)
|
||||
{
|
||||
m_pbHashOfFileOnDisk = hashedStream.Hash;
|
||||
// Reset memory protection settings (to always use reasonable
|
||||
// defaults)
|
||||
m_pwDatabase.MemoryProtection = new MemoryProtectionConfig();
|
||||
|
||||
return;
|
||||
|
||||
#if !KeePassWinRT
|
||||
// Remove old backups (this call is required here in order to apply
|
||||
// the default history maintenance settings for people upgrading from
|
||||
// KeePass <= 2.14 to >= 2.15; also it ensures history integrity in
|
||||
// case a different application has created the KDBX file and ignored
|
||||
// the history maintenance settings)
|
||||
m_pwDatabase.MaintainBackups(); // Don't mark database as modified
|
||||
|
||||
|
||||
// Is it ok to leave the stream open ????
|
||||
hashedStream.Close();
|
||||
sSource.Close();
|
||||
#endif
|
||||
}
|
||||
|
||||
private void ReadHeader(BinaryReaderEx br)
|
||||
{
|
||||
Debug.Assert(br != null);
|
||||
if(br == null) throw new ArgumentNullException("br");
|
||||
|
||||
byte[] pbSig1 = br.ReadBytes(4);
|
||||
uint uSig1 = MemUtil.BytesToUInt32(pbSig1);
|
||||
byte[] pbSig2 = br.ReadBytes(4);
|
||||
uint uSig2 = MemUtil.BytesToUInt32(pbSig2);
|
||||
|
||||
if((uSig1 == FileSignatureOld1) && (uSig2 == FileSignatureOld2))
|
||||
throw new OldFormatException(PwDefs.ShortProductName + @" 1.x",
|
||||
OldFormatException.OldFormatType.KeePass1x);
|
||||
|
||||
if((uSig1 == FileSignature1) && (uSig2 == FileSignature2)) { }
|
||||
else if((uSig1 == FileSignaturePreRelease1) && (uSig2 ==
|
||||
FileSignaturePreRelease2)) { }
|
||||
else throw new FormatException(KLRes.FileSigInvalid);
|
||||
|
||||
byte[] pb = br.ReadBytes(4);
|
||||
uint uVersion = MemUtil.BytesToUInt32(pb);
|
||||
|
||||
if ((uVersion & FileVersionCriticalMask) > (FileVersion32 & FileVersionCriticalMask))
|
||||
#if TODO
|
||||
throw new FormatException(KLRes.FileVersionUnsupported +
|
||||
MessageService.NewParagraph + KLRes.FileNewVerReq);
|
||||
#else
|
||||
throw new FormatException();
|
||||
#endif
|
||||
while(true)
|
||||
{
|
||||
if(ReadHeaderField(br) == false)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private bool ReadHeaderField(BinaryReaderEx brSource)
|
||||
{
|
||||
Debug.Assert(brSource != null);
|
||||
if(brSource == null) throw new ArgumentNullException("brSource");
|
||||
|
||||
byte btFieldID = brSource.ReadByte();
|
||||
ushort uSize = MemUtil.BytesToUInt16(brSource.ReadBytes(2));
|
||||
|
||||
byte[] pbData = null;
|
||||
if(uSize > 0)
|
||||
{
|
||||
string strPrevExcpText = brSource.ReadExceptionText;
|
||||
brSource.ReadExceptionText = KLRes.FileHeaderEndEarly;
|
||||
|
||||
pbData = brSource.ReadBytes(uSize);
|
||||
|
||||
brSource.ReadExceptionText = strPrevExcpText;
|
||||
}
|
||||
|
||||
bool bResult = true;
|
||||
Kdb4HeaderFieldID kdbID = (Kdb4HeaderFieldID)btFieldID;
|
||||
switch(kdbID)
|
||||
{
|
||||
case Kdb4HeaderFieldID.EndOfHeader:
|
||||
bResult = false; // Returning false indicates end of header
|
||||
break;
|
||||
|
||||
case Kdb4HeaderFieldID.CipherID:
|
||||
SetCipher(pbData);
|
||||
break;
|
||||
|
||||
case Kdb4HeaderFieldID.CompressionFlags:
|
||||
SetCompressionFlags(pbData);
|
||||
break;
|
||||
|
||||
case Kdb4HeaderFieldID.MasterSeed:
|
||||
m_pbMasterSeed = pbData;
|
||||
CryptoRandom.Instance.AddEntropy(pbData);
|
||||
break;
|
||||
|
||||
case Kdb4HeaderFieldID.TransformSeed:
|
||||
m_pbTransformSeed = pbData;
|
||||
CryptoRandom.Instance.AddEntropy(pbData);
|
||||
break;
|
||||
|
||||
case Kdb4HeaderFieldID.TransformRounds:
|
||||
m_pwDatabase.KeyEncryptionRounds = MemUtil.BytesToUInt64(pbData);
|
||||
break;
|
||||
|
||||
case Kdb4HeaderFieldID.EncryptionIV:
|
||||
m_pbEncryptionIV = pbData;
|
||||
break;
|
||||
|
||||
case Kdb4HeaderFieldID.ProtectedStreamKey:
|
||||
m_pbProtectedStreamKey = pbData;
|
||||
CryptoRandom.Instance.AddEntropy(pbData);
|
||||
break;
|
||||
|
||||
case Kdb4HeaderFieldID.StreamStartBytes:
|
||||
m_pbStreamStartBytes = pbData;
|
||||
break;
|
||||
|
||||
case Kdb4HeaderFieldID.InnerRandomStreamID:
|
||||
SetInnerRandomStreamID(pbData);
|
||||
break;
|
||||
|
||||
default:
|
||||
Debug.Assert(false);
|
||||
if(m_slLogger != null)
|
||||
m_slLogger.SetText(KLRes.UnknownHeaderId + @": " +
|
||||
kdbID.ToString() + "!", LogStatusType.Warning);
|
||||
break;
|
||||
}
|
||||
|
||||
return bResult;
|
||||
}
|
||||
|
||||
private void SetCipher(byte[] pbID)
|
||||
{
|
||||
if((pbID == null) || (pbID.Length != 16))
|
||||
throw new FormatException(KLRes.FileUnknownCipher);
|
||||
|
||||
m_pwDatabase.DataCipherUuid = new PwUuid(pbID);
|
||||
}
|
||||
|
||||
private void SetCompressionFlags(byte[] pbFlags)
|
||||
{
|
||||
int nID = (int)MemUtil.BytesToUInt32(pbFlags);
|
||||
if((nID < 0) || (nID >= (int)PwCompressionAlgorithm.Count))
|
||||
throw new FormatException(KLRes.FileUnknownCompression);
|
||||
|
||||
m_pwDatabase.Compression = (PwCompressionAlgorithm)nID;
|
||||
}
|
||||
|
||||
private void SetInnerRandomStreamID(byte[] pbID)
|
||||
{
|
||||
uint uID = MemUtil.BytesToUInt32(pbID);
|
||||
if(uID >= (uint)CrsAlgorithm.Count)
|
||||
throw new FormatException(KLRes.FileUnknownCipher);
|
||||
|
||||
m_craInnerRandomStream = (CrsAlgorithm)uID;
|
||||
}
|
||||
|
||||
private async Task<Stream> AttachStreamDecryptor(Stream s)
|
||||
{
|
||||
MemoryStream ms = new MemoryStream();
|
||||
|
||||
Debug.Assert(m_pbMasterSeed.Length == 32);
|
||||
if(m_pbMasterSeed.Length != 32)
|
||||
throw new FormatException(KLRes.MasterSeedLengthInvalid);
|
||||
ms.Write(m_pbMasterSeed, 0, 32);
|
||||
|
||||
Security.ProtectedBinary pb = await m_pwDatabase.MasterKey.GenerateKey32(m_pbTransformSeed,
|
||||
m_pwDatabase.KeyEncryptionRounds);
|
||||
|
||||
byte[] pKey32 = pb.ReadData();
|
||||
if((pKey32 == null) || (pKey32.Length != 32))
|
||||
throw new SecurityException(KLRes.InvalidCompositeKey);
|
||||
ms.Write(pKey32, 0, 32);
|
||||
|
||||
byte[] aesKey = SHA256Managed.Instance.ComputeHash(ms.ToArray());
|
||||
|
||||
//SHA256Managed sha256 = new SHA256Managed();
|
||||
//byte[] aesKey = sha256.ComputeHash(ms.ToArray());
|
||||
|
||||
//ms.Close();
|
||||
Array.Clear(pKey32, 0, 32);
|
||||
|
||||
if((aesKey == null) || (aesKey.Length != 32))
|
||||
throw new SecurityException(KLRes.FinalKeyCreationFailed);
|
||||
|
||||
ICipherEngine iEngine = CipherPool.GlobalPool.GetCipher(m_pwDatabase.DataCipherUuid);
|
||||
if(iEngine == null) throw new SecurityException(KLRes.FileUnknownCipher);
|
||||
return iEngine.DecryptStream(s, aesKey, m_pbEncryptionIV);
|
||||
|
||||
}
|
||||
|
||||
[Obsolete]
|
||||
public static List<PwEntry> ReadEntries(PwDatabase pwDatabase, Stream msData)
|
||||
{
|
||||
return ReadEntries(msData);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read entries from a stream.
|
||||
/// </summary>
|
||||
/// <param name="msData">Input stream to read the entries from.</param>
|
||||
/// <returns>Extracted entries.</returns>
|
||||
public static List<PwEntry> ReadEntries(Stream msData)
|
||||
{
|
||||
Debug.Assert(false, "not yet implemented");
|
||||
return null;
|
||||
#if TODO
|
||||
/* Kdb4File f = new Kdb4File(pwDatabase);
|
||||
f.m_format = Kdb4Format.PlainXml;
|
||||
|
||||
XmlDocument doc = new XmlDocument();
|
||||
doc.Load(msData);
|
||||
|
||||
XmlElement el = doc.DocumentElement;
|
||||
if(el.Name != ElemRoot) throw new FormatException();
|
||||
|
||||
List<PwEntry> vEntries = new List<PwEntry>();
|
||||
|
||||
foreach(XmlNode xmlChild in el.ChildNodes)
|
||||
{
|
||||
if(xmlChild.Name == ElemEntry)
|
||||
{
|
||||
PwEntry pe = f.ReadEntry(xmlChild);
|
||||
pe.Uuid = new PwUuid(true);
|
||||
|
||||
foreach(PwEntry peHistory in pe.History)
|
||||
peHistory.Uuid = pe.Uuid;
|
||||
|
||||
vEntries.Add(pe);
|
||||
}
|
||||
else { Debug.Assert(false); }
|
||||
}
|
||||
|
||||
return vEntries; */
|
||||
|
||||
PwDatabase pd = new PwDatabase();
|
||||
Kdb4File f = new Kdb4File(pd);
|
||||
f.Load(msData, Kdb4Format.PlainXml, null);
|
||||
|
||||
List<PwEntry> vEntries = new List<PwEntry>();
|
||||
foreach(PwEntry pe in pd.RootGroup.Entries)
|
||||
{
|
||||
pe.SetUuid(new PwUuid(true), true);
|
||||
vEntries.Add(pe);
|
||||
}
|
||||
|
||||
return vEntries;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
866
ModernKeePassLib/Serialization/Kdb4File.Write.cs
Normal file
866
ModernKeePassLib/Serialization/Kdb4File.Write.cs
Normal file
@@ -0,0 +1,866 @@
|
||||
/*
|
||||
KeePass Password Safe - The Open-Source Password Manager
|
||||
Copyright (C) 2003-2012 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.IO;
|
||||
//using System.Drawing;
|
||||
//using System.Drawing.Imaging;
|
||||
|
||||
#if !KeePassLibSD
|
||||
#else
|
||||
using ModernKeePassLibSD;
|
||||
#endif
|
||||
|
||||
using ModernKeePassLib.Collections;
|
||||
using ModernKeePassLib.Cryptography;
|
||||
using ModernKeePassLib.Interfaces;
|
||||
using ModernKeePassLib.Keys;
|
||||
using ModernKeePassLib.Security;
|
||||
using ModernKeePassLib.Utility;
|
||||
|
||||
namespace ModernKeePassLib.Serialization
|
||||
{
|
||||
/// <summary>
|
||||
/// Serialization to KeePass KDB files.
|
||||
/// </summary>
|
||||
public sealed partial class Kdb4File
|
||||
{
|
||||
// public void Save(string strFile, PwGroup pgDataSource, Kdb4Format format,
|
||||
// IStatusLogger slLogger)
|
||||
// {
|
||||
// bool bMadeUnhidden = UrlUtil.UnhideFile(strFile);
|
||||
//
|
||||
// IOConnectionInfo ioc = IOConnectionInfo.FromPath(strFile);
|
||||
// this.Save(IOConnection.OpenWrite(ioc), pgDataSource, format, slLogger);
|
||||
//
|
||||
// if(bMadeUnhidden) UrlUtil.HideFile(strFile, true); // Hide again
|
||||
// }
|
||||
|
||||
/// <summary>
|
||||
/// Save the contents of the current <c>PwDatabase</c> to a KDBX file.
|
||||
/// </summary>
|
||||
/// <param name="sSaveTo">Stream to write the KDBX file into.</param>
|
||||
/// <param name="pgDataSource">Group containing all groups and
|
||||
/// entries to write. If <c>null</c>, the complete database will
|
||||
/// be written.</param>
|
||||
/// <param name="format">Format of the file to create.</param>
|
||||
/// <param name="slLogger">Logger that recieves status information.</param>
|
||||
public void Save(Stream sSaveTo, PwGroup pgDataSource, Kdb4Format format,
|
||||
IStatusLogger slLogger)
|
||||
{
|
||||
Debug.Assert(false, "not yet implemented");
|
||||
return;
|
||||
#if TODO
|
||||
|
||||
Debug.Assert(sSaveTo != null);
|
||||
if(sSaveTo == null) throw new ArgumentNullException("sSaveTo");
|
||||
|
||||
m_format = format;
|
||||
m_slLogger = slLogger;
|
||||
|
||||
HashingStreamEx hashedStream = new HashingStreamEx(sSaveTo, true, null);
|
||||
|
||||
UTF8Encoding encNoBom = StrUtil.Utf8;
|
||||
CryptoRandom cr = CryptoRandom.Instance;
|
||||
|
||||
try
|
||||
{
|
||||
m_pbMasterSeed = cr.GetRandomBytes(32);
|
||||
m_pbTransformSeed = cr.GetRandomBytes(32);
|
||||
m_pbEncryptionIV = cr.GetRandomBytes(16);
|
||||
|
||||
m_pbProtectedStreamKey = cr.GetRandomBytes(32);
|
||||
m_craInnerRandomStream = CrsAlgorithm.Salsa20;
|
||||
m_randomStream = new CryptoRandomStream(m_craInnerRandomStream,
|
||||
m_pbProtectedStreamKey);
|
||||
|
||||
m_pbStreamStartBytes = cr.GetRandomBytes(32);
|
||||
|
||||
Stream writerStream;
|
||||
BinaryWriter bw = null;
|
||||
if(m_format == Kdb4Format.Default)
|
||||
{
|
||||
bw = new BinaryWriter(hashedStream, encNoBom);
|
||||
WriteHeader(bw); // Also flushes bw
|
||||
|
||||
Stream sEncrypted = AttachStreamEncryptor(hashedStream);
|
||||
if((sEncrypted == null) || (sEncrypted == hashedStream))
|
||||
throw new SecurityException(KLRes.CryptoStreamFailed);
|
||||
|
||||
sEncrypted.Write(m_pbStreamStartBytes, 0, m_pbStreamStartBytes.Length);
|
||||
|
||||
Stream sHashed = new HashedBlockStream(sEncrypted, true);
|
||||
|
||||
if(m_pwDatabase.Compression == PwCompressionAlgorithm.GZip)
|
||||
writerStream = new GZipStream(sHashed, CompressionMode.Compress);
|
||||
else
|
||||
writerStream = sHashed;
|
||||
}
|
||||
else if(m_format == Kdb4Format.PlainXml)
|
||||
writerStream = hashedStream;
|
||||
else { Debug.Assert(false); throw new FormatException("KdbFormat"); }
|
||||
|
||||
m_xmlWriter = new XmlTextWriter(writerStream, encNoBom);
|
||||
WriteDocument(pgDataSource);
|
||||
|
||||
m_xmlWriter.Flush();
|
||||
m_xmlWriter.Close();
|
||||
writerStream.Close();
|
||||
|
||||
GC.KeepAlive(bw);
|
||||
}
|
||||
finally { CommonCleanUpWrite(sSaveTo, hashedStream); }
|
||||
#endif
|
||||
}
|
||||
|
||||
private void CommonCleanUpWrite(Stream sSaveTo, HashingStreamEx hashedStream)
|
||||
{
|
||||
Debug.Assert(false, "not yet implemented");
|
||||
return;
|
||||
#if TODO
|
||||
hashedStream.Close();
|
||||
m_pbHashOfFileOnDisk = hashedStream.Hash;
|
||||
|
||||
sSaveTo.Close();
|
||||
|
||||
m_xmlWriter = null;
|
||||
#endif
|
||||
}
|
||||
|
||||
private void WriteHeader(BinaryWriter bw)
|
||||
{
|
||||
Debug.Assert(bw != null);
|
||||
if(bw == null) throw new ArgumentNullException("bw");
|
||||
|
||||
bw.Write(MemUtil.UInt32ToBytes(FileSignature1));
|
||||
bw.Write(MemUtil.UInt32ToBytes(FileSignature2));
|
||||
bw.Write(MemUtil.UInt32ToBytes(FileVersion32));
|
||||
|
||||
WriteHeaderField(bw, Kdb4HeaderFieldID.CipherID,
|
||||
m_pwDatabase.DataCipherUuid.UuidBytes);
|
||||
|
||||
int nCprID = (int)m_pwDatabase.Compression;
|
||||
WriteHeaderField(bw, Kdb4HeaderFieldID.CompressionFlags,
|
||||
MemUtil.UInt32ToBytes((uint)nCprID));
|
||||
|
||||
WriteHeaderField(bw, Kdb4HeaderFieldID.MasterSeed, m_pbMasterSeed);
|
||||
WriteHeaderField(bw, Kdb4HeaderFieldID.TransformSeed, m_pbTransformSeed);
|
||||
WriteHeaderField(bw, Kdb4HeaderFieldID.TransformRounds, MemUtil.UInt64ToBytes(m_pwDatabase.KeyEncryptionRounds));
|
||||
WriteHeaderField(bw, Kdb4HeaderFieldID.EncryptionIV, m_pbEncryptionIV);
|
||||
WriteHeaderField(bw, Kdb4HeaderFieldID.ProtectedStreamKey, m_pbProtectedStreamKey);
|
||||
WriteHeaderField(bw, Kdb4HeaderFieldID.StreamStartBytes, m_pbStreamStartBytes);
|
||||
|
||||
int nIrsID = (int)m_craInnerRandomStream;
|
||||
WriteHeaderField(bw, Kdb4HeaderFieldID.InnerRandomStreamID,
|
||||
MemUtil.UInt32ToBytes((uint)nIrsID));
|
||||
|
||||
WriteHeaderField(bw, Kdb4HeaderFieldID.EndOfHeader, new byte[]{ (byte)'\r', (byte)'\n', (byte)'\r', (byte)'\n' });
|
||||
bw.Flush();
|
||||
}
|
||||
|
||||
private static void WriteHeaderField(BinaryWriter bwOut,
|
||||
Kdb4HeaderFieldID kdbID, byte[] pbData)
|
||||
{
|
||||
Debug.Assert(bwOut != null);
|
||||
if(bwOut == null) throw new ArgumentNullException("bwOut");
|
||||
|
||||
bwOut.Write((byte)kdbID);
|
||||
|
||||
if(pbData != null)
|
||||
{
|
||||
ushort uLength = (ushort)pbData.Length;
|
||||
bwOut.Write(uLength);
|
||||
|
||||
if(uLength > 0) bwOut.Write(pbData);
|
||||
}
|
||||
else bwOut.Write((ushort)0);
|
||||
}
|
||||
|
||||
private Stream AttachStreamEncryptor(Stream s)
|
||||
{
|
||||
Debug.Assert(false, "not yet implemented");
|
||||
return null;
|
||||
#if TODO
|
||||
MemoryStream ms = new MemoryStream();
|
||||
|
||||
Debug.Assert(m_pbMasterSeed != null);
|
||||
Debug.Assert(m_pbMasterSeed.Length == 32);
|
||||
ms.Write(m_pbMasterSeed, 0, 32);
|
||||
|
||||
Debug.Assert(m_pwDatabase != null);
|
||||
Debug.Assert(m_pwDatabase.MasterKey != null);
|
||||
ProtectedBinary pbinKey = m_pwDatabase.MasterKey.GenerateKey32(
|
||||
m_pbTransformSeed, m_pwDatabase.KeyEncryptionRounds);
|
||||
Debug.Assert(pbinKey != null);
|
||||
if(pbinKey == null)
|
||||
throw new SecurityException(KLRes.InvalidCompositeKey);
|
||||
byte[] pKey32 = pbinKey.ReadData();
|
||||
if((pKey32 == null) || (pKey32.Length != 32))
|
||||
throw new SecurityException(KLRes.InvalidCompositeKey);
|
||||
ms.Write(pKey32, 0, 32);
|
||||
|
||||
SHA256Managed sha256 = new SHA256Managed();
|
||||
byte[] aesKey = sha256.ComputeHash(ms.ToArray());
|
||||
|
||||
ms.Close();
|
||||
Array.Clear(pKey32, 0, 32);
|
||||
|
||||
Debug.Assert(CipherPool.GlobalPool != null);
|
||||
ICipherEngine iEngine = CipherPool.GlobalPool.GetCipher(m_pwDatabase.DataCipherUuid);
|
||||
if(iEngine == null) throw new SecurityException(KLRes.FileUnknownCipher);
|
||||
return iEngine.EncryptStream(s, aesKey, m_pbEncryptionIV);
|
||||
#endif
|
||||
}
|
||||
|
||||
private void WriteDocument(PwGroup pgDataSource)
|
||||
{
|
||||
Debug.Assert(false, "not yet implemented");
|
||||
return;
|
||||
#if TODO
|
||||
Debug.Assert(m_xmlWriter != null);
|
||||
if(m_xmlWriter == null) throw new InvalidOperationException();
|
||||
|
||||
PwGroup pgRoot = (pgDataSource ?? m_pwDatabase.RootGroup);
|
||||
|
||||
uint uNumGroups, uNumEntries, uCurEntry = 0;
|
||||
pgRoot.GetCounts(true, out uNumGroups, out uNumEntries);
|
||||
|
||||
BinPoolBuild(pgRoot);
|
||||
|
||||
m_xmlWriter.Formatting = Formatting.Indented;
|
||||
m_xmlWriter.IndentChar = '\t';
|
||||
m_xmlWriter.Indentation = 1;
|
||||
|
||||
m_xmlWriter.WriteStartDocument(true);
|
||||
m_xmlWriter.WriteStartElement(ElemDocNode);
|
||||
|
||||
WriteMeta();
|
||||
|
||||
m_xmlWriter.WriteStartElement(ElemRoot);
|
||||
StartGroup(pgRoot);
|
||||
|
||||
Stack<PwGroup> groupStack = new Stack<PwGroup>();
|
||||
groupStack.Push(pgRoot);
|
||||
|
||||
GroupHandler gh = delegate(PwGroup pg)
|
||||
{
|
||||
Debug.Assert(pg != null);
|
||||
if(pg == null) throw new ArgumentNullException("pg");
|
||||
|
||||
while(true)
|
||||
{
|
||||
if(pg.ParentGroup == groupStack.Peek())
|
||||
{
|
||||
groupStack.Push(pg);
|
||||
StartGroup(pg);
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
groupStack.Pop();
|
||||
if(groupStack.Count <= 0) return false;
|
||||
|
||||
EndGroup();
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
EntryHandler eh = delegate(PwEntry pe)
|
||||
{
|
||||
Debug.Assert(pe != null);
|
||||
WriteEntry(pe, false);
|
||||
|
||||
++uCurEntry;
|
||||
if(m_slLogger != null)
|
||||
if(!m_slLogger.SetProgress((100 * uCurEntry) / uNumEntries))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
if(!pgRoot.TraverseTree(TraversalMethod.PreOrder, gh, eh))
|
||||
throw new InvalidOperationException();
|
||||
|
||||
while(groupStack.Count > 1)
|
||||
{
|
||||
m_xmlWriter.WriteEndElement();
|
||||
groupStack.Pop();
|
||||
}
|
||||
|
||||
EndGroup();
|
||||
|
||||
WriteList(ElemDeletedObjects, m_pwDatabase.DeletedObjects);
|
||||
m_xmlWriter.WriteEndElement(); // Root
|
||||
|
||||
m_xmlWriter.WriteEndElement(); // ElemDocNode
|
||||
m_xmlWriter.WriteEndDocument();
|
||||
#endif
|
||||
}
|
||||
|
||||
private void WriteMeta()
|
||||
{
|
||||
m_xmlWriter.WriteStartElement(ElemMeta);
|
||||
|
||||
WriteObject(ElemGenerator, PwDatabase.LocalizedAppName, false); // Generator name
|
||||
WriteObject(ElemDbName, m_pwDatabase.Name, true);
|
||||
WriteObject(ElemDbNameChanged, m_pwDatabase.NameChanged);
|
||||
WriteObject(ElemDbDesc, m_pwDatabase.Description, true);
|
||||
WriteObject(ElemDbDescChanged, m_pwDatabase.DescriptionChanged);
|
||||
WriteObject(ElemDbDefaultUser, m_pwDatabase.DefaultUserName, true);
|
||||
WriteObject(ElemDbDefaultUserChanged, m_pwDatabase.DefaultUserNameChanged);
|
||||
WriteObject(ElemDbMntncHistoryDays, m_pwDatabase.MaintenanceHistoryDays);
|
||||
WriteObject(ElemDbColor, StrUtil.ColorToUnnamedHtml(m_pwDatabase.Color, true), false);
|
||||
WriteObject(ElemDbKeyChanged, m_pwDatabase.MasterKeyChanged);
|
||||
WriteObject(ElemDbKeyChangeRec, m_pwDatabase.MasterKeyChangeRec);
|
||||
WriteObject(ElemDbKeyChangeForce, m_pwDatabase.MasterKeyChangeForce);
|
||||
|
||||
WriteList(ElemMemoryProt, m_pwDatabase.MemoryProtection);
|
||||
|
||||
WriteCustomIconList();
|
||||
|
||||
WriteObject(ElemRecycleBinEnabled, m_pwDatabase.RecycleBinEnabled);
|
||||
WriteObject(ElemRecycleBinUuid, m_pwDatabase.RecycleBinUuid);
|
||||
WriteObject(ElemRecycleBinChanged, m_pwDatabase.RecycleBinChanged);
|
||||
WriteObject(ElemEntryTemplatesGroup, m_pwDatabase.EntryTemplatesGroup);
|
||||
WriteObject(ElemEntryTemplatesGroupChanged, m_pwDatabase.EntryTemplatesGroupChanged);
|
||||
WriteObject(ElemHistoryMaxItems, m_pwDatabase.HistoryMaxItems);
|
||||
WriteObject(ElemHistoryMaxSize, m_pwDatabase.HistoryMaxSize);
|
||||
|
||||
WriteObject(ElemLastSelectedGroup, m_pwDatabase.LastSelectedGroup);
|
||||
WriteObject(ElemLastTopVisibleGroup, m_pwDatabase.LastTopVisibleGroup);
|
||||
|
||||
WriteBinPool();
|
||||
WriteList(ElemCustomData, m_pwDatabase.CustomData);
|
||||
|
||||
m_xmlWriter.WriteEndElement();
|
||||
}
|
||||
|
||||
private void StartGroup(PwGroup pg)
|
||||
{
|
||||
m_xmlWriter.WriteStartElement(ElemGroup);
|
||||
WriteObject(ElemUuid, pg.Uuid);
|
||||
WriteObject(ElemName, pg.Name, true);
|
||||
WriteObject(ElemNotes, pg.Notes, true);
|
||||
WriteObject(ElemIcon, (int)pg.IconId);
|
||||
|
||||
if(pg.CustomIconUuid != PwUuid.Zero)
|
||||
WriteObject(ElemCustomIconID, pg.CustomIconUuid);
|
||||
|
||||
WriteList(ElemTimes, pg);
|
||||
WriteObject(ElemIsExpanded, pg.IsExpanded);
|
||||
WriteObject(ElemGroupDefaultAutoTypeSeq, pg.DefaultAutoTypeSequence, true);
|
||||
WriteObject(ElemEnableAutoType, StrUtil.BoolToStringEx(pg.EnableAutoType), false);
|
||||
WriteObject(ElemEnableSearching, StrUtil.BoolToStringEx(pg.EnableSearching), false);
|
||||
WriteObject(ElemLastTopVisibleEntry, pg.LastTopVisibleEntry);
|
||||
}
|
||||
|
||||
private void EndGroup()
|
||||
{
|
||||
m_xmlWriter.WriteEndElement(); // Close group element
|
||||
}
|
||||
|
||||
private void WriteEntry(PwEntry pe, bool bIsHistory)
|
||||
{
|
||||
Debug.Assert(false, "not yet implemented");
|
||||
return;
|
||||
#if TODO
|
||||
Debug.Assert(pe != null); if(pe == null) throw new ArgumentNullException("pe");
|
||||
|
||||
m_xmlWriter.WriteStartElement(ElemEntry);
|
||||
|
||||
WriteObject(ElemUuid, pe.Uuid);
|
||||
WriteObject(ElemIcon, (int)pe.IconId);
|
||||
|
||||
if(pe.CustomIconUuid != PwUuid.Zero)
|
||||
WriteObject(ElemCustomIconID, pe.CustomIconUuid);
|
||||
|
||||
WriteObject(ElemFgColor, StrUtil.ColorToUnnamedHtml(pe.ForegroundColor, true), false);
|
||||
WriteObject(ElemBgColor, StrUtil.ColorToUnnamedHtml(pe.BackgroundColor, true), false);
|
||||
WriteObject(ElemOverrideUrl, pe.OverrideUrl, true);
|
||||
WriteObject(ElemTags, StrUtil.TagsToString(pe.Tags, false), true);
|
||||
|
||||
WriteList(ElemTimes, pe);
|
||||
|
||||
WriteList(pe.Strings, true);
|
||||
WriteList(pe.Binaries);
|
||||
WriteList(ElemAutoType, pe.AutoType);
|
||||
|
||||
if(!bIsHistory) WriteList(ElemHistory, pe.History, true);
|
||||
else { Debug.Assert(pe.History.UCount == 0); }
|
||||
|
||||
m_xmlWriter.WriteEndElement();
|
||||
#endif
|
||||
}
|
||||
|
||||
private void WriteList(ProtectedStringDictionary dictStrings, bool bEntryStrings)
|
||||
{
|
||||
Debug.Assert(dictStrings != null);
|
||||
if(dictStrings == null) throw new ArgumentNullException("dictStrings");
|
||||
|
||||
foreach(KeyValuePair<string, ProtectedString> kvp in dictStrings)
|
||||
WriteObject(kvp.Key, kvp.Value, bEntryStrings);
|
||||
}
|
||||
|
||||
private void WriteList(ProtectedBinaryDictionary dictBinaries)
|
||||
{
|
||||
Debug.Assert(dictBinaries != null);
|
||||
if(dictBinaries == null) throw new ArgumentNullException("dictBinaries");
|
||||
|
||||
foreach(KeyValuePair<string, ProtectedBinary> kvp in dictBinaries)
|
||||
WriteObject(kvp.Key, kvp.Value, true);
|
||||
}
|
||||
|
||||
private void WriteList(string name, AutoTypeConfig cfgAutoType)
|
||||
{
|
||||
Debug.Assert(name != null);
|
||||
Debug.Assert(cfgAutoType != null);
|
||||
if(cfgAutoType == null) throw new ArgumentNullException("cfgAutoType");
|
||||
|
||||
m_xmlWriter.WriteStartElement(name);
|
||||
|
||||
WriteObject(ElemAutoTypeEnabled, cfgAutoType.Enabled);
|
||||
WriteObject(ElemAutoTypeObfuscation, (int)cfgAutoType.ObfuscationOptions);
|
||||
|
||||
if(cfgAutoType.DefaultSequence.Length > 0)
|
||||
WriteObject(ElemAutoTypeDefaultSeq, cfgAutoType.DefaultSequence, true);
|
||||
|
||||
foreach(AutoTypeAssociation a in cfgAutoType.Associations)
|
||||
WriteObject(ElemAutoTypeItem, ElemWindow, ElemKeystrokeSequence,
|
||||
new KeyValuePair<string, string>(a.WindowName, a.Sequence));
|
||||
|
||||
m_xmlWriter.WriteEndElement();
|
||||
}
|
||||
|
||||
private void WriteList(string name, ITimeLogger times)
|
||||
{
|
||||
Debug.Assert(name != null);
|
||||
Debug.Assert(times != null); if(times == null) throw new ArgumentNullException("times");
|
||||
|
||||
m_xmlWriter.WriteStartElement(name);
|
||||
|
||||
WriteObject(ElemLastModTime, times.LastModificationTime);
|
||||
WriteObject(ElemCreationTime, times.CreationTime);
|
||||
WriteObject(ElemLastAccessTime, times.LastAccessTime);
|
||||
WriteObject(ElemExpiryTime, times.ExpiryTime);
|
||||
WriteObject(ElemExpires, times.Expires);
|
||||
WriteObject(ElemUsageCount, times.UsageCount);
|
||||
WriteObject(ElemLocationChanged, times.LocationChanged);
|
||||
|
||||
m_xmlWriter.WriteEndElement(); // Name
|
||||
}
|
||||
|
||||
private void WriteList(string name, PwObjectList<PwEntry> value, bool bIsHistory)
|
||||
{
|
||||
Debug.Assert(name != null);
|
||||
Debug.Assert(value != null); if(value == null) throw new ArgumentNullException("value");
|
||||
|
||||
m_xmlWriter.WriteStartElement(name);
|
||||
|
||||
foreach(PwEntry pe in value)
|
||||
WriteEntry(pe, bIsHistory);
|
||||
|
||||
m_xmlWriter.WriteEndElement();
|
||||
}
|
||||
|
||||
private void WriteList(string name, PwObjectList<PwDeletedObject> value)
|
||||
{
|
||||
Debug.Assert(name != null);
|
||||
Debug.Assert(value != null); if(value == null) throw new ArgumentNullException("value");
|
||||
|
||||
m_xmlWriter.WriteStartElement(name);
|
||||
|
||||
foreach(PwDeletedObject pdo in value)
|
||||
WriteObject(ElemDeletedObject, pdo);
|
||||
|
||||
m_xmlWriter.WriteEndElement();
|
||||
}
|
||||
|
||||
private void WriteList(string name, MemoryProtectionConfig value)
|
||||
{
|
||||
Debug.Assert(name != null);
|
||||
Debug.Assert(value != null);
|
||||
|
||||
m_xmlWriter.WriteStartElement(name);
|
||||
|
||||
WriteObject(ElemProtTitle, value.ProtectTitle);
|
||||
WriteObject(ElemProtUserName, value.ProtectUserName);
|
||||
WriteObject(ElemProtPassword, value.ProtectPassword);
|
||||
WriteObject(ElemProtUrl, value.ProtectUrl);
|
||||
WriteObject(ElemProtNotes, value.ProtectNotes);
|
||||
// WriteObject(ElemProtAutoHide, value.AutoEnableVisualHiding);
|
||||
|
||||
m_xmlWriter.WriteEndElement();
|
||||
}
|
||||
|
||||
private void WriteList(string name, StringDictionaryEx value)
|
||||
{
|
||||
Debug.Assert(name != null);
|
||||
Debug.Assert(value != null); if(value == null) throw new ArgumentNullException("value");
|
||||
|
||||
m_xmlWriter.WriteStartElement(name);
|
||||
|
||||
foreach(KeyValuePair<string, string> kvp in value)
|
||||
WriteObject(ElemStringDictExItem, ElemKey, ElemValue, kvp);
|
||||
|
||||
m_xmlWriter.WriteEndElement();
|
||||
}
|
||||
|
||||
private void WriteCustomIconList()
|
||||
{
|
||||
if(m_pwDatabase.CustomIcons.Count == 0) return;
|
||||
|
||||
m_xmlWriter.WriteStartElement(ElemCustomIcons);
|
||||
|
||||
foreach(PwCustomIcon pwci in m_pwDatabase.CustomIcons)
|
||||
{
|
||||
m_xmlWriter.WriteStartElement(ElemCustomIconItem);
|
||||
|
||||
WriteObject(ElemCustomIconItemID, pwci.Uuid);
|
||||
|
||||
string strData = Convert.ToBase64String(pwci.ImageDataPng);
|
||||
WriteObject(ElemCustomIconItemData, strData, false);
|
||||
|
||||
m_xmlWriter.WriteEndElement();
|
||||
}
|
||||
|
||||
m_xmlWriter.WriteEndElement();
|
||||
}
|
||||
|
||||
private void WriteObject(string name, string value,
|
||||
bool bFilterValueXmlChars)
|
||||
{
|
||||
Debug.Assert(name != null);
|
||||
Debug.Assert(value != null);
|
||||
|
||||
m_xmlWriter.WriteStartElement(name);
|
||||
|
||||
if(bFilterValueXmlChars)
|
||||
m_xmlWriter.WriteString(StrUtil.SafeXmlString(value));
|
||||
else m_xmlWriter.WriteString(value);
|
||||
|
||||
m_xmlWriter.WriteEndElement();
|
||||
}
|
||||
|
||||
private void WriteObject(string name, bool value)
|
||||
{
|
||||
Debug.Assert(name != null);
|
||||
|
||||
WriteObject(name, value ? ValTrue : ValFalse, false);
|
||||
}
|
||||
|
||||
private void WriteObject(string name, PwUuid value)
|
||||
{
|
||||
Debug.Assert(name != null);
|
||||
Debug.Assert(value != null); if(value == null) throw new ArgumentNullException("value");
|
||||
|
||||
WriteObject(name, Convert.ToBase64String(value.UuidBytes), false);
|
||||
}
|
||||
|
||||
private void WriteObject(string name, int value)
|
||||
{
|
||||
Debug.Assert(name != null);
|
||||
|
||||
m_xmlWriter.WriteStartElement(name);
|
||||
m_xmlWriter.WriteString(value.ToString());
|
||||
m_xmlWriter.WriteEndElement();
|
||||
}
|
||||
|
||||
private void WriteObject(string name, uint value)
|
||||
{
|
||||
Debug.Assert(name != null);
|
||||
|
||||
m_xmlWriter.WriteStartElement(name);
|
||||
m_xmlWriter.WriteString(value.ToString());
|
||||
m_xmlWriter.WriteEndElement();
|
||||
}
|
||||
|
||||
private void WriteObject(string name, long value)
|
||||
{
|
||||
Debug.Assert(name != null);
|
||||
|
||||
m_xmlWriter.WriteStartElement(name);
|
||||
m_xmlWriter.WriteString(value.ToString());
|
||||
m_xmlWriter.WriteEndElement();
|
||||
}
|
||||
|
||||
private void WriteObject(string name, ulong value)
|
||||
{
|
||||
Debug.Assert(name != null);
|
||||
|
||||
m_xmlWriter.WriteStartElement(name);
|
||||
m_xmlWriter.WriteString(value.ToString());
|
||||
m_xmlWriter.WriteEndElement();
|
||||
}
|
||||
|
||||
private void WriteObject(string name, DateTime value)
|
||||
{
|
||||
Debug.Assert(name != null);
|
||||
|
||||
WriteObject(name, TimeUtil.SerializeUtc(value), false);
|
||||
}
|
||||
|
||||
private void WriteObject(string name, string strKeyName,
|
||||
string strValueName, KeyValuePair<string, string> kvp)
|
||||
{
|
||||
m_xmlWriter.WriteStartElement(name);
|
||||
|
||||
m_xmlWriter.WriteStartElement(strKeyName);
|
||||
m_xmlWriter.WriteString(StrUtil.SafeXmlString(kvp.Key));
|
||||
m_xmlWriter.WriteEndElement();
|
||||
m_xmlWriter.WriteStartElement(strValueName);
|
||||
m_xmlWriter.WriteString(StrUtil.SafeXmlString(kvp.Value));
|
||||
m_xmlWriter.WriteEndElement();
|
||||
|
||||
m_xmlWriter.WriteEndElement();
|
||||
}
|
||||
|
||||
private void WriteObject(string name, ProtectedString value, bool bIsEntryString)
|
||||
{
|
||||
Debug.Assert(false, "not yet implemented");
|
||||
return;
|
||||
#if TODO
|
||||
|
||||
Debug.Assert(name != null);
|
||||
Debug.Assert(value != null); if(value == null) throw new ArgumentNullException("value");
|
||||
|
||||
m_xmlWriter.WriteStartElement(ElemString);
|
||||
m_xmlWriter.WriteStartElement(ElemKey);
|
||||
m_xmlWriter.WriteString(StrUtil.SafeXmlString(name));
|
||||
m_xmlWriter.WriteEndElement();
|
||||
m_xmlWriter.WriteStartElement(ElemValue);
|
||||
|
||||
bool bProtected = value.IsProtected;
|
||||
if(bIsEntryString)
|
||||
{
|
||||
// Adjust memory protection setting (which might be different
|
||||
// from the database default, e.g. due to an import which
|
||||
// didn't specify the correct setting)
|
||||
if(name == PwDefs.TitleField)
|
||||
bProtected = m_pwDatabase.MemoryProtection.ProtectTitle;
|
||||
else if(name == PwDefs.UserNameField)
|
||||
bProtected = m_pwDatabase.MemoryProtection.ProtectUserName;
|
||||
else if(name == PwDefs.PasswordField)
|
||||
bProtected = m_pwDatabase.MemoryProtection.ProtectPassword;
|
||||
else if(name == PwDefs.UrlField)
|
||||
bProtected = m_pwDatabase.MemoryProtection.ProtectUrl;
|
||||
else if(name == PwDefs.NotesField)
|
||||
bProtected = m_pwDatabase.MemoryProtection.ProtectNotes;
|
||||
}
|
||||
|
||||
if(bProtected && (m_format != Kdb4Format.PlainXml))
|
||||
{
|
||||
m_xmlWriter.WriteAttributeString(AttrProtected, ValTrue);
|
||||
|
||||
byte[] pbEncoded = value.ReadXorredString(m_randomStream);
|
||||
if(pbEncoded.Length > 0)
|
||||
m_xmlWriter.WriteBase64(pbEncoded, 0, pbEncoded.Length);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
string strValue = value.ReadString();
|
||||
|
||||
// If names should be localized, we need to apply the language-dependent
|
||||
// string transformation here. By default, language-dependent conversions
|
||||
// should be applied, otherwise characters could be rendered incorrectly
|
||||
// (code page problems).
|
||||
if(m_bLocalizedNames)
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
foreach(char ch in strValue)
|
||||
{
|
||||
char chMapped = ch;
|
||||
|
||||
// Symbols and surrogates must be moved into the correct code
|
||||
// page area
|
||||
if(char.IsSymbol(ch) || char.IsSurrogate(ch))
|
||||
{
|
||||
System.Globalization.UnicodeCategory cat = char.GetUnicodeCategory(ch);
|
||||
// Map character to correct position in code page
|
||||
chMapped = (char)((int)cat * 32 + ch);
|
||||
}
|
||||
else if(char.IsControl(ch))
|
||||
{
|
||||
if(ch >= 256) // Control character in high ANSI code page
|
||||
{
|
||||
// Some of the control characters map to corresponding ones
|
||||
// in the low ANSI range (up to 255) when calling
|
||||
// ToLower on them with invariant culture (see
|
||||
// http://lists.ximian.com/pipermail/mono-patches/2002-February/086106.html )
|
||||
chMapped = char.ToLower(ch, CultureInfo.InvariantCulture);
|
||||
}
|
||||
}
|
||||
|
||||
sb.Append(chMapped);
|
||||
}
|
||||
|
||||
strValue = sb.ToString(); // Correct string for current code page
|
||||
}
|
||||
|
||||
|
||||
if((m_format == Kdb4Format.PlainXml) && bProtected)
|
||||
m_xmlWriter.WriteAttributeString(AttrProtectedInMemPlainXml, ValTrue);
|
||||
|
||||
m_xmlWriter.WriteString(StrUtil.SafeXmlString(strValue));
|
||||
}
|
||||
|
||||
m_xmlWriter.WriteEndElement(); // ElemValue
|
||||
m_xmlWriter.WriteEndElement(); // ElemString
|
||||
#endif
|
||||
}
|
||||
|
||||
private void WriteObject(string name, ProtectedBinary value, bool bAllowRef)
|
||||
{
|
||||
Debug.Assert(name != null);
|
||||
Debug.Assert(value != null); if(value == null) throw new ArgumentNullException("value");
|
||||
|
||||
m_xmlWriter.WriteStartElement(ElemBinary);
|
||||
m_xmlWriter.WriteStartElement(ElemKey);
|
||||
m_xmlWriter.WriteString(StrUtil.SafeXmlString(name));
|
||||
m_xmlWriter.WriteEndElement();
|
||||
m_xmlWriter.WriteStartElement(ElemValue);
|
||||
|
||||
string strRef = (bAllowRef ? BinPoolFind(value) : null);
|
||||
if(strRef != null)
|
||||
{
|
||||
m_xmlWriter.WriteAttributeString(AttrRef, strRef);
|
||||
}
|
||||
else SubWriteValue(value);
|
||||
|
||||
m_xmlWriter.WriteEndElement(); // ElemValue
|
||||
m_xmlWriter.WriteEndElement(); // ElemBinary
|
||||
}
|
||||
|
||||
private void SubWriteValue(ProtectedBinary value)
|
||||
{
|
||||
|
||||
Debug.Assert(false, "not yet implemented");
|
||||
return ;
|
||||
#if TODO
|
||||
if(value.IsProtected && (m_format != Kdb4Format.PlainXml))
|
||||
{
|
||||
m_xmlWriter.WriteAttributeString(AttrProtected, ValTrue);
|
||||
|
||||
byte[] pbEncoded = value.ReadXorredData(m_randomStream);
|
||||
if(pbEncoded.Length > 0)
|
||||
m_xmlWriter.WriteBase64(pbEncoded, 0, pbEncoded.Length);
|
||||
}
|
||||
else
|
||||
{
|
||||
if(m_pwDatabase.Compression == PwCompressionAlgorithm.GZip)
|
||||
{
|
||||
m_xmlWriter.WriteAttributeString(AttrCompressed, ValTrue);
|
||||
|
||||
byte[] pbRaw = value.ReadData();
|
||||
byte[] pbCmp = MemUtil.Compress(pbRaw);
|
||||
m_xmlWriter.WriteBase64(pbCmp, 0, pbCmp.Length);
|
||||
}
|
||||
else
|
||||
{
|
||||
byte[] pbRaw = value.ReadData();
|
||||
m_xmlWriter.WriteBase64(pbRaw, 0, pbRaw.Length);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
private void WriteObject(string name, PwDeletedObject value)
|
||||
{
|
||||
Debug.Assert(name != null);
|
||||
Debug.Assert(value != null); if(value == null) throw new ArgumentNullException("value");
|
||||
|
||||
m_xmlWriter.WriteStartElement(name);
|
||||
WriteObject(ElemUuid, value.Uuid);
|
||||
WriteObject(ElemDeletionTime, value.DeletionTime);
|
||||
m_xmlWriter.WriteEndElement();
|
||||
}
|
||||
|
||||
private void WriteBinPool()
|
||||
{
|
||||
m_xmlWriter.WriteStartElement(ElemBinaries);
|
||||
|
||||
foreach(KeyValuePair<string, ProtectedBinary> kvp in m_dictBinPool)
|
||||
{
|
||||
m_xmlWriter.WriteStartElement(ElemBinary);
|
||||
m_xmlWriter.WriteAttributeString(AttrId, kvp.Key);
|
||||
SubWriteValue(kvp.Value);
|
||||
m_xmlWriter.WriteEndElement();
|
||||
}
|
||||
|
||||
m_xmlWriter.WriteEndElement();
|
||||
}
|
||||
|
||||
[Obsolete]
|
||||
public static bool WriteEntries(Stream msOutput, PwDatabase pwDatabase,
|
||||
PwEntry[] vEntries)
|
||||
{
|
||||
return WriteEntries(msOutput, vEntries);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write entries to a stream.
|
||||
/// </summary>
|
||||
/// <param name="msOutput">Output stream to which the entries will be written.</param>
|
||||
/// <param name="vEntries">Entries to serialize.</param>
|
||||
/// <returns>Returns <c>true</c>, if the entries were written successfully
|
||||
/// to the stream.</returns>
|
||||
public static bool WriteEntries(Stream msOutput, PwEntry[] vEntries)
|
||||
{
|
||||
/* Kdb4File f = new Kdb4File(pwDatabase);
|
||||
f.m_format = Kdb4Format.PlainXml;
|
||||
|
||||
XmlTextWriter xtw = null;
|
||||
try { xtw = new XmlTextWriter(msOutput, StrUtil.Utf8); }
|
||||
catch(Exception) { Debug.Assert(false); return false; }
|
||||
if(xtw == null) { Debug.Assert(false); return false; }
|
||||
|
||||
f.m_xmlWriter = xtw;
|
||||
|
||||
xtw.Formatting = Formatting.Indented;
|
||||
xtw.IndentChar = '\t';
|
||||
xtw.Indentation = 1;
|
||||
|
||||
xtw.WriteStartDocument(true);
|
||||
xtw.WriteStartElement(ElemRoot);
|
||||
|
||||
foreach(PwEntry pe in vEntries)
|
||||
f.WriteEntry(pe, false);
|
||||
|
||||
xtw.WriteEndElement();
|
||||
xtw.WriteEndDocument();
|
||||
|
||||
xtw.Flush();
|
||||
xtw.Close();
|
||||
return true; */
|
||||
|
||||
PwDatabase pd = new PwDatabase();
|
||||
pd.New(new IOConnectionInfo(), new CompositeKey());
|
||||
|
||||
foreach(PwEntry peCopy in vEntries)
|
||||
pd.RootGroup.AddEntry(peCopy.CloneDeep(), true);
|
||||
|
||||
Kdb4File f = new Kdb4File(pd);
|
||||
f.Save(msOutput, null, Kdb4Format.PlainXml, null);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
397
ModernKeePassLib/Serialization/Kdb4File.cs
Normal file
397
ModernKeePassLib/Serialization/Kdb4File.cs
Normal file
@@ -0,0 +1,397 @@
|
||||
/*
|
||||
KeePass Password Safe - The Open-Source Password Manager
|
||||
Copyright (C) 2003-2012 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.Xml;
|
||||
using System.Text;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Diagnostics;
|
||||
|
||||
#if !KeePassLibSD
|
||||
using System.IO.Compression;
|
||||
#endif
|
||||
|
||||
using ModernKeePassLib.Collections;
|
||||
using ModernKeePassLib.Cryptography;
|
||||
using ModernKeePassLib.Delegates;
|
||||
using ModernKeePassLib.Interfaces;
|
||||
using ModernKeePassLib.Security;
|
||||
using ModernKeePassLib.Utility;
|
||||
|
||||
namespace ModernKeePassLib.Serialization
|
||||
{
|
||||
/// <summary>
|
||||
/// The <c>Kdb4File</c> class supports saving the data to various
|
||||
/// formats.
|
||||
/// </summary>
|
||||
public enum Kdb4Format
|
||||
{
|
||||
/// <summary>
|
||||
/// The default, encrypted file format.
|
||||
/// </summary>
|
||||
Default = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Use this flag when exporting data to a plain-text XML file.
|
||||
/// </summary>
|
||||
PlainXml
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Serialization to KeePass KDB files.
|
||||
/// </summary>
|
||||
public sealed partial class Kdb4File
|
||||
{
|
||||
/// <summary>
|
||||
/// File identifier, first 32-bit value.
|
||||
/// </summary>
|
||||
private const uint FileSignature1 = 0x9AA2D903;
|
||||
|
||||
/// <summary>
|
||||
/// File identifier, second 32-bit value.
|
||||
/// </summary>
|
||||
private const uint FileSignature2 = 0xB54BFB67;
|
||||
|
||||
/// <summary>
|
||||
/// File version of files saved by the current <c>Kdb4File</c> class.
|
||||
/// KeePass 2.07 has version 1.01, 2.08 has 1.02, 2.09 has 2.00,
|
||||
/// 2.10 has 2.02, 2.11 has 2.04, 2.15 has 3.00.
|
||||
/// The first 2 bytes are critical (i.e. loading will fail, if the
|
||||
/// file version is too high), the last 2 bytes are informational.
|
||||
/// </summary>
|
||||
private const uint FileVersion32 = 0x00030000;
|
||||
|
||||
private const uint FileVersionCriticalMask = 0xFFFF0000;
|
||||
|
||||
// KeePass 1.x signature
|
||||
private const uint FileSignatureOld1 = 0x9AA2D903;
|
||||
private const uint FileSignatureOld2 = 0xB54BFB65;
|
||||
// KeePass 2.x pre-release (alpha and beta) signature
|
||||
private const uint FileSignaturePreRelease1 = 0x9AA2D903;
|
||||
private const uint FileSignaturePreRelease2 = 0xB54BFB66;
|
||||
|
||||
private const string ElemDocNode = "KeePassFile";
|
||||
private const string ElemMeta = "Meta";
|
||||
private const string ElemRoot = "Root";
|
||||
private const string ElemGroup = "Group";
|
||||
private const string ElemEntry = "Entry";
|
||||
|
||||
private const string ElemGenerator = "Generator";
|
||||
private const string ElemDbName = "DatabaseName";
|
||||
private const string ElemDbNameChanged = "DatabaseNameChanged";
|
||||
private const string ElemDbDesc = "DatabaseDescription";
|
||||
private const string ElemDbDescChanged = "DatabaseDescriptionChanged";
|
||||
private const string ElemDbDefaultUser = "DefaultUserName";
|
||||
private const string ElemDbDefaultUserChanged = "DefaultUserNameChanged";
|
||||
private const string ElemDbMntncHistoryDays = "MaintenanceHistoryDays";
|
||||
private const string ElemDbColor = "Color";
|
||||
private const string ElemDbKeyChanged = "MasterKeyChanged";
|
||||
private const string ElemDbKeyChangeRec = "MasterKeyChangeRec";
|
||||
private const string ElemDbKeyChangeForce = "MasterKeyChangeForce";
|
||||
private const string ElemRecycleBinEnabled = "RecycleBinEnabled";
|
||||
private const string ElemRecycleBinUuid = "RecycleBinUUID";
|
||||
private const string ElemRecycleBinChanged = "RecycleBinChanged";
|
||||
private const string ElemEntryTemplatesGroup = "EntryTemplatesGroup";
|
||||
private const string ElemEntryTemplatesGroupChanged = "EntryTemplatesGroupChanged";
|
||||
private const string ElemHistoryMaxItems = "HistoryMaxItems";
|
||||
private const string ElemHistoryMaxSize = "HistoryMaxSize";
|
||||
private const string ElemLastSelectedGroup = "LastSelectedGroup";
|
||||
private const string ElemLastTopVisibleGroup = "LastTopVisibleGroup";
|
||||
|
||||
private const string ElemMemoryProt = "MemoryProtection";
|
||||
private const string ElemProtTitle = "ProtectTitle";
|
||||
private const string ElemProtUserName = "ProtectUserName";
|
||||
private const string ElemProtPassword = "ProtectPassword";
|
||||
private const string ElemProtUrl = "ProtectURL";
|
||||
private const string ElemProtNotes = "ProtectNotes";
|
||||
// private const string ElemProtAutoHide = "AutoEnableVisualHiding";
|
||||
|
||||
private const string ElemCustomIcons = "CustomIcons";
|
||||
private const string ElemCustomIconItem = "Icon";
|
||||
private const string ElemCustomIconItemID = "UUID";
|
||||
private const string ElemCustomIconItemData = "Data";
|
||||
|
||||
private const string ElemAutoType = "AutoType";
|
||||
private const string ElemHistory = "History";
|
||||
|
||||
private const string ElemName = "Name";
|
||||
private const string ElemNotes = "Notes";
|
||||
private const string ElemUuid = "UUID";
|
||||
private const string ElemIcon = "IconID";
|
||||
private const string ElemCustomIconID = "CustomIconUUID";
|
||||
private const string ElemFgColor = "ForegroundColor";
|
||||
private const string ElemBgColor = "BackgroundColor";
|
||||
private const string ElemOverrideUrl = "OverrideURL";
|
||||
private const string ElemTimes = "Times";
|
||||
private const string ElemTags = "Tags";
|
||||
|
||||
private const string ElemCreationTime = "CreationTime";
|
||||
private const string ElemLastModTime = "LastModificationTime";
|
||||
private const string ElemLastAccessTime = "LastAccessTime";
|
||||
private const string ElemExpiryTime = "ExpiryTime";
|
||||
private const string ElemExpires = "Expires";
|
||||
private const string ElemUsageCount = "UsageCount";
|
||||
private const string ElemLocationChanged = "LocationChanged";
|
||||
|
||||
private const string ElemGroupDefaultAutoTypeSeq = "DefaultAutoTypeSequence";
|
||||
private const string ElemEnableAutoType = "EnableAutoType";
|
||||
private const string ElemEnableSearching = "EnableSearching";
|
||||
|
||||
private const string ElemString = "String";
|
||||
private const string ElemBinary = "Binary";
|
||||
private const string ElemKey = "Key";
|
||||
private const string ElemValue = "Value";
|
||||
|
||||
private const string ElemAutoTypeEnabled = "Enabled";
|
||||
private const string ElemAutoTypeObfuscation = "DataTransferObfuscation";
|
||||
private const string ElemAutoTypeDefaultSeq = "DefaultSequence";
|
||||
private const string ElemAutoTypeItem = "Association";
|
||||
private const string ElemWindow = "Window";
|
||||
private const string ElemKeystrokeSequence = "KeystrokeSequence";
|
||||
|
||||
private const string ElemBinaries = "Binaries";
|
||||
|
||||
private const string AttrId = "ID";
|
||||
private const string AttrRef = "Ref";
|
||||
private const string AttrProtected = "Protected";
|
||||
private const string AttrProtectedInMemPlainXml = "ProtectInMemory";
|
||||
private const string AttrCompressed = "Compressed";
|
||||
|
||||
private const string ElemIsExpanded = "IsExpanded";
|
||||
private const string ElemLastTopVisibleEntry = "LastTopVisibleEntry";
|
||||
|
||||
private const string ElemDeletedObjects = "DeletedObjects";
|
||||
private const string ElemDeletedObject = "DeletedObject";
|
||||
private const string ElemDeletionTime = "DeletionTime";
|
||||
|
||||
private const string ValFalse = "False";
|
||||
private const string ValTrue = "True";
|
||||
|
||||
private const string ElemCustomData = "CustomData";
|
||||
private const string ElemStringDictExItem = "Item";
|
||||
|
||||
private PwDatabase m_pwDatabase; // Not null, see constructor
|
||||
|
||||
private XmlWriter m_xmlWriter = null;
|
||||
private CryptoRandomStream m_randomStream = null;
|
||||
private Kdb4Format m_format = Kdb4Format.Default;
|
||||
private IStatusLogger m_slLogger = null;
|
||||
|
||||
private byte[] m_pbMasterSeed = null;
|
||||
private byte[] m_pbTransformSeed = null;
|
||||
private byte[] m_pbEncryptionIV = null;
|
||||
private byte[] m_pbProtectedStreamKey = null;
|
||||
private byte[] m_pbStreamStartBytes = null;
|
||||
|
||||
// ArcFourVariant only for compatibility; KeePass will default to a
|
||||
// different (more secure) algorithm when *writing* databases
|
||||
private CrsAlgorithm m_craInnerRandomStream = CrsAlgorithm.ArcFourVariant;
|
||||
|
||||
private Dictionary<string, ProtectedBinary> m_dictBinPool =
|
||||
new Dictionary<string, ProtectedBinary>();
|
||||
|
||||
private byte[] m_pbHashOfFileOnDisk = null;
|
||||
|
||||
private readonly DateTime m_dtNow = DateTime.Now; // Cache current time
|
||||
|
||||
private const uint NeutralLanguageOffset = 0x100000; // 2^20, see 32-bit Unicode specs
|
||||
private const uint NeutralLanguageIDSec = 0x7DC5C; // See 32-bit Unicode specs
|
||||
private const uint NeutralLanguageID = NeutralLanguageOffset + NeutralLanguageIDSec;
|
||||
private static bool m_bLocalizedNames = false;
|
||||
|
||||
private enum Kdb4HeaderFieldID : byte
|
||||
{
|
||||
EndOfHeader = 0,
|
||||
Comment = 1,
|
||||
CipherID = 2,
|
||||
CompressionFlags = 3,
|
||||
MasterSeed = 4,
|
||||
TransformSeed = 5,
|
||||
TransformRounds = 6,
|
||||
EncryptionIV = 7,
|
||||
ProtectedStreamKey = 8,
|
||||
StreamStartBytes = 9,
|
||||
InnerRandomStreamID = 10
|
||||
}
|
||||
|
||||
public byte[] HashOfFileOnDisk
|
||||
{
|
||||
get { return m_pbHashOfFileOnDisk; }
|
||||
}
|
||||
|
||||
private bool m_bRepairMode = false;
|
||||
public bool RepairMode
|
||||
{
|
||||
get { return m_bRepairMode; }
|
||||
set { m_bRepairMode = value; }
|
||||
}
|
||||
|
||||
private string m_strDetachBins = null;
|
||||
/// <summary>
|
||||
/// Detach binaries when opening a file. If this isn't <c>null</c>,
|
||||
/// all binaries are saved to the specified path and are removed
|
||||
/// from the database.
|
||||
/// </summary>
|
||||
public string DetachBinaries
|
||||
{
|
||||
get { return m_strDetachBins; }
|
||||
set { m_strDetachBins = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Default constructor.
|
||||
/// </summary>
|
||||
/// <param name="pwDataStore">The <c>PwDatabase</c> instance that the
|
||||
/// class will load file data into or use to create a KDBX file.</param>
|
||||
public Kdb4File(PwDatabase pwDataStore)
|
||||
{
|
||||
Debug.Assert(pwDataStore != null);
|
||||
if(pwDataStore == null) throw new ArgumentNullException("pwDataStore");
|
||||
|
||||
m_pwDatabase = pwDataStore;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Call this once to determine the current localization settings.
|
||||
/// </summary>
|
||||
public static void DetermineLanguageId()
|
||||
{
|
||||
// Test if localized names should be used. If localized names are used,
|
||||
// the m_bLocalizedNames value must be set to true. By default, localized
|
||||
// names should be used! (Otherwise characters could be corrupted
|
||||
// because of different code pages).
|
||||
unchecked
|
||||
{
|
||||
uint uTest = 0;
|
||||
foreach(char ch in PwDatabase.LocalizedAppName)
|
||||
uTest = uTest * 5 + ch;
|
||||
|
||||
m_bLocalizedNames = (uTest != NeutralLanguageID);
|
||||
}
|
||||
}
|
||||
|
||||
private void BinPoolBuild(PwGroup pgDataSource)
|
||||
{
|
||||
m_dictBinPool = new Dictionary<string, ProtectedBinary>();
|
||||
|
||||
if(pgDataSource == null) { Debug.Assert(false); return; }
|
||||
|
||||
EntryHandler eh = delegate(PwEntry pe)
|
||||
{
|
||||
foreach(PwEntry peHistory in pe.History)
|
||||
{
|
||||
BinPoolAdd(peHistory.Binaries);
|
||||
}
|
||||
|
||||
BinPoolAdd(pe.Binaries);
|
||||
return true;
|
||||
};
|
||||
|
||||
pgDataSource.TraverseTree(TraversalMethod.PreOrder, null, eh);
|
||||
}
|
||||
|
||||
private void BinPoolAdd(ProtectedBinaryDictionary dict)
|
||||
{
|
||||
foreach(KeyValuePair<string, ProtectedBinary> kvp in dict)
|
||||
{
|
||||
BinPoolAdd(kvp.Value);
|
||||
}
|
||||
}
|
||||
|
||||
private void BinPoolAdd(ProtectedBinary pb)
|
||||
{
|
||||
if(pb == null) { Debug.Assert(false); return; }
|
||||
|
||||
if(BinPoolFind(pb) != null) return; // Exists already
|
||||
|
||||
m_dictBinPool.Add(m_dictBinPool.Count.ToString(), pb);
|
||||
}
|
||||
|
||||
private string BinPoolFind(ProtectedBinary pb)
|
||||
{
|
||||
if(pb == null) { Debug.Assert(false); return null; }
|
||||
|
||||
foreach(KeyValuePair<string, ProtectedBinary> kvp in m_dictBinPool)
|
||||
{
|
||||
if(pb.Equals(kvp.Value)) return kvp.Key;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private ProtectedBinary BinPoolGet(string strKey)
|
||||
{
|
||||
if(strKey == null) { Debug.Assert(false); return null; }
|
||||
|
||||
ProtectedBinary pb;
|
||||
if(m_dictBinPool.TryGetValue(strKey, out pb)) return pb;
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static void SaveBinary(string strName, ProtectedBinary pb,
|
||||
string strSaveDir)
|
||||
{
|
||||
// TODO Bert: Needs to be implemented.
|
||||
#if !TODO
|
||||
Debug.Assert(false,"not implemented");
|
||||
#else
|
||||
|
||||
|
||||
if(pb == null) { Debug.Assert(false); return; }
|
||||
|
||||
if(string.IsNullOrEmpty(strName)) strName = "File.bin";
|
||||
|
||||
string strPath;
|
||||
int iTry = 1;
|
||||
do
|
||||
{
|
||||
strPath = UrlUtil.EnsureTerminatingSeparator(strSaveDir, false);
|
||||
|
||||
string strExt = UrlUtil.GetExtension(strName);
|
||||
string strDesc = UrlUtil.StripExtension(strName);
|
||||
|
||||
strPath += strDesc;
|
||||
if(iTry > 1) strPath += " (" + iTry.ToString() + ")";
|
||||
|
||||
if(!string.IsNullOrEmpty(strExt)) strPath += "." + strExt;
|
||||
|
||||
++iTry;
|
||||
}
|
||||
while(File.Exists(strPath));
|
||||
|
||||
#if !KeePassLibSD
|
||||
byte[] pbData = pb.ReadData();
|
||||
File.WriteAllBytes(strPath, pbData);
|
||||
MemUtil.ZeroByteArray(pbData);
|
||||
#else
|
||||
FileStream fs = new FileStream(strPath, FileMode.Create,
|
||||
FileAccess.Write, FileShare.None);
|
||||
byte[] pbData = pb.ReadData();
|
||||
fs.Write(pbData, 0, pbData.Length);
|
||||
fs.Close();
|
||||
#endif
|
||||
|
||||
#endif // TODO
|
||||
}
|
||||
|
||||
}
|
||||
}
|
70
ModernKeePassLib/Serialization/OldFormatException.cs
Normal file
70
ModernKeePassLib/Serialization/OldFormatException.cs
Normal file
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
KeePass Password Safe - The Open-Source Password Manager
|
||||
Copyright (C) 2003-2012 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.Text;
|
||||
using ModernKeePassLib.Resources;
|
||||
using ModernKeePassLib.Utility;
|
||||
|
||||
namespace ModernKeePassLib.Serialization
|
||||
{
|
||||
public sealed class OldFormatException : Exception
|
||||
{
|
||||
private string m_strFormat = string.Empty;
|
||||
private OldFormatType m_type = OldFormatType.Unknown;
|
||||
|
||||
public enum OldFormatType
|
||||
{
|
||||
Unknown = 0,
|
||||
KeePass1x = 1
|
||||
}
|
||||
|
||||
public override string Message
|
||||
{
|
||||
get
|
||||
{
|
||||
Debug.Assert(false, "not yet implemented");
|
||||
return null;
|
||||
#if TODO
|
||||
string str = KLRes.OldFormat + ((m_strFormat.Length > 0) ?
|
||||
(@" (" + m_strFormat + @")") : string.Empty) + ".";
|
||||
|
||||
if(m_type == OldFormatType.KeePass1x)
|
||||
str += MessageService.NewParagraph + KLRes.KeePass1xHint;
|
||||
|
||||
return str;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
public OldFormatException(string strFormatName)
|
||||
{
|
||||
if(strFormatName != null) m_strFormat = strFormatName;
|
||||
}
|
||||
|
||||
public OldFormatException(string strFormatName, OldFormatType t)
|
||||
{
|
||||
if(strFormatName != null) m_strFormat = strFormatName;
|
||||
|
||||
m_type = t;
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user