keepass2android/src/KeePassLib2Android/Serialization/KdbxFile.Read.Streamed.cs

923 lines
27 KiB
C#

/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2012 Dominik Reichl <dominik.reichl@t-online.de>
Modified to be used with Mono for Android. Changes Copyright (C) 2013 Philipp Crocoll
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.Security;
using System.Security.Cryptography;
using System.Drawing;
using System.Xml;
using System.IO;
using System.Diagnostics;
using KeePassLib;
using KeePassLib.Collections;
using KeePassLib.Cryptography;
using KeePassLib.Cryptography.Cipher;
using KeePassLib.Interfaces;
using KeePassLib.Resources;
using KeePassLib.Security;
using KeePassLib.Utility;
namespace KeePassLib.Serialization
{
/// <summary>
/// Serialization to KeePass KDBX files.
/// </summary>
public sealed partial class KdbxFile
{
private class ColorTranslator
{
public static Color FromHtml(String colorString)
{
Color color;
if (colorString.StartsWith("#"))
{
colorString = colorString.Substring(1);
}
if (colorString.EndsWith(";"))
{
colorString = colorString.Substring(0, colorString.Length - 1);
}
int red, green, blue;
switch (colorString.Length)
{
case 6:
red = int.Parse(colorString.Substring(0, 2), System.Globalization.NumberStyles.HexNumber);
green = int.Parse(colorString.Substring(2, 2), System.Globalization.NumberStyles.HexNumber);
blue = int.Parse(colorString.Substring(4, 2), System.Globalization.NumberStyles.HexNumber);
color = Color.FromArgb(red, green, blue);
break;
case 3:
red = int.Parse(colorString.Substring(0, 1), System.Globalization.NumberStyles.HexNumber);
green = int.Parse(colorString.Substring(1, 1), System.Globalization.NumberStyles.HexNumber);
blue = int.Parse(colorString.Substring(2, 1), System.Globalization.NumberStyles.HexNumber);
color = Color.FromArgb(red, green, blue);
break;
case 1:
red = green = blue = int.Parse(colorString.Substring(0, 1), System.Globalization.NumberStyles.HexNumber);
color = Color.FromArgb(red, green, blue);
break;
default:
throw new ArgumentException("Invalid color: " + colorString);
}
return color;
}
}
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
xrs.ProhibitDtd = true;
#endif
xrs.ValidationType = ValidationType.None;
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 == ElemHeaderHash)
{
string strHash = ReadString(xr);
if(!string.IsNullOrEmpty(strHash) && (m_pbHashOfHeader != null) &&
!m_bRepairMode)
{
byte[] pbHash = Convert.FromBase64String(strHash);
if(!MemUtil.ArraysEqual(pbHash, m_pbHashOfHeader))
throw new IOException(KLRes.FileCorrupted);
}
}
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(!string.IsNullOrEmpty(strColor))
m_pwDatabase.Color = ColorTranslator.FromHtml(strColor);
}
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(!string.IsNullOrEmpty(strColor))
m_ctxEntry.ForegroundColor = ColorTranslator.FromHtml(strColor);
}
else if(xr.Name == ElemBgColor)
{
string strColor = ReadString(xr);
if(!string.IsNullOrEmpty(strColor))
m_ctxEntry.BackgroundColor = ColorTranslator.FromHtml(strColor);
}
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.ReadElementString();
}
private string ReadStringRaw(XmlReader xr)
{
m_bReadNextNode = false; // ReadElementString skips end tag
return xr.ReadElementString();
}
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 == KdbxFormat.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;
}
}
}