1 /*
2   KeePass Password Safe - The Open-Source Password Manager
3   Copyright (C) 2003-2021 Dominik Reichl <dominik.reichl@t-online.de>
4 
5   This program is free software; you can redistribute it and/or modify
6   it under the terms of the GNU General Public License as published by
7   the Free Software Foundation; either version 2 of the License, or
8   (at your option) any later version.
9 
10   This program is distributed in the hope that it will be useful,
11   but WITHOUT ANY WARRANTY; without even the implied warranty of
12   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13   GNU General Public License for more details.
14 
15   You should have received a copy of the GNU General Public License
16   along with this program; if not, write to the Free Software
17   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
18 */
19 
20 using System;
21 using System.Collections;
22 using System.Collections.Generic;
23 using System.Diagnostics;
24 using System.Text;
25 
26 using KeePassLib.Interfaces;
27 
28 #if KeePassLibSD
29 using KeePassLibSD;
30 #endif
31 
32 namespace KeePassLib.Collections
33 {
34 	public sealed class StringDictionaryEx : IDeepCloneable<StringDictionaryEx>,
35 		IEnumerable<KeyValuePair<string, string>>, IEquatable<StringDictionaryEx>
36 	{
37 		private SortedDictionary<string, string> m_d =
38 			new SortedDictionary<string, string>();
39 
40 		// Non-null if and only if last mod. times should be remembered
41 		private Dictionary<string, DateTime> m_dLastMod = null;
42 
43 		public int Count
44 		{
45 			get { return m_d.Count; }
46 		}
47 
StringDictionaryEx()48 		public StringDictionaryEx()
49 		{
50 		}
51 
StringDictionaryEx(bool bRememberLastMod)52 		internal StringDictionaryEx(bool bRememberLastMod)
53 		{
54 			if(bRememberLastMod) m_dLastMod = new Dictionary<string, DateTime>();
55 		}
56 
IEnumerable.GetEnumerator()57 		IEnumerator IEnumerable.GetEnumerator()
58 		{
59 			return m_d.GetEnumerator();
60 		}
61 
GetEnumerator()62 		public IEnumerator<KeyValuePair<string, string>> GetEnumerator()
63 		{
64 			return m_d.GetEnumerator();
65 		}
66 
CloneDeep()67 		public StringDictionaryEx CloneDeep()
68 		{
69 			StringDictionaryEx sdNew = new StringDictionaryEx();
70 
71 			foreach(KeyValuePair<string, string> kvp in m_d)
72 				sdNew.m_d[kvp.Key] = kvp.Value;
73 
74 			if(m_dLastMod != null)
75 				sdNew.m_dLastMod = new Dictionary<string, DateTime>(m_dLastMod);
76 
77 			Debug.Assert(Equals(sdNew));
78 			return sdNew;
79 		}
80 
Equals(StringDictionaryEx sdOther)81 		public bool Equals(StringDictionaryEx sdOther)
82 		{
83 			if(sdOther == null) { Debug.Assert(false); return false; }
84 
85 			if(m_d.Count != sdOther.m_d.Count) return false;
86 
87 			foreach(KeyValuePair<string, string> kvp in sdOther.m_d)
88 			{
89 				string str = Get(kvp.Key);
90 				if((str == null) || (str != kvp.Value)) return false;
91 			}
92 
93 			int cLastModT = ((m_dLastMod != null) ? m_dLastMod.Count : -1);
94 			int cLastModO = ((sdOther.m_dLastMod != null) ? sdOther.m_dLastMod.Count : -1);
95 			if(cLastModT != cLastModO) return false;
96 
97 			if(m_dLastMod != null)
98 			{
99 				foreach(KeyValuePair<string, DateTime> kvp in sdOther.m_dLastMod)
100 				{
101 					DateTime? odt = GetLastModificationTime(kvp.Key);
102 					if(!odt.HasValue) return false;
103 					if(odt.Value != kvp.Value) return false;
104 				}
105 			}
106 
107 			return true;
108 		}
109 
Get(string strName)110 		public string Get(string strName)
111 		{
112 			if(strName == null) { Debug.Assert(false); throw new ArgumentNullException("strName"); }
113 
114 			string str;
115 			m_d.TryGetValue(strName, out str);
116 			return str;
117 		}
118 
GetLastModificationTime(string strName)119 		internal DateTime? GetLastModificationTime(string strName)
120 		{
121 			if(strName == null) { Debug.Assert(false); throw new ArgumentNullException("strName"); }
122 
123 			if(m_dLastMod == null) return null;
124 
125 			DateTime dt;
126 			if(m_dLastMod.TryGetValue(strName, out dt)) return dt;
127 			return null;
128 		}
129 
Exists(string strName)130 		public bool Exists(string strName)
131 		{
132 			if(strName == null) { Debug.Assert(false); throw new ArgumentNullException("strName"); }
133 
134 			return m_d.ContainsKey(strName);
135 		}
136 
Set(string strName, string strValue)137 		public void Set(string strName, string strValue)
138 		{
139 			if(strName == null) { Debug.Assert(false); throw new ArgumentNullException("strName"); }
140 			if(strValue == null) { Debug.Assert(false); throw new ArgumentNullException("strValue"); }
141 
142 			m_d[strName] = strValue;
143 
144 			if(m_dLastMod != null) m_dLastMod[strName] = DateTime.UtcNow;
145 		}
146 
Set(string strName, string strValue, DateTime? odtLastMod)147 		internal void Set(string strName, string strValue, DateTime? odtLastMod)
148 		{
149 			if(strName == null) { Debug.Assert(false); throw new ArgumentNullException("strName"); }
150 			if(strValue == null) { Debug.Assert(false); throw new ArgumentNullException("strValue"); }
151 
152 			m_d[strName] = strValue;
153 
154 			if(m_dLastMod != null)
155 			{
156 				if(odtLastMod.HasValue) m_dLastMod[strName] = odtLastMod.Value;
157 				else m_dLastMod.Remove(strName);
158 			}
159 		}
160 
Remove(string strName)161 		public bool Remove(string strName)
162 		{
163 			if(strName == null) { Debug.Assert(false); throw new ArgumentNullException("strName"); }
164 
165 			if(m_dLastMod != null) m_dLastMod.Remove(strName);
166 
167 			return m_d.Remove(strName);
168 		}
169 	}
170 }
171