2013-02-23 08:24:43 -05:00
/ *
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.RegularExpressions ;
using KeePassLib.Collections ;
using KeePassLib.Delegates ;
using KeePassLib.Interfaces ;
using KeePassLib.Security ;
using KeePassLib.Utility ;
namespace KeePassLib
{
/// <summary>
/// A group containing several password entries.
/// </summary>
public sealed class PwGroup : ITimeLogger , IStructureItem , IDeepCloneable < PwGroup >
{
2013-06-19 13:44:35 -04:00
private const int SearchContextStringMaxLength = 50 ; // Note, doesn't include elipsis, if added
public const string SearchContextUuid = "Uuid" ;
public const string SearchContextParentGroup = "Parent Group" ;
public const string SearchContextTags = "Tags" ;
2013-02-23 08:24:43 -05:00
public const bool DefaultAutoTypeEnabled = true ;
public const bool DefaultSearchingEnabled = true ;
private PwObjectList < PwGroup > m_listGroups = new PwObjectList < PwGroup > ( ) ;
private PwObjectList < PwEntry > m_listEntries = new PwObjectList < PwEntry > ( ) ;
private PwGroup m_pParentGroup = null ;
private DateTime m_tParentGroupLastMod = PwDefs . DtDefaultNow ;
2013-06-15 15:12:46 -04:00
private string m_tParentGroupLastModLazy ;
2013-02-23 08:24:43 -05:00
private PwUuid m_uuid = PwUuid . Zero ;
private string m_strName = string . Empty ;
private string m_strNotes = string . Empty ;
private PwIcon m_pwIcon = PwIcon . Folder ;
private PwUuid m_pwCustomIconID = PwUuid . Zero ;
private DateTime m_tCreation = PwDefs . DtDefaultNow ;
private DateTime m_tLastMod = PwDefs . DtDefaultNow ;
private DateTime m_tLastAccess = PwDefs . DtDefaultNow ;
private DateTime m_tExpire = PwDefs . DtDefaultNow ;
2013-06-15 15:12:46 -04:00
private string m_tCreationLazy ;
private string m_tLastModLazy ;
private string m_tLastAccessLazy ;
private string m_tExpireLazy ;
2013-02-23 08:24:43 -05:00
private bool m_bExpires = false ;
private ulong m_uUsageCount = 0 ;
private bool m_bIsExpanded = true ;
private bool m_bVirtual = false ;
private string m_strDefaultAutoTypeSequence = string . Empty ;
private bool? m_bEnableAutoType = null ;
private bool? m_bEnableSearching = null ;
private PwUuid m_pwLastTopVisibleEntry = PwUuid . Zero ;
/// <summary>
/// UUID of this group.
/// </summary>
public PwUuid Uuid
{
get { return m_uuid ; }
set
{
Debug . Assert ( value ! = null ) ; if ( value = = null ) throw new ArgumentNullException ( "value" ) ;
m_uuid = value ;
}
}
/// <summary>
/// The name of this group. Cannot be <c>null</c>.
/// </summary>
public string Name
{
get { return m_strName ; }
set
{
Debug . Assert ( value ! = null ) ; if ( value = = null ) throw new ArgumentNullException ( "value" ) ;
m_strName = value ;
}
}
/// <summary>
/// Comments about this group. Cannot be <c>null</c>.
/// </summary>
public string Notes
{
get { return m_strNotes ; }
set
{
Debug . Assert ( value ! = null ) ; if ( value = = null ) throw new ArgumentNullException ( "value" ) ;
m_strNotes = value ;
}
}
/// <summary>
/// Icon of the group.
/// </summary>
public PwIcon IconId
{
get { return m_pwIcon ; }
set { m_pwIcon = value ; }
}
/// <summary>
/// Get the custom icon ID. This value is 0, if no custom icon is
/// being used (i.e. the icon specified by the <c>IconID</c> property
/// should be displayed).
/// </summary>
public PwUuid CustomIconUuid
{
get { return m_pwCustomIconID ; }
set
{
Debug . Assert ( value ! = null ) ; if ( value = = null ) throw new ArgumentNullException ( "value" ) ;
m_pwCustomIconID = value ;
}
}
/// <summary>
/// Reference to the group to which this group belongs. May be <c>null</c>.
/// </summary>
public PwGroup ParentGroup
{
get { return m_pParentGroup ; }
/// Plugins: use <c>PwGroup.AddGroup</c> instead.
internal set { Debug . Assert ( value ! = this ) ; m_pParentGroup = value ; }
}
/// <summary>
/// The date/time when the location of the object was last changed.
/// </summary>
public DateTime LocationChanged
{
2013-06-15 15:12:46 -04:00
get { return GetLazyTime ( ref m_tParentGroupLastModLazy , ref m_tParentGroupLastMod ) ; }
set { m_tParentGroupLastMod = value ; m_tParentGroupLastModLazy = null ; }
}
public void SetLazyLocationChanged ( string xmlDateTime )
{
m_tParentGroupLastModLazy = xmlDateTime ;
2013-02-23 08:24:43 -05:00
}
/// <summary>
/// A flag that specifies if the group is shown as expanded or
/// collapsed in the user interface.
/// </summary>
public bool IsExpanded
{
get { return m_bIsExpanded ; }
set { m_bIsExpanded = value ; }
}
/// <summary>
/// The date/time when this group was created.
/// </summary>
public DateTime CreationTime
{
2013-06-15 15:12:46 -04:00
get { return GetLazyTime ( ref m_tCreationLazy , ref m_tCreation ) ; }
set { m_tCreation = value ; m_tCreationLazy = null ; }
2013-02-23 08:24:43 -05:00
}
2013-06-15 15:12:46 -04:00
public void SetLazyCreationTime ( string xmlDateTime )
2013-02-23 08:24:43 -05:00
{
2013-06-15 15:12:46 -04:00
m_tCreationLazy = xmlDateTime ;
2013-02-23 08:24:43 -05:00
}
/// <summary>
/// The date/time when this group was last accessed (read).
/// </summary>
public DateTime LastAccessTime
{
2013-06-15 15:12:46 -04:00
get { return GetLazyTime ( ref m_tLastAccessLazy , ref m_tLastAccess ) ; }
set { m_tLastAccess = value ; m_tLastAccessLazy = null ; }
}
public void SetLazyLastAccessTime ( string xmlDateTime )
{
m_tLastAccessLazy = xmlDateTime ;
}
/// <summary>
/// The date/time when this group was last modified.
/// </summary>
public DateTime LastModificationTime
{
get { return GetLazyTime ( ref m_tLastModLazy , ref m_tLastMod ) ; }
set { m_tLastMod = value ; m_tLastModLazy = null ; }
}
public void SetLazyLastModificationTime ( string xmlDateTime )
{
m_tLastModLazy = xmlDateTime ;
2013-02-23 08:24:43 -05:00
}
/// <summary>
2013-06-15 15:12:46 -04:00
/// The date/time when this group expires. Use the <c>Expires</c> property
/// to specify if the group does actually expire or not.
2013-02-23 08:24:43 -05:00
/// </summary>
public DateTime ExpiryTime
{
2013-06-15 15:12:46 -04:00
get { return GetLazyTime ( ref m_tExpireLazy , ref m_tExpire ) ; }
set { m_tExpire = value ; m_tExpireLazy = null ; }
}
public void SetLazyExpiryTime ( string xmlDateTime )
{
m_tExpireLazy = xmlDateTime ;
2013-02-23 08:24:43 -05:00
}
/// <summary>
/// Flag that determines if the group expires.
/// </summary>
public bool Expires
{
get { return m_bExpires ; }
set { m_bExpires = value ; }
}
/// <summary>
/// Get or set the usage count of the group. To increase the usage
/// count by one, use the <c>Touch</c> function.
/// </summary>
public ulong UsageCount
{
get { return m_uUsageCount ; }
set { m_uUsageCount = value ; }
}
/// <summary>
/// Get a list of subgroups in this group.
/// </summary>
public PwObjectList < PwGroup > Groups
{
get { return m_listGroups ; }
}
/// <summary>
/// Get a list of entries in this group.
/// </summary>
public PwObjectList < PwEntry > Entries
{
get { return m_listEntries ; }
}
/// <summary>
/// A flag specifying whether this group is virtual or not. Virtual
/// groups can contain links to entries stored in other groups.
/// Note that this flag has to be interpreted and set by the calling
/// code; it won't prevent you from accessing and modifying the list
/// of entries in this group in any way.
/// </summary>
public bool IsVirtual
{
get { return m_bVirtual ; }
set { m_bVirtual = value ; }
}
/// <summary>
/// Default auto-type keystroke sequence for all entries in
/// this group. This property can be an empty string, which
/// means that the value should be inherited from the parent.
/// </summary>
public string DefaultAutoTypeSequence
{
get { return m_strDefaultAutoTypeSequence ; }
set
{
Debug . Assert ( value ! = null ) ; if ( value = = null ) throw new ArgumentNullException ( "value" ) ;
m_strDefaultAutoTypeSequence = value ;
}
}
public bool? EnableAutoType
{
get { return m_bEnableAutoType ; }
set { m_bEnableAutoType = value ; }
}
public bool? EnableSearching
{
get { return m_bEnableSearching ; }
set { m_bEnableSearching = value ; }
}
public PwUuid LastTopVisibleEntry
{
get { return m_pwLastTopVisibleEntry ; }
set
{
Debug . Assert ( value ! = null ) ; if ( value = = null ) throw new ArgumentNullException ( "value" ) ;
m_pwLastTopVisibleEntry = value ;
}
}
public static EventHandler < ObjectTouchedEventArgs > GroupTouched ;
public EventHandler < ObjectTouchedEventArgs > Touched ;
/// <summary>
/// Construct a new, empty group.
/// </summary>
public PwGroup ( )
{
}
/// <summary>
/// Construct a new, empty group.
/// </summary>
/// <param name="bCreateNewUuid">Create a new UUID for this group.</param>
/// <param name="bSetTimes">Set creation, last access and last modification times to the current time.</param>
public PwGroup ( bool bCreateNewUuid , bool bSetTimes )
{
if ( bCreateNewUuid ) m_uuid = new PwUuid ( true ) ;
if ( bSetTimes )
{
m_tCreation = m_tLastMod = m_tLastAccess =
m_tParentGroupLastMod = DateTime . Now ;
}
}
/// <summary>
/// Construct a new group.
/// </summary>
/// <param name="bCreateNewUuid">Create a new UUID for this group.</param>
/// <param name="bSetTimes">Set creation, last access and last modification times to the current time.</param>
/// <param name="strName">Name of the new group.</param>
/// <param name="pwIcon">Icon of the new group.</param>
public PwGroup ( bool bCreateNewUuid , bool bSetTimes , string strName , PwIcon pwIcon )
{
if ( bCreateNewUuid ) m_uuid = new PwUuid ( true ) ;
if ( bSetTimes )
{
m_tCreation = m_tLastMod = m_tLastAccess =
m_tParentGroupLastMod = DateTime . Now ;
}
if ( strName ! = null ) m_strName = strName ;
m_pwIcon = pwIcon ;
}
/// <summary>
/// Deeply clone the current group. The returned group will be an exact
/// value copy of the current object (including UUID, etc.).
/// </summary>
/// <returns>Exact value copy of the current <c>PwGroup</c> object.</returns>
public PwGroup CloneDeep ( )
{
PwGroup pg = new PwGroup ( false , false ) ;
pg . m_uuid = m_uuid ; // PwUuid is immutable
pg . m_listGroups = m_listGroups . CloneDeep ( ) ;
pg . m_listEntries = m_listEntries . CloneDeep ( ) ;
pg . m_pParentGroup = m_pParentGroup ;
pg . m_tParentGroupLastMod = m_tParentGroupLastMod ;
2013-06-21 12:56:50 -04:00
pg . m_tParentGroupLastModLazy = m_tParentGroupLastModLazy ;
2013-02-23 08:24:43 -05:00
pg . m_strName = m_strName ;
pg . m_strNotes = m_strNotes ;
pg . m_pwIcon = m_pwIcon ;
pg . m_pwCustomIconID = m_pwCustomIconID ;
pg . m_tCreation = m_tCreation ;
pg . m_tExpire = m_tExpire ;
pg . m_tLastAccess = m_tLastAccess ;
pg . m_tLastMod = m_tLastMod ;
pg . m_bExpires = m_bExpires ;
pg . m_uUsageCount = m_uUsageCount ;
2013-06-20 15:29:07 -04:00
pg . m_tCreationLazy = m_tCreationLazy ;
pg . m_tLastModLazy = m_tLastModLazy ;
pg . m_tLastAccessLazy = m_tLastAccessLazy ;
pg . m_tExpireLazy = m_tExpireLazy ;
2013-02-23 08:24:43 -05:00
pg . m_bIsExpanded = m_bIsExpanded ;
pg . m_bVirtual = m_bVirtual ;
pg . m_strDefaultAutoTypeSequence = m_strDefaultAutoTypeSequence ;
pg . m_pwLastTopVisibleEntry = m_pwLastTopVisibleEntry ;
return pg ;
}
public PwGroup CloneStructure ( )
{
PwGroup pg = new PwGroup ( false , false ) ;
pg . m_uuid = m_uuid ; // PwUuid is immutable
pg . m_tParentGroupLastMod = m_tParentGroupLastMod ;
// Do not assign m_pParentGroup
foreach ( PwGroup pgSub in m_listGroups )
pg . AddGroup ( pgSub . CloneStructure ( ) , true ) ;
foreach ( PwEntry peSub in m_listEntries )
pg . AddEntry ( peSub . CloneStructure ( ) , true ) ;
return pg ;
}
/// <summary>
/// Assign properties to the current group based on a template group.
/// </summary>
/// <param name="pgTemplate">Template group. Must not be <c>null</c>.</param>
/// <param name="bOnlyIfNewer">Only set the properties of the template group
/// if it is newer than the current one.</param>
/// <param name="bAssignLocationChanged">If <c>true</c>, the
/// <c>LocationChanged</c> property is copied, otherwise not.</param>
public void AssignProperties ( PwGroup pgTemplate , bool bOnlyIfNewer ,
bool bAssignLocationChanged )
{
Debug . Assert ( pgTemplate ! = null ) ; if ( pgTemplate = = null ) throw new ArgumentNullException ( "pgTemplate" ) ;
2013-06-21 12:56:50 -04:00
if ( bOnlyIfNewer & & ( pgTemplate . LastModificationTime < LastModificationTime ) ) return ;
2013-02-23 08:24:43 -05:00
// Template UUID should be the same as the current one
Debug . Assert ( m_uuid . EqualsValue ( pgTemplate . m_uuid ) ) ;
m_uuid = pgTemplate . m_uuid ;
if ( bAssignLocationChanged )
m_tParentGroupLastMod = pgTemplate . m_tParentGroupLastMod ;
m_strName = pgTemplate . m_strName ;
m_strNotes = pgTemplate . m_strNotes ;
m_pwIcon = pgTemplate . m_pwIcon ;
m_pwCustomIconID = pgTemplate . m_pwCustomIconID ;
2013-06-21 12:56:50 -04:00
m_tCreation = pgTemplate . CreationTime ;
m_tLastMod = pgTemplate . LastModificationTime ;
m_tLastAccess = pgTemplate . LastAccessTime ;
m_tExpire = pgTemplate . ExpiryTime ;
2013-02-23 08:24:43 -05:00
m_bExpires = pgTemplate . m_bExpires ;
m_uUsageCount = pgTemplate . m_uUsageCount ;
m_strDefaultAutoTypeSequence = pgTemplate . m_strDefaultAutoTypeSequence ;
m_pwLastTopVisibleEntry = pgTemplate . m_pwLastTopVisibleEntry ;
}
/// <summary>
/// Touch the group. This function updates the internal last access
/// time. If the <paramref name="bModified" /> parameter is <c>true</c>,
/// the last modification time gets updated, too.
/// </summary>
/// <param name="bModified">Modify last modification time.</param>
public void Touch ( bool bModified )
{
Touch ( bModified , true ) ;
}
/// <summary>
/// Touch the group. This function updates the internal last access
/// time. If the <paramref name="bModified" /> parameter is <c>true</c>,
/// the last modification time gets updated, too.
/// </summary>
/// <param name="bModified">Modify last modification time.</param>
/// <param name="bTouchParents">If <c>true</c>, all parent objects
/// get touched, too.</param>
public void Touch ( bool bModified , bool bTouchParents )
{
m_tLastAccess = DateTime . Now ;
+ + m_uUsageCount ;
if ( bModified ) m_tLastMod = m_tLastAccess ;
if ( this . Touched ! = null )
this . Touched ( this , new ObjectTouchedEventArgs ( this ,
bModified , bTouchParents ) ) ;
if ( PwGroup . GroupTouched ! = null )
PwGroup . GroupTouched ( this , new ObjectTouchedEventArgs ( this ,
bModified , bTouchParents ) ) ;
if ( bTouchParents & & ( m_pParentGroup ! = null ) )
m_pParentGroup . Touch ( bModified , true ) ;
}
/// <summary>
/// Get number of groups and entries in the current group. This function
/// can also traverse through all subgroups and accumulate their counts
/// (recursive mode).
/// </summary>
/// <param name="bRecursive">If this parameter is <c>true</c>, all
/// subgroups and entries in subgroups will be counted and added to
/// the returned value. If it is <c>false</c>, only the number of
/// subgroups and entries of the current group is returned.</param>
/// <param name="uNumGroups">Number of subgroups.</param>
/// <param name="uNumEntries">Number of entries.</param>
public void GetCounts ( bool bRecursive , out uint uNumGroups , out uint uNumEntries )
{
if ( bRecursive )
{
uint uTotalGroups = m_listGroups . UCount ;
uint uTotalEntries = m_listEntries . UCount ;
uint uSubGroupCount , uSubEntryCount ;
foreach ( PwGroup pg in m_listGroups )
{
pg . GetCounts ( true , out uSubGroupCount , out uSubEntryCount ) ;
uTotalGroups + = uSubGroupCount ;
uTotalEntries + = uSubEntryCount ;
}
uNumGroups = uTotalGroups ;
uNumEntries = uTotalEntries ;
}
else // !bRecursive
{
uNumGroups = m_listGroups . UCount ;
uNumEntries = m_listEntries . UCount ;
}
}
public uint GetEntriesCount ( bool bRecursive )
{
uint uGroups , uEntries ;
GetCounts ( bRecursive , out uGroups , out uEntries ) ;
return uEntries ;
}
/// <summary>
/// Traverse the group/entry tree in the current group. Various traversal
/// methods are available.
/// </summary>
/// <param name="tm">Specifies the traversal method.</param>
/// <param name="groupHandler">Function that performs an action on
/// the currently visited group (see <c>GroupHandler</c> for more).
/// This parameter may be <c>null</c>, in this case the tree is traversed but
/// you don't get notifications for each visited group.</param>
/// <param name="entryHandler">Function that performs an action on
/// the currently visited entry (see <c>EntryHandler</c> for more).
/// This parameter may be <c>null</c>.</param>
/// <returns>Returns <c>true</c> if all entries and groups have been
/// traversed. If the traversal has been canceled by one of the two
/// handlers, the return value is <c>false</c>.</returns>
public bool TraverseTree ( TraversalMethod tm , GroupHandler groupHandler , EntryHandler entryHandler )
{
bool bRet = false ;
switch ( tm )
{
case TraversalMethod . None :
bRet = true ;
break ;
case TraversalMethod . PreOrder :
bRet = PreOrderTraverseTree ( groupHandler , entryHandler ) ;
break ;
default :
Debug . Assert ( false ) ;
break ;
}
return bRet ;
}
private bool PreOrderTraverseTree ( GroupHandler groupHandler , EntryHandler entryHandler )
{
if ( entryHandler ! = null )
{
foreach ( PwEntry pe in m_listEntries )
{
if ( ! entryHandler ( pe ) ) return false ;
}
}
if ( groupHandler ! = null )
{
foreach ( PwGroup pg in m_listGroups )
{
if ( ! groupHandler ( pg ) ) return false ;
pg . PreOrderTraverseTree ( groupHandler , entryHandler ) ;
}
}
else // groupHandler == null
{
foreach ( PwGroup pg in m_listGroups )
{
pg . PreOrderTraverseTree ( null , entryHandler ) ;
}
}
return true ;
}
/// <summary>
/// Pack all groups into one flat linked list of references (recursively).
/// Temporary IDs (<c>TemporaryID</c> field) and levels (<c>TemporaryLevel</c>)
/// are assigned automatically.
/// </summary>
/// <returns>Flat list of all groups.</returns>
public LinkedList < PwGroup > GetFlatGroupList ( )
{
LinkedList < PwGroup > list = new LinkedList < PwGroup > ( ) ;
foreach ( PwGroup pg in m_listGroups )
{
list . AddLast ( pg ) ;
if ( pg . Groups . UCount ! = 0 )
LinearizeGroupRecursive ( list , pg , 1 ) ;
}
return list ;
}
private void LinearizeGroupRecursive ( LinkedList < PwGroup > list , PwGroup pg , ushort uLevel )
{
Debug . Assert ( pg ! = null ) ; if ( pg = = null ) return ;
foreach ( PwGroup pwg in pg . Groups )
{
list . AddLast ( pwg ) ;
if ( pwg . Groups . UCount ! = 0 )
LinearizeGroupRecursive ( list , pwg , ( ushort ) ( uLevel + 1 ) ) ;
}
}
/// <summary>
/// Pack all entries into one flat linked list of references. Temporary
/// group IDs are assigned automatically.
/// </summary>
/// <param name="flatGroupList">A flat group list created by
/// <c>GetFlatGroupList</c>.</param>
/// <returns>Flat list of all entries.</returns>
public static LinkedList < PwEntry > GetFlatEntryList ( LinkedList < PwGroup > flatGroupList )
{
Debug . Assert ( flatGroupList ! = null ) ; if ( flatGroupList = = null ) return null ;
LinkedList < PwEntry > list = new LinkedList < PwEntry > ( ) ;
foreach ( PwGroup pg in flatGroupList )
{
foreach ( PwEntry pe in pg . Entries )
list . AddLast ( pe ) ;
}
return list ;
}
/// <summary>
/// Enable protection of a specific string field type.
/// </summary>
/// <param name="strFieldName">Name of the string field to protect or unprotect.</param>
/// <param name="bEnable">Enable protection or not.</param>
/// <returns>Returns <c>true</c>, if the operation completed successfully,
/// otherwise <c>false</c>.</returns>
public bool EnableStringFieldProtection ( string strFieldName , bool bEnable )
{
Debug . Assert ( strFieldName ! = null ) ;
EntryHandler eh = delegate ( PwEntry pe )
{
// Enable protection of current string
pe . Strings . EnableProtection ( strFieldName , bEnable ) ;
// Do the same for all history items
foreach ( PwEntry peHistory in pe . History )
{
peHistory . Strings . EnableProtection ( strFieldName , bEnable ) ;
}
return true ;
} ;
return PreOrderTraverseTree ( null , eh ) ;
}
/// <summary>
/// Search this group and all subgroups for entries.
/// </summary>
/// <param name="sp">Specifies the search method.</param>
/// <param name="listStorage">Entry list in which the search results will
/// be stored.</param>
public void SearchEntries ( SearchParameters sp , PwObjectList < PwEntry > listStorage )
{
SearchEntries ( sp , listStorage , null ) ;
}
/// <summary>
/// Search this group and all subgroups for entries.
/// </summary>
/// <param name="sp">Specifies the search method.</param>
/// <param name="listStorage">Entry list in which the search results will
/// be stored.</param>
public void SearchEntries ( SearchParameters sp , PwObjectList < PwEntry > listStorage ,
IStatusLogger slStatus )
2013-06-19 13:44:35 -04:00
{
2013-08-13 23:59:26 -04:00
SearchEntries ( sp , listStorage , null , slStatus ) ;
2013-06-19 13:44:35 -04:00
}
/// <summary>
/// Search this group and all subgroups for entries.
/// </summary>
/// <param name="sp">Specifies the search method.</param>
/// <param name="listStorage">Entry list in which the search results will
/// be stored.</param>
/// <param name="resultContexts">Dictionary that will be populated with text fragments indicating the context of why each entry (keyed by Uuid) was returned</param>
public void SearchEntries ( SearchParameters sp , PwObjectList < PwEntry > listStorage , IDictionary < PwUuid , String > resultContexts ,
IStatusLogger slStatus )
2013-02-23 08:24:43 -05:00
{
if ( sp = = null ) { Debug . Assert ( false ) ; return ; }
if ( listStorage = = null ) { Debug . Assert ( false ) ; return ; }
ulong uCurEntries = 0 , uTotalEntries = 0 ;
List < string > lTerms = StrUtil . SplitSearchTerms ( sp . SearchString ) ;
if ( ( lTerms . Count < = 1 ) | | sp . RegularExpression )
{
if ( slStatus ! = null ) uTotalEntries = GetEntriesCount ( true ) ;
2013-06-19 13:44:35 -04:00
SearchEntriesSingle ( sp , listStorage , resultContexts , slStatus , ref uCurEntries ,
2013-02-23 08:24:43 -05:00
uTotalEntries ) ;
return ;
}
// Search longer strings first (for improved performance)
lTerms . Sort ( StrUtil . CompareLengthGt ) ;
string strFullSearch = sp . SearchString ; // Backup
PwGroup pg = this ;
for ( int iTerm = 0 ; iTerm < lTerms . Count ; + + iTerm )
{
// Update counters for a better state guess
if ( slStatus ! = null )
{
ulong uRemRounds = ( ulong ) ( lTerms . Count - iTerm ) ;
uTotalEntries = uCurEntries + ( uRemRounds *
pg . GetEntriesCount ( true ) ) ;
}
PwGroup pgNew = new PwGroup ( ) ;
sp . SearchString = lTerms [ iTerm ] ;
bool bNegate = false ;
if ( sp . SearchString . StartsWith ( "-" ) )
{
sp . SearchString = sp . SearchString . Substring ( 1 ) ;
bNegate = ( sp . SearchString . Length > 0 ) ;
}
2013-06-19 13:44:35 -04:00
if ( ! pg . SearchEntriesSingle ( sp , pgNew . Entries , resultContexts , slStatus ,
2013-02-23 08:24:43 -05:00
ref uCurEntries , uTotalEntries ) )
{
pg = null ;
break ;
}
if ( bNegate )
{
PwObjectList < PwEntry > lCand = pg . GetEntries ( true ) ;
pg = new PwGroup ( ) ;
foreach ( PwEntry peCand in lCand )
{
if ( pgNew . Entries . IndexOf ( peCand ) < 0 ) pg . Entries . Add ( peCand ) ;
}
}
else pg = pgNew ;
}
if ( pg ! = null ) listStorage . Add ( pg . Entries ) ;
sp . SearchString = strFullSearch ; // Restore
}
private bool SearchEntriesSingle ( SearchParameters spIn ,
2013-06-19 13:44:35 -04:00
PwObjectList < PwEntry > listStorage , IDictionary < PwUuid , String > resultContexts , IStatusLogger slStatus ,
2013-02-23 08:24:43 -05:00
ref ulong uCurEntries , ulong uTotalEntries )
{
SearchParameters sp = spIn . Clone ( ) ;
if ( sp . SearchString = = null ) { Debug . Assert ( false ) ; return true ; }
sp . SearchString = sp . SearchString . Trim ( ) ;
bool bTitle = sp . SearchInTitles ;
bool bUserName = sp . SearchInUserNames ;
bool bPassword = sp . SearchInPasswords ;
bool bUrl = sp . SearchInUrls ;
bool bNotes = sp . SearchInNotes ;
bool bOther = sp . SearchInOther ;
bool bUuids = sp . SearchInUuids ;
bool bGroupName = sp . SearchInGroupNames ;
bool bTags = sp . SearchInTags ;
bool bExcludeExpired = sp . ExcludeExpired ;
bool bRespectEntrySearchingDisabled = sp . RespectEntrySearchingDisabled ;
DateTime dtNow = DateTime . Now ;
Regex rx = null ;
if ( sp . RegularExpression )
{
RegexOptions ro = RegexOptions . Compiled ;
if ( ( sp . ComparisonMode = = StringComparison . CurrentCultureIgnoreCase ) | |
( sp . ComparisonMode = = StringComparison . InvariantCultureIgnoreCase ) | |
( sp . ComparisonMode = = StringComparison . OrdinalIgnoreCase ) )
{
ro | = RegexOptions . IgnoreCase ;
}
rx = new Regex ( sp . SearchString , ro ) ;
}
ulong uLocalCurEntries = uCurEntries ;
EntryHandler eh = null ;
if ( sp . SearchString . Length < = 0 ) // Report all
{
eh = delegate ( PwEntry pe )
{
if ( slStatus ! = null )
{
if ( ! slStatus . SetProgress ( ( uint ) ( ( uLocalCurEntries *
100 UL ) / uTotalEntries ) ) ) return false ;
+ + uLocalCurEntries ;
}
if ( bRespectEntrySearchingDisabled & & ! pe . GetSearchingEnabled ( ) )
return true ; // Skip
if ( bExcludeExpired & & pe . Expires & & ( dtNow > pe . ExpiryTime ) )
return true ; // Skip
listStorage . Add ( pe ) ;
return true ;
} ;
}
else
{
eh = delegate ( PwEntry pe )
{
if ( slStatus ! = null )
{
if ( ! slStatus . SetProgress ( ( uint ) ( ( uLocalCurEntries *
100 UL ) / uTotalEntries ) ) ) return false ;
+ + uLocalCurEntries ;
}
if ( bRespectEntrySearchingDisabled & & ! pe . GetSearchingEnabled ( ) )
return true ; // Skip
if ( bExcludeExpired & & pe . Expires & & ( dtNow > pe . ExpiryTime ) )
return true ; // Skip
uint uInitialResults = listStorage . UCount ;
foreach ( KeyValuePair < string , ProtectedString > kvp in pe . Strings )
{
string strKey = kvp . Key ;
if ( strKey = = PwDefs . TitleField )
{
if ( bTitle ) SearchEvalAdd ( sp , kvp . Value . ReadString ( ) ,
2013-06-19 13:44:35 -04:00
rx , pe , listStorage , resultContexts , strKey ) ;
2013-02-23 08:24:43 -05:00
}
else if ( strKey = = PwDefs . UserNameField )
{
if ( bUserName ) SearchEvalAdd ( sp , kvp . Value . ReadString ( ) ,
2013-06-19 13:44:35 -04:00
rx , pe , listStorage , resultContexts , strKey ) ;
2013-02-23 08:24:43 -05:00
}
else if ( strKey = = PwDefs . PasswordField )
{
if ( bPassword ) SearchEvalAdd ( sp , kvp . Value . ReadString ( ) ,
2013-06-19 13:44:35 -04:00
rx , pe , listStorage , resultContexts , strKey ) ;
2013-02-23 08:24:43 -05:00
}
else if ( strKey = = PwDefs . UrlField )
{
if ( bUrl ) SearchEvalAdd ( sp , kvp . Value . ReadString ( ) ,
2013-06-19 13:44:35 -04:00
rx , pe , listStorage , resultContexts , strKey ) ;
2013-02-23 08:24:43 -05:00
}
else if ( strKey = = PwDefs . NotesField )
{
if ( bNotes ) SearchEvalAdd ( sp , kvp . Value . ReadString ( ) ,
2013-06-19 13:44:35 -04:00
rx , pe , listStorage , resultContexts , strKey ) ;
2013-02-23 08:24:43 -05:00
}
else if ( bOther )
SearchEvalAdd ( sp , kvp . Value . ReadString ( ) ,
2013-06-19 13:44:35 -04:00
rx , pe , listStorage , resultContexts , strKey ) ;
2013-02-23 08:24:43 -05:00
// An entry can match only once => break if we have added it
if ( listStorage . UCount > uInitialResults ) break ;
}
if ( bUuids & & ( listStorage . UCount = = uInitialResults ) )
2013-06-19 13:44:35 -04:00
SearchEvalAdd ( sp , pe . Uuid . ToHexString ( ) , rx , pe , listStorage , resultContexts , SearchContextUuid ) ;
2013-02-23 08:24:43 -05:00
if ( bGroupName & & ( listStorage . UCount = = uInitialResults ) & &
( pe . ParentGroup ! = null ) )
2013-06-19 13:44:35 -04:00
SearchEvalAdd ( sp , pe . ParentGroup . Name , rx , pe , listStorage , resultContexts , SearchContextParentGroup ) ;
2013-02-23 08:24:43 -05:00
if ( bTags )
{
foreach ( string strTag in pe . Tags )
{
if ( listStorage . UCount ! = uInitialResults ) break ; // Match
2013-06-19 13:44:35 -04:00
SearchEvalAdd ( sp , strTag , rx , pe , listStorage , resultContexts , SearchContextTags ) ;
2013-02-23 08:24:43 -05:00
}
}
return true ;
} ;
}
if ( ! PreOrderTraverseTree ( null , eh ) ) return false ;
uCurEntries = uLocalCurEntries ;
return true ;
}
private static void SearchEvalAdd ( SearchParameters sp , string strDataField ,
2013-06-19 13:44:35 -04:00
Regex rx , PwEntry pe , PwObjectList < PwEntry > lResults , IDictionary < PwUuid , String > resultContexts , string contextFieldName )
2013-02-23 08:24:43 -05:00
{
bool bMatch = false ;
2013-06-19 13:44:35 -04:00
int matchPos ;
2013-02-23 08:24:43 -05:00
2013-06-19 13:44:35 -04:00
if ( rx = = null )
{
matchPos = strDataField . IndexOf ( sp . SearchString , sp . ComparisonMode ) ;
bMatch = matchPos > = 0 ;
}
else
{
var match = rx . Match ( strDataField ) ;
bMatch = match . Success ;
matchPos = match . Index ;
}
2013-02-23 08:24:43 -05:00
if ( ! bMatch & & ( sp . DataTransformationFn ! = null ) )
{
string strCmp = sp . DataTransformationFn ( strDataField , pe ) ;
if ( ! object . ReferenceEquals ( strCmp , strDataField ) )
{
2013-06-19 13:44:35 -04:00
if ( rx = = null )
{
matchPos = strCmp . IndexOf ( sp . SearchString , sp . ComparisonMode ) ;
bMatch = matchPos > = 0 ;
}
else
{
var match = rx . Match ( strCmp ) ;
bMatch = match . Success ;
matchPos = match . Index ;
}
2013-02-23 08:24:43 -05:00
}
}
2013-06-19 13:44:35 -04:00
if ( bMatch )
{
lResults . Add ( pe ) ;
if ( resultContexts ! = null )
{
// Trim the value if necessary
var contextString = strDataField ;
if ( contextString . Length > SearchContextStringMaxLength )
{
// Start 10% before actual data, and don't run over
var startPos = Math . Min ( matchPos - ( SearchContextStringMaxLength / 10 ) , contextString . Length - SearchContextStringMaxLength ) ;
contextString = "<22> " + contextString . Substring ( startPos , SearchContextStringMaxLength ) + ( ( startPos + SearchContextStringMaxLength < contextString . Length ) ? " <20> " : null ) ;
}
resultContexts [ pe . Uuid ] = contextFieldName + ": " + contextString ;
}
}
2013-02-23 08:24:43 -05:00
}
public List < string > BuildEntryTagsList ( )
{
return BuildEntryTagsList ( false ) ;
}
public List < string > BuildEntryTagsList ( bool bSort )
{
List < string > vTags = new List < string > ( ) ;
EntryHandler eh = delegate ( PwEntry pe )
{
foreach ( string strTag in pe . Tags )
{
bool bFound = false ;
for ( int i = 0 ; i < vTags . Count ; + + i )
{
if ( vTags [ i ] . Equals ( strTag , StrUtil . CaseIgnoreCmp ) )
{
bFound = true ;
break ;
}
}
if ( ! bFound ) vTags . Add ( strTag ) ;
}
return true ;
} ;
TraverseTree ( TraversalMethod . PreOrder , null , eh ) ;
if ( bSort ) vTags . Sort ( StrUtil . CaseIgnoreComparer ) ;
return vTags ;
}
public void FindEntriesByTag ( string strTag , PwObjectList < PwEntry > listStorage ,
bool bSearchRecursive )
{
if ( strTag = = null ) throw new ArgumentNullException ( "strTag" ) ;
if ( strTag . Length = = 0 ) return ;
foreach ( PwEntry pe in m_listEntries )
{
foreach ( string strEntryTag in pe . Tags )
{
if ( strEntryTag . Equals ( strTag , StrUtil . CaseIgnoreCmp ) )
{
listStorage . Add ( pe ) ;
break ;
}
}
}
if ( bSearchRecursive )
{
foreach ( PwGroup pg in m_listGroups )
pg . FindEntriesByTag ( strTag , listStorage , true ) ;
}
}
/// <summary>
/// Find a group.
/// </summary>
/// <param name="uuid">UUID identifying the group the caller is looking for.</param>
/// <param name="bSearchRecursive">If <c>true</c>, the search is recursive.</param>
/// <returns>Returns reference to found group, otherwise <c>null</c>.</returns>
public PwGroup FindGroup ( PwUuid uuid , bool bSearchRecursive )
{
// Do not assert on PwUuid.Zero
if ( m_uuid . EqualsValue ( uuid ) ) return this ;
if ( bSearchRecursive )
{
PwGroup pgRec ;
foreach ( PwGroup pg in m_listGroups )
{
pgRec = pg . FindGroup ( uuid , true ) ;
if ( pgRec ! = null ) return pgRec ;
}
}
else // Not recursive
{
foreach ( PwGroup pg in m_listGroups )
{
if ( pg . m_uuid . EqualsValue ( uuid ) )
return pg ;
}
}
return null ;
}
/// <summary>
/// Find an object.
/// </summary>
/// <param name="uuid">UUID of the object to find.</param>
/// <param name="bRecursive">Specifies whether to search recursively.</param>
/// <param name="bEntries">If <c>null</c>, groups and entries are
/// searched. If <c>true</c>, only entries are searched. If <c>false</c>,
/// only groups are searched.</param>
/// <returns>Reference to the object, if found. Otherwise <c>null</c>.</returns>
public IStructureItem FindObject ( PwUuid uuid , bool bRecursive ,
bool? bEntries )
{
if ( bEntries . HasValue )
{
if ( bEntries . Value ) return FindEntry ( uuid , bRecursive ) ;
else return FindGroup ( uuid , bRecursive ) ;
}
PwGroup pg = FindGroup ( uuid , bRecursive ) ;
if ( pg ! = null ) return pg ;
return FindEntry ( uuid , bRecursive ) ;
}
/// <summary>
/// Try to find a subgroup and create it, if it doesn't exist yet.
/// </summary>
/// <param name="strName">Name of the subgroup.</param>
/// <param name="bCreateIfNotFound">If the group isn't found: create it.</param>
/// <returns>Returns a reference to the requested group or <c>null</c> if
/// it doesn't exist and shouldn't be created.</returns>
public PwGroup FindCreateGroup ( string strName , bool bCreateIfNotFound )
{
Debug . Assert ( strName ! = null ) ; if ( strName = = null ) throw new ArgumentNullException ( "strName" ) ;
foreach ( PwGroup pg in m_listGroups )
{
if ( pg . Name = = strName ) return pg ;
}
if ( ! bCreateIfNotFound ) return null ;
PwGroup pgNew = new PwGroup ( true , true , strName , PwIcon . Folder ) ;
AddGroup ( pgNew , true ) ;
return pgNew ;
}
/// <summary>
/// Find an entry.
/// </summary>
/// <param name="uuid">UUID identifying the entry the caller is looking for.</param>
/// <param name="bSearchRecursive">If <c>true</c>, the search is recursive.</param>
/// <returns>Returns reference to found entry, otherwise <c>null</c>.</returns>
public PwEntry FindEntry ( PwUuid uuid , bool bSearchRecursive )
{
foreach ( PwEntry pe in m_listEntries )
{
if ( pe . Uuid . EqualsValue ( uuid ) ) return pe ;
}
if ( bSearchRecursive )
{
PwEntry peSub ;
foreach ( PwGroup pg in m_listGroups )
{
peSub = pg . FindEntry ( uuid , true ) ;
if ( peSub ! = null ) return peSub ;
}
}
return null ;
}
/// <summary>
/// Get the full path of a group.
/// </summary>
/// <returns>Full path of the group.</returns>
public string GetFullPath ( )
{
return GetFullPath ( "." , false ) ;
}
/// <summary>
/// Get the full path of a group.
/// </summary>
/// <param name="strSeparator">String that separates the group
/// names.</param>
/// <returns>Full path of the group.</returns>
public string GetFullPath ( string strSeparator , bool bIncludeTopMostGroup )
{
Debug . Assert ( strSeparator ! = null ) ;
if ( strSeparator = = null ) throw new ArgumentNullException ( "strSeparator" ) ;
string strPath = m_strName ;
PwGroup pg = m_pParentGroup ;
while ( pg ! = null )
{
if ( ( ! bIncludeTopMostGroup ) & & ( pg . m_pParentGroup = = null ) )
break ;
strPath = pg . Name + strSeparator + strPath ;
pg = pg . m_pParentGroup ;
}
return strPath ;
}
/// <summary>
/// Assign new UUIDs to groups and entries.
/// </summary>
/// <param name="bNewGroups">Create new UUIDs for subgroups.</param>
/// <param name="bNewEntries">Create new UUIDs for entries.</param>
/// <param name="bRecursive">Recursive tree traversal.</param>
public void CreateNewItemUuids ( bool bNewGroups , bool bNewEntries , bool bRecursive )
{
if ( bNewGroups )
{
foreach ( PwGroup pg in m_listGroups )
pg . Uuid = new PwUuid ( true ) ;
}
if ( bNewEntries )
{
foreach ( PwEntry pe in m_listEntries )
pe . SetUuid ( new PwUuid ( true ) , true ) ;
}
if ( bRecursive )
{
foreach ( PwGroup pg in m_listGroups )
pg . CreateNewItemUuids ( bNewGroups , bNewEntries , true ) ;
}
}
public void TakeOwnership ( bool bTakeSubGroups , bool bTakeEntries , bool bRecursive )
{
if ( bTakeSubGroups )
{
foreach ( PwGroup pg in m_listGroups )
pg . ParentGroup = this ;
}
if ( bTakeEntries )
{
foreach ( PwEntry pe in m_listEntries )
pe . ParentGroup = this ;
}
if ( bRecursive )
{
foreach ( PwGroup pg in m_listGroups )
pg . TakeOwnership ( bTakeSubGroups , bTakeEntries , true ) ;
}
}
/// <summary>
/// Find/create a subtree of groups.
/// </summary>
/// <param name="strTree">Tree string.</param>
/// <param name="vSeparators">Separators that delimit groups in the
/// <c>strTree</c> parameter.</param>
public PwGroup FindCreateSubTree ( string strTree , char [ ] vSeparators )
{
return FindCreateSubTree ( strTree , vSeparators , true ) ;
}
public PwGroup FindCreateSubTree ( string strTree , char [ ] vSeparators ,
bool bAllowCreate )
{
Debug . Assert ( strTree ! = null ) ; if ( strTree = = null ) return this ;
if ( strTree . Length = = 0 ) return this ;
string [ ] vGroups = strTree . Split ( vSeparators ) ;
if ( ( vGroups = = null ) | | ( vGroups . Length = = 0 ) ) return this ;
PwGroup pgContainer = this ;
for ( int nGroup = 0 ; nGroup < vGroups . Length ; + + nGroup )
{
if ( string . IsNullOrEmpty ( vGroups [ nGroup ] ) ) continue ;
bool bFound = false ;
foreach ( PwGroup pg in pgContainer . Groups )
{
if ( pg . Name = = vGroups [ nGroup ] )
{
pgContainer = pg ;
bFound = true ;
break ;
}
}
if ( ! bFound )
{
if ( ! bAllowCreate ) return null ;
PwGroup pg = new PwGroup ( true , true , vGroups [ nGroup ] , PwIcon . Folder ) ;
pgContainer . AddGroup ( pg , true ) ;
pgContainer = pg ;
}
}
return pgContainer ;
}
/// <summary>
/// Get the level of the group (i.e. the number of parent groups).
/// </summary>
/// <returns>Number of parent groups.</returns>
public uint GetLevel ( )
{
PwGroup pg = m_pParentGroup ;
uint uLevel = 0 ;
while ( pg ! = null )
{
pg = pg . ParentGroup ;
+ + uLevel ;
}
return uLevel ;
}
public string GetAutoTypeSequenceInherited ( )
{
if ( m_strDefaultAutoTypeSequence . Length > 0 )
return m_strDefaultAutoTypeSequence ;
if ( m_pParentGroup ! = null )
return m_pParentGroup . GetAutoTypeSequenceInherited ( ) ;
return string . Empty ;
}
public bool GetAutoTypeEnabledInherited ( )
{
if ( m_bEnableAutoType . HasValue ) return m_bEnableAutoType . Value ;
if ( m_pParentGroup ! = null )
return m_pParentGroup . GetAutoTypeEnabledInherited ( ) ;
return DefaultAutoTypeEnabled ;
}
public bool GetSearchingEnabledInherited ( )
{
if ( m_bEnableSearching . HasValue ) return m_bEnableSearching . Value ;
if ( m_pParentGroup ! = null )
return m_pParentGroup . GetSearchingEnabledInherited ( ) ;
return DefaultSearchingEnabled ;
}
/// <summary>
/// Get a list of subgroups (not including this one).
/// </summary>
/// <param name="bRecursive">If <c>true</c>, subgroups are added
/// recursively, i.e. all child groups are returned, too.</param>
/// <returns>List of subgroups. If <paramref name="bRecursive" /> is
/// <c>true</c>, it is guaranteed that subsubgroups appear after
/// subgroups.</returns>
public PwObjectList < PwGroup > GetGroups ( bool bRecursive )
{
if ( bRecursive = = false ) return m_listGroups ;
PwObjectList < PwGroup > list = m_listGroups . CloneShallow ( ) ;
foreach ( PwGroup pgSub in m_listGroups )
{
list . Add ( pgSub . GetGroups ( true ) ) ;
}
return list ;
}
public PwObjectList < PwEntry > GetEntries ( bool bIncludeSubGroupEntries )
{
if ( bIncludeSubGroupEntries = = false ) return m_listEntries ;
PwObjectList < PwEntry > list = m_listEntries . CloneShallow ( ) ;
foreach ( PwGroup pgSub in m_listGroups )
{
list . Add ( pgSub . GetEntries ( true ) ) ;
}
return list ;
}
/// <summary>
/// Get objects contained in this group.
/// </summary>
/// <param name="bRecursive">Specifies whether to search recursively.</param>
/// <param name="bEntries">If <c>null</c>, the returned list contains
/// groups and entries. If <c>true</c>, the returned list contains only
/// entries. If <c>false</c>, the returned list contains only groups.</param>
/// <returns>List of objects.</returns>
public List < IStructureItem > GetObjects ( bool bRecursive , bool? bEntries )
{
List < IStructureItem > list = new List < IStructureItem > ( ) ;
if ( ! bEntries . HasValue | | ! bEntries . Value )
{
PwObjectList < PwGroup > lGroups = GetGroups ( bRecursive ) ;
foreach ( PwGroup pg in lGroups ) list . Add ( pg ) ;
}
if ( ! bEntries . HasValue | | bEntries . Value )
{
PwObjectList < PwEntry > lEntries = GetEntries ( bRecursive ) ;
foreach ( PwEntry pe in lEntries ) list . Add ( pe ) ;
}
return list ;
}
public bool IsContainedIn ( PwGroup pgContainer )
{
PwGroup pgCur = m_pParentGroup ;
while ( pgCur ! = null )
{
if ( pgCur = = pgContainer ) return true ;
pgCur = pgCur . m_pParentGroup ;
}
return false ;
}
/// <summary>
/// Add a subgroup to this group.
/// </summary>
/// <param name="subGroup">Group to be added. Must not be <c>null</c>.</param>
/// <param name="bTakeOwnership">If this parameter is <c>true</c>, the
/// parent group reference of the subgroup will be set to the current
/// group (i.e. the current group takes ownership of the subgroup).</param>
public void AddGroup ( PwGroup subGroup , bool bTakeOwnership )
{
AddGroup ( subGroup , bTakeOwnership , false ) ;
}
/// <summary>
/// Add a subgroup to this group.
/// </summary>
/// <param name="subGroup">Group to be added. Must not be <c>null</c>.</param>
/// <param name="bTakeOwnership">If this parameter is <c>true</c>, the
/// parent group reference of the subgroup will be set to the current
/// group (i.e. the current group takes ownership of the subgroup).</param>
/// <param name="bUpdateLocationChangedOfSub">If <c>true</c>, the
/// <c>LocationChanged</c> property of the subgroup is updated.</param>
public void AddGroup ( PwGroup subGroup , bool bTakeOwnership ,
bool bUpdateLocationChangedOfSub )
{
if ( subGroup = = null ) throw new ArgumentNullException ( "subGroup" ) ;
m_listGroups . Add ( subGroup ) ;
if ( bTakeOwnership ) subGroup . m_pParentGroup = this ;
if ( bUpdateLocationChangedOfSub ) subGroup . LocationChanged = DateTime . Now ;
}
/// <summary>
/// Add an entry to this group.
/// </summary>
/// <param name="pe">Entry to be added. Must not be <c>null</c>.</param>
/// <param name="bTakeOwnership">If this parameter is <c>true</c>, the
/// parent group reference of the entry will be set to the current
/// group (i.e. the current group takes ownership of the entry).</param>
public void AddEntry ( PwEntry pe , bool bTakeOwnership )
{
AddEntry ( pe , bTakeOwnership , false ) ;
}
/// <summary>
/// Add an entry to this group.
/// </summary>
/// <param name="pe">Entry to be added. Must not be <c>null</c>.</param>
/// <param name="bTakeOwnership">If this parameter is <c>true</c>, the
/// parent group reference of the entry will be set to the current
/// group (i.e. the current group takes ownership of the entry).</param>
/// <param name="bUpdateLocationChangedOfEntry">If <c>true</c>, the
/// <c>LocationChanged</c> property of the entry is updated.</param>
public void AddEntry ( PwEntry pe , bool bTakeOwnership ,
bool bUpdateLocationChangedOfEntry )
{
if ( pe = = null ) throw new ArgumentNullException ( "pe" ) ;
m_listEntries . Add ( pe ) ;
// Do not remove the entry from its previous parent group,
// only assign it to the new one
if ( bTakeOwnership ) pe . ParentGroup = this ;
if ( bUpdateLocationChangedOfEntry ) pe . LocationChanged = DateTime . Now ;
}
public void SortSubGroups ( bool bRecursive )
{
m_listGroups . Sort ( new PwGroupComparer ( ) ) ;
if ( bRecursive )
{
foreach ( PwGroup pgSub in m_listGroups )
pgSub . SortSubGroups ( true ) ;
}
}
public void DeleteAllObjects ( PwDatabase pdContext )
{
DateTime dtNow = DateTime . Now ;
foreach ( PwEntry pe in m_listEntries )
{
PwDeletedObject pdo = new PwDeletedObject ( pe . Uuid , dtNow ) ;
pdContext . DeletedObjects . Add ( pdo ) ;
}
m_listEntries . Clear ( ) ;
foreach ( PwGroup pg in m_listGroups )
{
pg . DeleteAllObjects ( pdContext ) ;
PwDeletedObject pdo = new PwDeletedObject ( pg . Uuid , dtNow ) ;
pdContext . DeletedObjects . Add ( pdo ) ;
}
m_listGroups . Clear ( ) ;
}
2013-06-15 15:12:46 -04:00
private DateTime GetLazyTime ( ref string lazyTime , ref DateTime dateTime )
{
if ( lazyTime ! = null )
{
dateTime = TimeUtil . DeserializeUtcOrDefault ( lazyTime , m_tLastMod ) ;
lazyTime = null ;
}
return dateTime ;
}
2013-02-23 08:24:43 -05:00
}
public sealed class PwGroupComparer : IComparer < PwGroup >
{
public PwGroupComparer ( )
{
}
public int Compare ( PwGroup a , PwGroup b )
{
return StrUtil . CompareNaturally ( a . Name , b . Name ) ;
}
}
}