Replace (most) placeholders before copying to clipboard/keyboard
This commit is contained in:
		
							
								
								
									
										229
									
								
								src/keepass2android/Utils/EntryUtil.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										229
									
								
								src/keepass2android/Utils/EntryUtil.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,229 @@
 | 
			
		||||
/*
 | 
			
		||||
  KeePass Password Safe - The Open-Source Password Manager
 | 
			
		||||
  Copyright (C) 2003-2013 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.Diagnostics;
 | 
			
		||||
using System.IO;
 | 
			
		||||
using System.IO.Compression;
 | 
			
		||||
using System.Security.Cryptography;
 | 
			
		||||
 | 
			
		||||
using KeePass.Util.Spr;
 | 
			
		||||
 | 
			
		||||
using KeePassLib;
 | 
			
		||||
using KeePassLib.Collections;
 | 
			
		||||
using KeePassLib.Cryptography;
 | 
			
		||||
using KeePassLib.Cryptography.PasswordGenerator;
 | 
			
		||||
using KeePassLib.Delegates;
 | 
			
		||||
using KeePassLib.Security;
 | 
			
		||||
using KeePassLib.Utility;
 | 
			
		||||
using KeePassLib.Serialization;
 | 
			
		||||
 | 
			
		||||
namespace KeePass.Util
 | 
			
		||||
{
 | 
			
		||||
	/// <summary>
 | 
			
		||||
	/// This class contains various static functions for entry operations.
 | 
			
		||||
	/// </summary>
 | 
			
		||||
	public static class EntryUtil
 | 
			
		||||
	{
 | 
			
		||||
 | 
			
		||||
		// Old format name (<= 2.14): "KeePassEntriesCF"
 | 
			
		||||
		public const string ClipFormatEntries = "KeePassEntriesCX";
 | 
			
		||||
		private static byte[] AdditionalEntropy = { 0xF8, 0x03, 0xFA, 0x51, 0x87, 0x18, 0x49, 0x5D };
 | 
			
		||||
 | 
			
		||||
		public static string FillPlaceholders(string strText, SprContext ctx)
 | 
			
		||||
		{
 | 
			
		||||
			if((ctx == null) || (ctx.Entry == null)) return strText;
 | 
			
		||||
 | 
			
		||||
			string str = strText;
 | 
			
		||||
 | 
			
		||||
		/*NOT SUPPORTED CURRENTLY	if((ctx.Flags & SprCompileFlags.NewPassword) != SprCompileFlags.None)
 | 
			
		||||
				str = ReplaceNewPasswordPlaceholder(str, ctx);
 | 
			
		||||
*/
 | 
			
		||||
			if((ctx.Flags & SprCompileFlags.HmacOtp) != SprCompileFlags.None)
 | 
			
		||||
				str = ReplaceHmacOtpPlaceholder(str, ctx);
 | 
			
		||||
 | 
			
		||||
			return str;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
/*		private static string ReplaceNewPasswordPlaceholder(string strText,
 | 
			
		||||
			SprContext ctx)
 | 
			
		||||
		{
 | 
			
		||||
			PwEntry pe = ctx.Entry;
 | 
			
		||||
			PwDatabase pd = ctx.Database;
 | 
			
		||||
			if((pe == null) || (pd == null)) return strText;
 | 
			
		||||
 | 
			
		||||
			string str = strText;
 | 
			
		||||
 | 
			
		||||
			const string strNewPwPlh = @"{NEWPASSWORD}";
 | 
			
		||||
			if(str.IndexOf(strNewPwPlh, StrUtil.CaseIgnoreCmp) >= 0)
 | 
			
		||||
			{
 | 
			
		||||
				ProtectedString psAutoGen;
 | 
			
		||||
				PwgError e = PwGenerator.Generate(out psAutoGen,
 | 
			
		||||
					Program.Config.PasswordGenerator.AutoGeneratedPasswordsProfile,
 | 
			
		||||
					null, Program.PwGeneratorPool);
 | 
			
		||||
				psAutoGen = psAutoGen.WithProtection(pd.MemoryProtection.ProtectPassword);
 | 
			
		||||
 | 
			
		||||
				if(e == PwgError.Success)
 | 
			
		||||
				{
 | 
			
		||||
					pe.CreateBackup(pd);
 | 
			
		||||
					pe.Strings.Set(PwDefs.PasswordField, psAutoGen);
 | 
			
		||||
					pe.Touch(true, false);
 | 
			
		||||
					pd.Modified = true;
 | 
			
		||||
 | 
			
		||||
					string strIns = SprEngine.TransformContent(psAutoGen.ReadString(), ctx);
 | 
			
		||||
					str = StrUtil.ReplaceCaseInsensitive(str, strNewPwPlh, strIns);
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			return str;
 | 
			
		||||
		}
 | 
			
		||||
*/
 | 
			
		||||
		private static string ReplaceHmacOtpPlaceholder(string strText,
 | 
			
		||||
			SprContext ctx)
 | 
			
		||||
		{
 | 
			
		||||
			PwEntry pe = ctx.Entry;
 | 
			
		||||
			PwDatabase pd = ctx.Database;
 | 
			
		||||
			if((pe == null) || (pd == null)) return strText;
 | 
			
		||||
 | 
			
		||||
			string str = strText;
 | 
			
		||||
 | 
			
		||||
			const string strHmacOtpPlh = @"{HMACOTP}";
 | 
			
		||||
			if(str.IndexOf(strHmacOtpPlh, StrUtil.CaseIgnoreCmp) >= 0)
 | 
			
		||||
			{
 | 
			
		||||
				const string strKeyField = "HmacOtp-Secret";
 | 
			
		||||
				const string strCounterField = "HmacOtp-Counter";
 | 
			
		||||
 | 
			
		||||
				byte[] pbSecret = StrUtil.Utf8.GetBytes(pe.Strings.ReadSafe(
 | 
			
		||||
					strKeyField));
 | 
			
		||||
 | 
			
		||||
				string strCounter = pe.Strings.ReadSafe(strCounterField);
 | 
			
		||||
				ulong uCounter;
 | 
			
		||||
				ulong.TryParse(strCounter, out uCounter);
 | 
			
		||||
 | 
			
		||||
				string strValue = HmacOtp.Generate(pbSecret, uCounter, 6, false, -1);
 | 
			
		||||
 | 
			
		||||
				pe.Strings.Set(strCounterField, new ProtectedString(false,
 | 
			
		||||
					(uCounter + 1).ToString()));
 | 
			
		||||
				pd.Modified = true;
 | 
			
		||||
 | 
			
		||||
				str = StrUtil.ReplaceCaseInsensitive(str, strHmacOtpPlh, strValue);
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			return str;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		public static bool EntriesHaveSameParent(PwObjectList<PwEntry> v)
 | 
			
		||||
		{
 | 
			
		||||
			if(v == null) { Debug.Assert(false); return true; }
 | 
			
		||||
			if(v.UCount == 0) return true;
 | 
			
		||||
 | 
			
		||||
			PwGroup pg = v.GetAt(0).ParentGroup;
 | 
			
		||||
			foreach(PwEntry pe in v)
 | 
			
		||||
			{
 | 
			
		||||
				if(pe.ParentGroup != pg) return false;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			return true;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		public static void ReorderEntriesAsInDatabase(PwObjectList<PwEntry> v,
 | 
			
		||||
			PwDatabase pd)
 | 
			
		||||
		{
 | 
			
		||||
			if((v == null) || (pd == null)) { Debug.Assert(false); return; }
 | 
			
		||||
			if(pd.RootGroup == null) { Debug.Assert(false); return; } // DB must be open
 | 
			
		||||
 | 
			
		||||
			PwObjectList<PwEntry> vRem = v.CloneShallow();
 | 
			
		||||
			v.Clear();
 | 
			
		||||
 | 
			
		||||
			EntryHandler eh = delegate(PwEntry pe)
 | 
			
		||||
			{
 | 
			
		||||
				int p = vRem.IndexOf(pe);
 | 
			
		||||
				if(p >= 0)
 | 
			
		||||
				{
 | 
			
		||||
					v.Add(pe);
 | 
			
		||||
					vRem.RemoveAt((uint)p);
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				return true;
 | 
			
		||||
			};
 | 
			
		||||
 | 
			
		||||
			pd.RootGroup.TraverseTree(TraversalMethod.PreOrder, null, eh);
 | 
			
		||||
 | 
			
		||||
			foreach(PwEntry peRem in vRem) v.Add(peRem); // Entries not found
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
		public static string CreateSummaryList(PwGroup pgItems, bool bStartWithNewPar)
 | 
			
		||||
		{
 | 
			
		||||
			List<PwEntry> l = pgItems.GetEntries(true).CloneShallowToList();
 | 
			
		||||
			string str = CreateSummaryList(pgItems, l.ToArray());
 | 
			
		||||
 | 
			
		||||
			if((str.Length == 0) || !bStartWithNewPar) return str;
 | 
			
		||||
			return (MessageService.NewParagraph + str);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		public static string CreateSummaryList(PwGroup pgSubGroups, PwEntry[] vEntries)
 | 
			
		||||
		{
 | 
			
		||||
			int nMaxEntries = 10;
 | 
			
		||||
			string strSummary = string.Empty;
 | 
			
		||||
 | 
			
		||||
			if(pgSubGroups != null)
 | 
			
		||||
			{
 | 
			
		||||
				PwObjectList<PwGroup> vGroups = pgSubGroups.GetGroups(true);
 | 
			
		||||
				if(vGroups.UCount > 0)
 | 
			
		||||
				{
 | 
			
		||||
					StringBuilder sbGroups = new StringBuilder();
 | 
			
		||||
					sbGroups.Append("- ");
 | 
			
		||||
					uint uToList = Math.Min(3U, vGroups.UCount);
 | 
			
		||||
					for(uint u = 0; u < uToList; ++u)
 | 
			
		||||
					{
 | 
			
		||||
						if(sbGroups.Length > 2) sbGroups.Append(", ");
 | 
			
		||||
						sbGroups.Append(vGroups.GetAt(u).Name);
 | 
			
		||||
					}
 | 
			
		||||
					if(uToList < vGroups.UCount) sbGroups.Append(", ...");
 | 
			
		||||
					strSummary += sbGroups.ToString(); // New line below
 | 
			
		||||
 | 
			
		||||
					nMaxEntries -= 2;
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			int nSummaryShow = Math.Min(nMaxEntries, vEntries.Length);
 | 
			
		||||
			if(nSummaryShow == (vEntries.Length - 1)) --nSummaryShow; // Plural msg
 | 
			
		||||
 | 
			
		||||
			for(int iSumEnum = 0; iSumEnum < nSummaryShow; ++iSumEnum)
 | 
			
		||||
			{
 | 
			
		||||
				if(strSummary.Length > 0) strSummary += MessageService.NewLine;
 | 
			
		||||
 | 
			
		||||
				PwEntry pe = vEntries[iSumEnum];
 | 
			
		||||
				strSummary += ("- " + StrUtil.CompactString3Dots(
 | 
			
		||||
					pe.Strings.ReadSafe(PwDefs.TitleField), 39));
 | 
			
		||||
				if(PwDefs.IsTanEntry(pe))
 | 
			
		||||
				{
 | 
			
		||||
					string strTanIdx = pe.Strings.ReadSafe(PwDefs.UserNameField);
 | 
			
		||||
					if(!string.IsNullOrEmpty(strTanIdx))
 | 
			
		||||
						strSummary += (@" (#" + strTanIdx + @")");
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			return strSummary;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										209
									
								
								src/keepass2android/Utils/Spr/SprContext.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										209
									
								
								src/keepass2android/Utils/Spr/SprContext.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,209 @@
 | 
			
		||||
/*
 | 
			
		||||
  KeePass Password Safe - The Open-Source Password Manager
 | 
			
		||||
  Copyright (C) 2003-2013 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.Diagnostics;
 | 
			
		||||
 | 
			
		||||
using KeePassLib;
 | 
			
		||||
using KeePassLib.Interfaces;
 | 
			
		||||
 | 
			
		||||
using SprRefsCache = System.Collections.Generic.Dictionary<string, string>;
 | 
			
		||||
 | 
			
		||||
namespace KeePass.Util.Spr
 | 
			
		||||
{
 | 
			
		||||
	[Flags]
 | 
			
		||||
	public enum SprCompileFlags
 | 
			
		||||
	{
 | 
			
		||||
		None = 0,
 | 
			
		||||
 | 
			
		||||
		AppPaths = 0x1, // Paths to IE, Firefox, Opera, ...
 | 
			
		||||
		PickChars = 0x2,
 | 
			
		||||
		EntryStrings = 0x4,
 | 
			
		||||
		EntryStringsSpecial = 0x8, // {URL:RMVSCM}, ...
 | 
			
		||||
		PasswordEnc = 0x10,
 | 
			
		||||
		Group = 0x20,
 | 
			
		||||
		Paths = 0x40, // App-dir, doc-dir, path sep, ...
 | 
			
		||||
		AutoType = 0x80, // Replacements like {CLEARFIELD}, ...
 | 
			
		||||
		DateTime = 0x100,
 | 
			
		||||
		References = 0x200,
 | 
			
		||||
		EnvVars = 0x400,
 | 
			
		||||
		NewPassword = 0x800,
 | 
			
		||||
		HmacOtp = 0x1000,
 | 
			
		||||
		Comments = 0x2000,
 | 
			
		||||
 | 
			
		||||
		ExtActive = 0x4000, // Active transformations provided by plugins
 | 
			
		||||
		ExtNonActive = 0x8000, // Non-active transformations provided by plugins
 | 
			
		||||
 | 
			
		||||
		// Next free: 0x10000
 | 
			
		||||
		All = 0xFFFF,
 | 
			
		||||
 | 
			
		||||
		// Internal:
 | 
			
		||||
		UIInteractive = SprCompileFlags.PickChars,
 | 
			
		||||
		StateChanging = (SprCompileFlags.NewPassword | SprCompileFlags.HmacOtp),
 | 
			
		||||
 | 
			
		||||
		Active = (SprCompileFlags.UIInteractive | SprCompileFlags.StateChanging |
 | 
			
		||||
			SprCompileFlags.ExtActive),
 | 
			
		||||
		NonActive = (SprCompileFlags.All & ~SprCompileFlags.Active),
 | 
			
		||||
 | 
			
		||||
		Deref = (SprCompileFlags.EntryStrings | SprCompileFlags.EntryStringsSpecial |
 | 
			
		||||
			SprCompileFlags.References)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public sealed class SprContext
 | 
			
		||||
	{
 | 
			
		||||
		private PwEntry m_pe = null;
 | 
			
		||||
		public PwEntry Entry
 | 
			
		||||
		{
 | 
			
		||||
			get { return m_pe; }
 | 
			
		||||
			set { m_pe = value; }
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		private PwDatabase m_pd = null;
 | 
			
		||||
		public PwDatabase Database
 | 
			
		||||
		{
 | 
			
		||||
			get { return m_pd; }
 | 
			
		||||
			set { m_pd = value; }
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		private bool m_bMakeAT = false;
 | 
			
		||||
		public bool EncodeAsAutoTypeSequence
 | 
			
		||||
		{
 | 
			
		||||
			get { return m_bMakeAT; }
 | 
			
		||||
			set { m_bMakeAT = value; }
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		private bool m_bMakeCmdQuotes = false;
 | 
			
		||||
		public bool EncodeQuotesForCommandLine
 | 
			
		||||
		{
 | 
			
		||||
			get { return m_bMakeCmdQuotes; }
 | 
			
		||||
			set { m_bMakeCmdQuotes = value; }
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		private bool m_bForcePlainTextPasswords = true;
 | 
			
		||||
		public bool ForcePlainTextPasswords
 | 
			
		||||
		{
 | 
			
		||||
			get { return m_bForcePlainTextPasswords; }
 | 
			
		||||
			set { m_bForcePlainTextPasswords = value; }
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		private SprCompileFlags m_flags = SprCompileFlags.All;
 | 
			
		||||
		public SprCompileFlags Flags
 | 
			
		||||
		{
 | 
			
		||||
			get { return m_flags; }
 | 
			
		||||
			set { m_flags = value; }
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		private SprRefsCache m_refsCache = new SprRefsCache();
 | 
			
		||||
		/// <summary>
 | 
			
		||||
		/// Used internally by <c>SprEngine</c>; don't modify it.
 | 
			
		||||
		/// </summary>
 | 
			
		||||
		internal SprRefsCache RefsCache
 | 
			
		||||
		{
 | 
			
		||||
			get { return m_refsCache; }
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// private bool m_bNoUrlSchemeOnce = false;
 | 
			
		||||
		// /// <summary>
 | 
			
		||||
		// /// Used internally by <c>SprEngine</c>; don't modify it.
 | 
			
		||||
		// /// </summary>
 | 
			
		||||
		// internal bool UrlRemoveSchemeOnce
 | 
			
		||||
		// {
 | 
			
		||||
		//	get { return m_bNoUrlSchemeOnce; }
 | 
			
		||||
		//	set { m_bNoUrlSchemeOnce = value; }
 | 
			
		||||
		// }
 | 
			
		||||
 | 
			
		||||
		public SprContext() { }
 | 
			
		||||
 | 
			
		||||
		public SprContext(PwEntry pe, PwDatabase pd, SprCompileFlags fl)
 | 
			
		||||
		{
 | 
			
		||||
			Init(pe, pd, false, false, fl);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		public SprContext(PwEntry pe, PwDatabase pd, SprCompileFlags fl,
 | 
			
		||||
			bool bEncodeAsAutoTypeSequence, bool bEncodeQuotesForCommandLine)
 | 
			
		||||
		{
 | 
			
		||||
			Init(pe, pd, bEncodeAsAutoTypeSequence, bEncodeQuotesForCommandLine, fl);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		private void Init(PwEntry pe, PwDatabase pd, bool bAT, bool bCmdQuotes,
 | 
			
		||||
			SprCompileFlags fl)
 | 
			
		||||
		{
 | 
			
		||||
			m_pe = pe;
 | 
			
		||||
			m_pd = pd;
 | 
			
		||||
			m_bMakeAT = bAT;
 | 
			
		||||
			m_bMakeCmdQuotes = bCmdQuotes;
 | 
			
		||||
			m_flags = fl;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		public SprContext Clone()
 | 
			
		||||
		{
 | 
			
		||||
			return (SprContext)this.MemberwiseClone();
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		/// <summary>
 | 
			
		||||
		/// Used by <c>SprEngine</c> internally; do not use.
 | 
			
		||||
		/// </summary>
 | 
			
		||||
		internal SprContext WithoutContentTransformations()
 | 
			
		||||
		{
 | 
			
		||||
			SprContext ctx = Clone();
 | 
			
		||||
 | 
			
		||||
			ctx.m_bMakeAT = false;
 | 
			
		||||
			ctx.m_bMakeCmdQuotes = false;
 | 
			
		||||
			// ctx.m_bNoUrlSchemeOnce = false;
 | 
			
		||||
 | 
			
		||||
			Debug.Assert(object.ReferenceEquals(m_pe, ctx.m_pe));
 | 
			
		||||
			Debug.Assert(object.ReferenceEquals(m_pd, ctx.m_pd));
 | 
			
		||||
			Debug.Assert(object.ReferenceEquals(m_refsCache, ctx.m_refsCache));
 | 
			
		||||
			return ctx;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public sealed class SprEventArgs : EventArgs
 | 
			
		||||
	{
 | 
			
		||||
		private string m_str = string.Empty;
 | 
			
		||||
		public string Text
 | 
			
		||||
		{
 | 
			
		||||
			get { return m_str; }
 | 
			
		||||
			set
 | 
			
		||||
			{
 | 
			
		||||
				if(value == null) throw new ArgumentNullException("value");
 | 
			
		||||
				m_str = value;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		private SprContext m_ctx = null;
 | 
			
		||||
		public SprContext Context
 | 
			
		||||
		{
 | 
			
		||||
			get { return m_ctx; }
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		public SprEventArgs() { }
 | 
			
		||||
 | 
			
		||||
		public SprEventArgs(string strText, SprContext ctx)
 | 
			
		||||
		{
 | 
			
		||||
			if(strText == null) throw new ArgumentNullException("strText");
 | 
			
		||||
			// ctx == null is allowed
 | 
			
		||||
 | 
			
		||||
			m_str = strText;
 | 
			
		||||
			m_ctx = ctx;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										75
									
								
								src/keepass2android/Utils/Spr/SprEncoding.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										75
									
								
								src/keepass2android/Utils/Spr/SprEncoding.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,75 @@
 | 
			
		||||
/*
 | 
			
		||||
  KeePass Password Safe - The Open-Source Password Manager
 | 
			
		||||
  Copyright (C) 2003-2013 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.Diagnostics;
 | 
			
		||||
 | 
			
		||||
namespace KeePass.Util.Spr
 | 
			
		||||
{
 | 
			
		||||
	internal static class SprEncoding
 | 
			
		||||
	{
 | 
			
		||||
		internal static string MakeAutoTypeSequence(string str)
 | 
			
		||||
		{
 | 
			
		||||
			if(str == null) { Debug.Assert(false); return string.Empty; }
 | 
			
		||||
 | 
			
		||||
			str = SprEncoding.EscapeAutoTypeBrackets(str);
 | 
			
		||||
 | 
			
		||||
			str = str.Replace(@"[", @"{[}");
 | 
			
		||||
			str = str.Replace(@"]", @"{]}");
 | 
			
		||||
 | 
			
		||||
			str = str.Replace(@"+", @"{+}");
 | 
			
		||||
			str = str.Replace(@"%", @"{%}");
 | 
			
		||||
			str = str.Replace(@"~", @"{~}");
 | 
			
		||||
			str = str.Replace(@"(", @"{(}");
 | 
			
		||||
			str = str.Replace(@")", @"{)}");
 | 
			
		||||
 | 
			
		||||
			str = str.Replace(@"^", @"{^}");
 | 
			
		||||
 | 
			
		||||
			return str;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		private static string EscapeAutoTypeBrackets(string str)
 | 
			
		||||
		{
 | 
			
		||||
			char chOpen = '\u25A1';
 | 
			
		||||
			while(str.IndexOf(chOpen) >= 0) ++chOpen;
 | 
			
		||||
 | 
			
		||||
			char chClose = chOpen;
 | 
			
		||||
			++chClose;
 | 
			
		||||
			while(str.IndexOf(chClose) >= 0) ++chClose;
 | 
			
		||||
 | 
			
		||||
			str = str.Replace('{', chOpen);
 | 
			
		||||
			str = str.Replace('}', chClose);
 | 
			
		||||
 | 
			
		||||
			str = str.Replace(new string(chOpen, 1), @"{{}");
 | 
			
		||||
			str = str.Replace(new string(chClose, 1), @"{}}");
 | 
			
		||||
 | 
			
		||||
			return str;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		internal static string MakeCommandQuotes(string str)
 | 
			
		||||
		{
 | 
			
		||||
			if(str == null) { Debug.Assert(false); return string.Empty; }
 | 
			
		||||
 | 
			
		||||
			// See SHELLEXECUTEINFO structure documentation
 | 
			
		||||
			return str.Replace("\"", "\"\"\"");
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										153
									
								
								src/keepass2android/Utils/Spr/SprEngine.PickChars.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										153
									
								
								src/keepass2android/Utils/Spr/SprEngine.PickChars.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,153 @@
 | 
			
		||||
/*
 | 
			
		||||
  KeePass Password Safe - The Open-Source Password Manager
 | 
			
		||||
  Copyright (C) 2003-2013 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.Diagnostics;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
using KeePassLib;
 | 
			
		||||
using KeePassLib.Security;
 | 
			
		||||
using KeePassLib.Utility;
 | 
			
		||||
 | 
			
		||||
namespace KeePass.Util.Spr
 | 
			
		||||
{
 | 
			
		||||
	/// <summary>
 | 
			
		||||
	/// String placeholders and field reference replacement engine.
 | 
			
		||||
	/// </summary>
 | 
			
		||||
	public static partial class SprEngine
 | 
			
		||||
	{
 | 
			
		||||
		// Legacy, for backward compatibility only; see PickChars
 | 
			
		||||
		private static string ReplacePickPw(string strText, SprContext ctx,
 | 
			
		||||
			uint uRecursionLevel)
 | 
			
		||||
		{
 | 
			
		||||
			if(ctx.Entry == null) { Debug.Assert(false); return strText; }
 | 
			
		||||
 | 
			
		||||
			string str = strText;
 | 
			
		||||
 | 
			
		||||
			while(true)
 | 
			
		||||
			{
 | 
			
		||||
				const string strStart = @"{PICKPASSWORDCHARS";
 | 
			
		||||
 | 
			
		||||
				int iStart = str.IndexOf(strStart, StrUtil.CaseIgnoreCmp);
 | 
			
		||||
				if(iStart < 0) break;
 | 
			
		||||
 | 
			
		||||
				int iEnd = str.IndexOf('}', iStart);
 | 
			
		||||
				if(iEnd < 0) break;
 | 
			
		||||
 | 
			
		||||
				string strPlaceholder = str.Substring(iStart, iEnd - iStart + 1);
 | 
			
		||||
 | 
			
		||||
				string strParam = str.Substring(iStart + strStart.Length,
 | 
			
		||||
					iEnd - (iStart + strStart.Length));
 | 
			
		||||
				string[] vParams = strParam.Split(new char[] { ':' });
 | 
			
		||||
 | 
			
		||||
				uint uCharCount = 0;
 | 
			
		||||
				if(vParams.Length >= 2) uint.TryParse(vParams[1], out uCharCount);
 | 
			
		||||
 | 
			
		||||
				str = ReplacePickPwPlaceholder(str, strPlaceholder, uCharCount,
 | 
			
		||||
					ctx, uRecursionLevel);
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			return str;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		private static string ReplacePickPwPlaceholder(string str,
 | 
			
		||||
			string strPlaceholder, uint uCharCount, SprContext ctx,
 | 
			
		||||
			uint uRecursionLevel)
 | 
			
		||||
		{
 | 
			
		||||
			if(str.IndexOf(strPlaceholder, StrUtil.CaseIgnoreCmp) < 0) return str;
 | 
			
		||||
 | 
			
		||||
			ProtectedString ps = ctx.Entry.Strings.Get(PwDefs.PasswordField);
 | 
			
		||||
			if(ps != null)
 | 
			
		||||
			{
 | 
			
		||||
				string strPassword = ps.ReadString();
 | 
			
		||||
 | 
			
		||||
				string strPick = SprEngine.CompileInternal(strPassword,
 | 
			
		||||
					ctx.WithoutContentTransformations(), uRecursionLevel + 1);
 | 
			
		||||
 | 
			
		||||
				if(!string.IsNullOrEmpty(strPick))
 | 
			
		||||
				{
 | 
			
		||||
					ProtectedString psPick = new ProtectedString(false, strPick);
 | 
			
		||||
					string strPicked = string.Empty;
 | 
			
		||||
 | 
			
		||||
					str = StrUtil.ReplaceCaseInsensitive(str, strPlaceholder,
 | 
			
		||||
						SprEngine.TransformContent(strPicked, ctx));
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			return StrUtil.ReplaceCaseInsensitive(str, strPlaceholder, string.Empty);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
		private static string ConvertToDownArrows(string str, int iOffset,
 | 
			
		||||
			string strLayout)
 | 
			
		||||
		{
 | 
			
		||||
			if(string.IsNullOrEmpty(str)) return string.Empty;
 | 
			
		||||
 | 
			
		||||
			StringBuilder sb = new StringBuilder();
 | 
			
		||||
			for(int i = 0; i < str.Length; ++i)
 | 
			
		||||
			{
 | 
			
		||||
				// if((sb.Length > 0) && !string.IsNullOrEmpty(strSep)) sb.Append(strSep);
 | 
			
		||||
 | 
			
		||||
				char ch = str[i];
 | 
			
		||||
 | 
			
		||||
				int? iDowns = null;
 | 
			
		||||
				if(strLayout.Length == 0)
 | 
			
		||||
				{
 | 
			
		||||
					if((ch >= '0') && (ch <= '9')) iDowns = (int)ch - '0';
 | 
			
		||||
					else if((ch >= 'a') && (ch <= 'z')) iDowns = (int)ch - 'a';
 | 
			
		||||
					else if((ch >= 'A') && (ch <= 'Z')) iDowns = (int)ch - 'A';
 | 
			
		||||
				}
 | 
			
		||||
				else if(strLayout.Equals("0a", StrUtil.CaseIgnoreCmp))
 | 
			
		||||
				{
 | 
			
		||||
					if((ch >= '0') && (ch <= '9')) iDowns = (int)ch - '0';
 | 
			
		||||
					else if((ch >= 'a') && (ch <= 'z')) iDowns = (int)ch - 'a' + 10;
 | 
			
		||||
					else if((ch >= 'A') && (ch <= 'Z')) iDowns = (int)ch - 'A' + 10;
 | 
			
		||||
				}
 | 
			
		||||
				else if(strLayout.Equals("a0", StrUtil.CaseIgnoreCmp))
 | 
			
		||||
				{
 | 
			
		||||
					if((ch >= '0') && (ch <= '9')) iDowns = (int)ch - '0' + 26;
 | 
			
		||||
					else if((ch >= 'a') && (ch <= 'z')) iDowns = (int)ch - 'a';
 | 
			
		||||
					else if((ch >= 'A') && (ch <= 'Z')) iDowns = (int)ch - 'A';
 | 
			
		||||
				}
 | 
			
		||||
				else if(strLayout.Equals("1a", StrUtil.CaseIgnoreCmp))
 | 
			
		||||
				{
 | 
			
		||||
					if((ch >= '1') && (ch <= '9')) iDowns = (int)ch - '1';
 | 
			
		||||
					else if(ch == '0') iDowns = 9;
 | 
			
		||||
					else if((ch >= 'a') && (ch <= 'z')) iDowns = (int)ch - 'a' + 10;
 | 
			
		||||
					else if((ch >= 'A') && (ch <= 'Z')) iDowns = (int)ch - 'A' + 10;
 | 
			
		||||
				}
 | 
			
		||||
				else if(strLayout.Equals("a1", StrUtil.CaseIgnoreCmp))
 | 
			
		||||
				{
 | 
			
		||||
					if((ch >= '1') && (ch <= '9')) iDowns = (int)ch - '1' + 26;
 | 
			
		||||
					else if(ch == '0') iDowns = 9 + 26;
 | 
			
		||||
					else if((ch >= 'a') && (ch <= 'z')) iDowns = (int)ch - 'a';
 | 
			
		||||
					else if((ch >= 'A') && (ch <= 'Z')) iDowns = (int)ch - 'A';
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				if(!iDowns.HasValue) continue;
 | 
			
		||||
 | 
			
		||||
				for(int j = 0; j < (iOffset + iDowns); ++j) sb.Append(@"{DOWN}");
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			return sb.ToString();
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										607
									
								
								src/keepass2android/Utils/Spr/SprEngine.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										607
									
								
								src/keepass2android/Utils/Spr/SprEngine.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,607 @@
 | 
			
		||||
/*
 | 
			
		||||
  KeePass Password Safe - The Open-Source Password Manager
 | 
			
		||||
  Copyright (C) 2003-2013 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;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Text;
 | 
			
		||||
using System.IO;
 | 
			
		||||
using System.Diagnostics;
 | 
			
		||||
 | 
			
		||||
using KeePassLib;
 | 
			
		||||
using KeePassLib.Collections;
 | 
			
		||||
using KeePassLib.Security;
 | 
			
		||||
using KeePassLib.Utility;
 | 
			
		||||
using keepass2android;
 | 
			
		||||
 | 
			
		||||
namespace KeePass.Util.Spr
 | 
			
		||||
{
 | 
			
		||||
	/// <summary>
 | 
			
		||||
	/// String placeholders and field reference replacement engine.
 | 
			
		||||
	/// </summary>
 | 
			
		||||
	public static partial class SprEngine
 | 
			
		||||
	{
 | 
			
		||||
		private const uint MaxRecursionDepth = 12;
 | 
			
		||||
		private const StringComparison ScMethod = StringComparison.OrdinalIgnoreCase;
 | 
			
		||||
 | 
			
		||||
		// private static readonly char[] m_vPlhEscapes = new char[] { '{', '}', '%' };
 | 
			
		||||
 | 
			
		||||
		// Important notes for plugin developers subscribing to the following events:
 | 
			
		||||
		// * If possible, prefer subscribing to FilterCompile instead of
 | 
			
		||||
		//   FilterCompilePre.
 | 
			
		||||
		// * If your plugin provides an active transformation (e.g. replacing a
 | 
			
		||||
		//   placeholder that changes some state or requires UI interaction), you
 | 
			
		||||
		//   must only perform the transformation if the ExtActive bit is set in
 | 
			
		||||
		//   args.Context.Flags of the event arguments object args provided to the
 | 
			
		||||
		//   event handler.
 | 
			
		||||
		// * Non-active transformations should only be performed if the ExtNonActive
 | 
			
		||||
		//   bit is set in args.Context.Flags.
 | 
			
		||||
		// * If your plugin provides a placeholder (like e.g. {EXAMPLE}), you
 | 
			
		||||
		//   should add this placeholder to the FilterPlaceholderHints list
 | 
			
		||||
		//   (e.g. add the string "{EXAMPLE}"). Please remove your strings from
 | 
			
		||||
		//   the list when your plugin is terminated.
 | 
			
		||||
		public static event EventHandler<SprEventArgs> FilterCompilePre;
 | 
			
		||||
		public static event EventHandler<SprEventArgs> FilterCompile;
 | 
			
		||||
 | 
			
		||||
		private static List<string> m_lFilterPlh = new List<string>();
 | 
			
		||||
		// See the events above
 | 
			
		||||
		public static List<string> FilterPlaceholderHints
 | 
			
		||||
		{
 | 
			
		||||
			get { return m_lFilterPlh; }
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		private static void InitializeStatic()
 | 
			
		||||
		{
 | 
			
		||||
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		[Obsolete]
 | 
			
		||||
		public static string Compile(string strText, bool bIsAutoTypeSequence,
 | 
			
		||||
			PwEntry pwEntry, PwDatabase pwDatabase, bool bEscapeForAutoType,
 | 
			
		||||
			bool bEscapeQuotesForCommandLine)
 | 
			
		||||
		{
 | 
			
		||||
			SprContext ctx = new SprContext(pwEntry, pwDatabase, SprCompileFlags.All,
 | 
			
		||||
				bEscapeForAutoType, bEscapeQuotesForCommandLine);
 | 
			
		||||
			return Compile(strText, ctx);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		public static string Compile(string strText, SprContext ctx)
 | 
			
		||||
		{
 | 
			
		||||
			if(strText == null) { Debug.Assert(false); return string.Empty; }
 | 
			
		||||
			if(strText.Length == 0) return string.Empty;
 | 
			
		||||
 | 
			
		||||
			SprEngine.InitializeStatic();
 | 
			
		||||
 | 
			
		||||
			if(ctx == null) ctx = new SprContext();
 | 
			
		||||
			ctx.RefsCache.Clear();
 | 
			
		||||
 | 
			
		||||
			string str = SprEngine.CompileInternal(strText, ctx, 0);
 | 
			
		||||
 | 
			
		||||
			// if(bEscapeForAutoType && !bIsAutoTypeSequence)
 | 
			
		||||
			//	str = SprEncoding.MakeAutoTypeSequence(str);
 | 
			
		||||
 | 
			
		||||
			return str;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		private static string CompileInternal(string strText, SprContext ctx,
 | 
			
		||||
			uint uRecursionLevel)
 | 
			
		||||
		{
 | 
			
		||||
			if(strText == null) { Debug.Assert(false); return string.Empty; }
 | 
			
		||||
			if(ctx == null) { Debug.Assert(false); ctx = new SprContext(); }
 | 
			
		||||
 | 
			
		||||
			if(uRecursionLevel >= SprEngine.MaxRecursionDepth)
 | 
			
		||||
			{
 | 
			
		||||
				Debug.Assert(false); // Most likely a recursive reference
 | 
			
		||||
				return string.Empty; // Do not return strText (endless loop)
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			string str = strText;
 | 
			
		||||
 | 
			
		||||
			bool bExt = ((ctx.Flags & (SprCompileFlags.ExtActive |
 | 
			
		||||
				SprCompileFlags.ExtNonActive)) != SprCompileFlags.None);
 | 
			
		||||
			if(bExt && (SprEngine.FilterCompilePre != null))
 | 
			
		||||
			{
 | 
			
		||||
				SprEventArgs args = new SprEventArgs(str, ctx.Clone());
 | 
			
		||||
				SprEngine.FilterCompilePre(null, args);
 | 
			
		||||
				str = args.Text;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if((ctx.Flags & SprCompileFlags.Comments) != SprCompileFlags.None)
 | 
			
		||||
				str = RemoveComments(str);
 | 
			
		||||
 | 
			
		||||
			if(ctx.Entry != null)
 | 
			
		||||
			{
 | 
			
		||||
				if((ctx.Flags & SprCompileFlags.PickChars) != SprCompileFlags.None)
 | 
			
		||||
					str = ReplacePickPw(str, ctx, uRecursionLevel);
 | 
			
		||||
 | 
			
		||||
				if((ctx.Flags & SprCompileFlags.EntryStrings) != SprCompileFlags.None)
 | 
			
		||||
					str = FillEntryStrings(str, ctx, uRecursionLevel);
 | 
			
		||||
 | 
			
		||||
				if((ctx.Flags & SprCompileFlags.EntryStringsSpecial) != SprCompileFlags.None)
 | 
			
		||||
				{
 | 
			
		||||
					// ctx.UrlRemoveSchemeOnce = true;
 | 
			
		||||
					// str = SprEngine.FillIfExists(str, @"{URL:RMVSCM}",
 | 
			
		||||
					//	ctx.Entry.Strings.GetSafe(PwDefs.UrlField), ctx, uRecursionLevel);
 | 
			
		||||
					// Debug.Assert(!ctx.UrlRemoveSchemeOnce);
 | 
			
		||||
 | 
			
		||||
					str = FillEntryStringsSpecial(str, ctx, uRecursionLevel);
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				if(((ctx.Flags & SprCompileFlags.PasswordEnc) != SprCompileFlags.None) &&
 | 
			
		||||
					(str.IndexOf(@"{PASSWORD_ENC}", SprEngine.ScMethod) >= 0))
 | 
			
		||||
					str = SprEngine.FillIfExists(str, @"{PASSWORD_ENC}", new ProtectedString(false,
 | 
			
		||||
						StrUtil.EncryptString(ctx.Entry.Strings.ReadSafe(PwDefs.PasswordField))),
 | 
			
		||||
						ctx, uRecursionLevel);
 | 
			
		||||
 | 
			
		||||
				if(((ctx.Flags & SprCompileFlags.Group) != SprCompileFlags.None) &&
 | 
			
		||||
					(ctx.Entry.ParentGroup != null))
 | 
			
		||||
				{
 | 
			
		||||
					str = SprEngine.FillIfExists(str, @"{GROUP}", new ProtectedString(
 | 
			
		||||
						false, ctx.Entry.ParentGroup.Name), ctx, uRecursionLevel);
 | 
			
		||||
 | 
			
		||||
					str = SprEngine.FillIfExists(str, @"{GROUPPATH}", new ProtectedString(
 | 
			
		||||
						false, ctx.Entry.ParentGroup.GetFullPath()), ctx, uRecursionLevel);
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
			if(ctx.Database != null)
 | 
			
		||||
			{
 | 
			
		||||
				if((ctx.Flags & SprCompileFlags.Paths) != SprCompileFlags.None)
 | 
			
		||||
				{
 | 
			
		||||
					// For backward compatibility only
 | 
			
		||||
					str = SprEngine.FillIfExists(str, @"{DOCDIR}", new ProtectedString(
 | 
			
		||||
						false, UrlUtil.GetFileDirectory(ctx.Database.IOConnectionInfo.Path,
 | 
			
		||||
						false, false)), ctx, uRecursionLevel);
 | 
			
		||||
 | 
			
		||||
					str = SprEngine.FillIfExists(str, @"{DB_PATH}", new ProtectedString(
 | 
			
		||||
						false, ctx.Database.IOConnectionInfo.Path), ctx, uRecursionLevel);
 | 
			
		||||
					str = SprEngine.FillIfExists(str, @"{DB_DIR}", new ProtectedString(
 | 
			
		||||
						false, UrlUtil.GetFileDirectory(ctx.Database.IOConnectionInfo.Path,
 | 
			
		||||
						false, false)), ctx, uRecursionLevel);
 | 
			
		||||
					str = SprEngine.FillIfExists(str, @"{DB_NAME}", new ProtectedString(
 | 
			
		||||
						false, UrlUtil.GetFileName(ctx.Database.IOConnectionInfo.Path)),
 | 
			
		||||
						ctx, uRecursionLevel);
 | 
			
		||||
					str = SprEngine.FillIfExists(str, @"{DB_BASENAME}", new ProtectedString(
 | 
			
		||||
						false, UrlUtil.StripExtension(UrlUtil.GetFileName(
 | 
			
		||||
						ctx.Database.IOConnectionInfo.Path))), ctx, uRecursionLevel);
 | 
			
		||||
					str = SprEngine.FillIfExists(str, @"{DB_EXT}", new ProtectedString(
 | 
			
		||||
						false, UrlUtil.GetExtension(ctx.Database.IOConnectionInfo.Path)),
 | 
			
		||||
						ctx, uRecursionLevel);
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if((ctx.Flags & SprCompileFlags.Paths) != SprCompileFlags.None)
 | 
			
		||||
			{
 | 
			
		||||
				str = SprEngine.FillIfExists(str, @"{ENV_DIRSEP}", new ProtectedString(
 | 
			
		||||
					false, Path.DirectorySeparatorChar.ToString()), ctx, uRecursionLevel);
 | 
			
		||||
 | 
			
		||||
				string strPF86 = Environment.GetEnvironmentVariable("ProgramFiles(x86)");
 | 
			
		||||
				if(string.IsNullOrEmpty(strPF86))
 | 
			
		||||
					strPF86 = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles);
 | 
			
		||||
				if(strPF86 != null)
 | 
			
		||||
					str = SprEngine.FillIfExists(str, @"{ENV_PROGRAMFILES_X86}",
 | 
			
		||||
						new ProtectedString(false, strPF86), ctx, uRecursionLevel);
 | 
			
		||||
				else { Debug.Assert(false); }
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if((ctx.Flags & SprCompileFlags.AutoType) != SprCompileFlags.None)
 | 
			
		||||
			{
 | 
			
		||||
				str = StrUtil.ReplaceCaseInsensitive(str, @"{CLEARFIELD}",
 | 
			
		||||
					@"{HOME}+({END}){DEL}{DELAY 50}");
 | 
			
		||||
				str = StrUtil.ReplaceCaseInsensitive(str, @"{WIN}", @"{VKEY 91}");
 | 
			
		||||
				str = StrUtil.ReplaceCaseInsensitive(str, @"{LWIN}", @"{VKEY 91}");
 | 
			
		||||
				str = StrUtil.ReplaceCaseInsensitive(str, @"{RWIN}", @"{VKEY 92}");
 | 
			
		||||
				str = StrUtil.ReplaceCaseInsensitive(str, @"{APPS}", @"{VKEY 93}");
 | 
			
		||||
 | 
			
		||||
				for(int np = 0; np < 10; ++np)
 | 
			
		||||
					str = StrUtil.ReplaceCaseInsensitive(str, @"{NUMPAD" +
 | 
			
		||||
						Convert.ToString(np, 10) + @"}", @"{VKEY " +
 | 
			
		||||
						Convert.ToString(np + 0x60, 10) + @"}");
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if((ctx.Flags & SprCompileFlags.DateTime) != SprCompileFlags.None)
 | 
			
		||||
			{
 | 
			
		||||
				DateTime dtNow = DateTime.Now; // Local time
 | 
			
		||||
				str = SprEngine.FillIfExists(str, @"{DT_YEAR}", new ProtectedString(
 | 
			
		||||
					false, dtNow.Year.ToString("D4")), ctx, uRecursionLevel);
 | 
			
		||||
				str = SprEngine.FillIfExists(str, @"{DT_MONTH}", new ProtectedString(
 | 
			
		||||
					false, dtNow.Month.ToString("D2")), ctx, uRecursionLevel);
 | 
			
		||||
				str = SprEngine.FillIfExists(str, @"{DT_DAY}", new ProtectedString(
 | 
			
		||||
					false, dtNow.Day.ToString("D2")), ctx, uRecursionLevel);
 | 
			
		||||
				str = SprEngine.FillIfExists(str, @"{DT_HOUR}", new ProtectedString(
 | 
			
		||||
					false, dtNow.Hour.ToString("D2")), ctx, uRecursionLevel);
 | 
			
		||||
				str = SprEngine.FillIfExists(str, @"{DT_MINUTE}", new ProtectedString(
 | 
			
		||||
					false, dtNow.Minute.ToString("D2")), ctx, uRecursionLevel);
 | 
			
		||||
				str = SprEngine.FillIfExists(str, @"{DT_SECOND}", new ProtectedString(
 | 
			
		||||
					false, dtNow.Second.ToString("D2")), ctx, uRecursionLevel);
 | 
			
		||||
				str = SprEngine.FillIfExists(str, @"{DT_SIMPLE}", new ProtectedString(
 | 
			
		||||
					false, dtNow.ToString("yyyyMMddHHmmss")), ctx, uRecursionLevel);
 | 
			
		||||
 | 
			
		||||
				dtNow = dtNow.ToUniversalTime();
 | 
			
		||||
				str = SprEngine.FillIfExists(str, @"{DT_UTC_YEAR}", new ProtectedString(
 | 
			
		||||
					false, dtNow.Year.ToString("D4")), ctx, uRecursionLevel);
 | 
			
		||||
				str = SprEngine.FillIfExists(str, @"{DT_UTC_MONTH}", new ProtectedString(
 | 
			
		||||
					false, dtNow.Month.ToString("D2")), ctx, uRecursionLevel);
 | 
			
		||||
				str = SprEngine.FillIfExists(str, @"{DT_UTC_DAY}", new ProtectedString(
 | 
			
		||||
					false, dtNow.Day.ToString("D2")), ctx, uRecursionLevel);
 | 
			
		||||
				str = SprEngine.FillIfExists(str, @"{DT_UTC_HOUR}", new ProtectedString(
 | 
			
		||||
					false, dtNow.Hour.ToString("D2")), ctx, uRecursionLevel);
 | 
			
		||||
				str = SprEngine.FillIfExists(str, @"{DT_UTC_MINUTE}", new ProtectedString(
 | 
			
		||||
					false, dtNow.Minute.ToString("D2")), ctx, uRecursionLevel);
 | 
			
		||||
				str = SprEngine.FillIfExists(str, @"{DT_UTC_SECOND}", new ProtectedString(
 | 
			
		||||
					false, dtNow.Second.ToString("D2")), ctx, uRecursionLevel);
 | 
			
		||||
				str = SprEngine.FillIfExists(str, @"{DT_UTC_SIMPLE}", new ProtectedString(
 | 
			
		||||
					false, dtNow.ToString("yyyyMMddHHmmss")), ctx, uRecursionLevel);
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if((ctx.Flags & SprCompileFlags.References) != SprCompileFlags.None)
 | 
			
		||||
				str = SprEngine.FillRefPlaceholders(str, ctx, uRecursionLevel);
 | 
			
		||||
 | 
			
		||||
			if(((ctx.Flags & SprCompileFlags.EnvVars) != SprCompileFlags.None) &&
 | 
			
		||||
				(str.IndexOf('%') >= 0))
 | 
			
		||||
			{
 | 
			
		||||
				// Replace environment variables
 | 
			
		||||
				foreach(DictionaryEntry de in Environment.GetEnvironmentVariables())
 | 
			
		||||
				{
 | 
			
		||||
					string strKey = (de.Key as string);
 | 
			
		||||
					string strValue = (de.Value as string);
 | 
			
		||||
 | 
			
		||||
					if((strKey != null) && (strValue != null))
 | 
			
		||||
						str = SprEngine.FillIfExists(str, @"%" + strKey + @"%",
 | 
			
		||||
							new ProtectedString(false, strValue), ctx, uRecursionLevel);
 | 
			
		||||
					else { Debug.Assert(false); }
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			str = EntryUtil.FillPlaceholders(str, ctx);
 | 
			
		||||
 | 
			
		||||
			if(bExt && (SprEngine.FilterCompile != null))
 | 
			
		||||
			{
 | 
			
		||||
				SprEventArgs args = new SprEventArgs(str, ctx.Clone());
 | 
			
		||||
				SprEngine.FilterCompile(null, args);
 | 
			
		||||
				str = args.Text;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if(ctx.EncodeAsAutoTypeSequence)
 | 
			
		||||
			{
 | 
			
		||||
				str = StrUtil.NormalizeNewLines(str, false);
 | 
			
		||||
				str = str.Replace("\n", @"{ENTER}");
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			return str;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		private static string FillIfExists(string strData, string strPlaceholder,
 | 
			
		||||
			ProtectedString psParsable, SprContext ctx, uint uRecursionLevel)
 | 
			
		||||
		{
 | 
			
		||||
			// // The UrlRemoveSchemeOnce property of ctx must be cleared
 | 
			
		||||
			// // before this method returns and before any recursive call
 | 
			
		||||
			// bool bRemoveScheme = false;
 | 
			
		||||
			// if(ctx != null)
 | 
			
		||||
			// {
 | 
			
		||||
			//	bRemoveScheme = ctx.UrlRemoveSchemeOnce;
 | 
			
		||||
			//	ctx.UrlRemoveSchemeOnce = false;
 | 
			
		||||
			// }
 | 
			
		||||
 | 
			
		||||
			if(strData == null) { Debug.Assert(false); return string.Empty; }
 | 
			
		||||
			if(strPlaceholder == null) { Debug.Assert(false); return strData; }
 | 
			
		||||
			if(strPlaceholder.Length == 0) { Debug.Assert(false); return strData; }
 | 
			
		||||
			if(psParsable == null) { Debug.Assert(false); return strData; }
 | 
			
		||||
 | 
			
		||||
			if(strData.IndexOf(strPlaceholder, SprEngine.ScMethod) >= 0)
 | 
			
		||||
			{
 | 
			
		||||
				string strReplacement = SprEngine.CompileInternal(
 | 
			
		||||
					psParsable.ReadString(), ctx.WithoutContentTransformations(),
 | 
			
		||||
					uRecursionLevel + 1);
 | 
			
		||||
 | 
			
		||||
				// if(bRemoveScheme)
 | 
			
		||||
				//	strReplacement = UrlUtil.RemoveScheme(strReplacement);
 | 
			
		||||
 | 
			
		||||
				return SprEngine.FillPlaceholder(strData, strPlaceholder,
 | 
			
		||||
					strReplacement, ctx);
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			return strData;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		private static string FillPlaceholder(string strData, string strPlaceholder,
 | 
			
		||||
			string strReplaceWith, SprContext ctx)
 | 
			
		||||
		{
 | 
			
		||||
			if(strData == null) { Debug.Assert(false); return string.Empty; }
 | 
			
		||||
			if(strPlaceholder == null) { Debug.Assert(false); return strData; }
 | 
			
		||||
			if(strPlaceholder.Length == 0) { Debug.Assert(false); return strData; }
 | 
			
		||||
			if(strReplaceWith == null) { Debug.Assert(false); return strData; }
 | 
			
		||||
 | 
			
		||||
			return StrUtil.ReplaceCaseInsensitive(strData, strPlaceholder,
 | 
			
		||||
				SprEngine.TransformContent(strReplaceWith, ctx));
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		public static string TransformContent(string strContent, SprContext ctx)
 | 
			
		||||
		{
 | 
			
		||||
			if(strContent == null) { Debug.Assert(false); return string.Empty; }
 | 
			
		||||
 | 
			
		||||
			string str = strContent;
 | 
			
		||||
 | 
			
		||||
			if(ctx != null)
 | 
			
		||||
			{
 | 
			
		||||
				if(ctx.EncodeQuotesForCommandLine)
 | 
			
		||||
					str = SprEncoding.MakeCommandQuotes(str);
 | 
			
		||||
 | 
			
		||||
				if(ctx.EncodeAsAutoTypeSequence)
 | 
			
		||||
					str = SprEncoding.MakeAutoTypeSequence(str);
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			return str;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		private static string FillEntryStrings(string str, SprContext ctx,
 | 
			
		||||
			uint uRecursionLevel)
 | 
			
		||||
		{
 | 
			
		||||
			List<string> vKeys = ctx.Entry.Strings.GetKeys();
 | 
			
		||||
 | 
			
		||||
			// Ensure that all standard field names are in the list
 | 
			
		||||
			// (this is required in order to replace the standard placeholders
 | 
			
		||||
			// even if the corresponding standard field isn't present in
 | 
			
		||||
			// the entry)
 | 
			
		||||
			List<string> vStdNames = PwDefs.GetStandardFields();
 | 
			
		||||
			foreach(string strStdField in vStdNames)
 | 
			
		||||
			{
 | 
			
		||||
				if(!vKeys.Contains(strStdField)) vKeys.Add(strStdField);
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			// Do not directly enumerate the strings in ctx.Entry.Strings,
 | 
			
		||||
			// because strings might change during the Spr compilation
 | 
			
		||||
			foreach(string strField in vKeys)
 | 
			
		||||
			{
 | 
			
		||||
				string strKey = (PwDefs.IsStandardField(strField) ?
 | 
			
		||||
					(@"{" + strField + @"}") :
 | 
			
		||||
					(@"{" + PwDefs.AutoTypeStringPrefix + strField + @"}"));
 | 
			
		||||
 | 
			
		||||
				if(!ctx.ForcePlainTextPasswords && strKey.Equals(@"{" +
 | 
			
		||||
					PwDefs.PasswordField + @"}", StrUtil.CaseIgnoreCmp))
 | 
			
		||||
				{
 | 
			
		||||
					str = SprEngine.FillIfExists(str, strKey, new ProtectedString(
 | 
			
		||||
						false, PwDefs.HiddenPassword), ctx, uRecursionLevel);
 | 
			
		||||
					continue;
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				// Use GetSafe because the field doesn't necessarily exist
 | 
			
		||||
				// (might be a standard field that has been added above)
 | 
			
		||||
				str = SprEngine.FillIfExists(str, strKey, ctx.Entry.Strings.GetSafe(
 | 
			
		||||
					strField), ctx, uRecursionLevel);
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			return str;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		private const string UrlSpecialRmvScm = @"{URL:RMVSCM}";
 | 
			
		||||
		private const string UrlSpecialScm = @"{URL:SCM}";
 | 
			
		||||
		private const string UrlSpecialHost = @"{URL:HOST}";
 | 
			
		||||
		private const string UrlSpecialPort = @"{URL:PORT}";
 | 
			
		||||
		private const string UrlSpecialPath = @"{URL:PATH}";
 | 
			
		||||
		private const string UrlSpecialQuery = @"{URL:QUERY}";
 | 
			
		||||
		private static string FillEntryStringsSpecial(string str, SprContext ctx,
 | 
			
		||||
			uint uRecursionLevel)
 | 
			
		||||
		{
 | 
			
		||||
			if((str.IndexOf(UrlSpecialRmvScm, SprEngine.ScMethod) >= 0) ||
 | 
			
		||||
				(str.IndexOf(UrlSpecialScm, SprEngine.ScMethod) >= 0) ||
 | 
			
		||||
				(str.IndexOf(UrlSpecialHost, SprEngine.ScMethod) >= 0) ||
 | 
			
		||||
				(str.IndexOf(UrlSpecialPort, SprEngine.ScMethod) >= 0) ||
 | 
			
		||||
				(str.IndexOf(UrlSpecialPath, SprEngine.ScMethod) >= 0) ||
 | 
			
		||||
				(str.IndexOf(UrlSpecialQuery, SprEngine.ScMethod) >= 0))
 | 
			
		||||
			{
 | 
			
		||||
				string strUrl = SprEngine.FillIfExists(@"{URL}", @"{URL}",
 | 
			
		||||
					ctx.Entry.Strings.GetSafe(PwDefs.UrlField), ctx, uRecursionLevel);
 | 
			
		||||
 | 
			
		||||
				str = StrUtil.ReplaceCaseInsensitive(str, UrlSpecialRmvScm,
 | 
			
		||||
					UrlUtil.RemoveScheme(strUrl));
 | 
			
		||||
 | 
			
		||||
				try
 | 
			
		||||
				{
 | 
			
		||||
					Uri uri = new Uri(strUrl);
 | 
			
		||||
 | 
			
		||||
					str = StrUtil.ReplaceCaseInsensitive(str, UrlSpecialScm,
 | 
			
		||||
						uri.Scheme);
 | 
			
		||||
					str = StrUtil.ReplaceCaseInsensitive(str, UrlSpecialHost,
 | 
			
		||||
						uri.Host);
 | 
			
		||||
					str = StrUtil.ReplaceCaseInsensitive(str, UrlSpecialPort,
 | 
			
		||||
						uri.Port.ToString());
 | 
			
		||||
					str = StrUtil.ReplaceCaseInsensitive(str, UrlSpecialPath,
 | 
			
		||||
						uri.AbsolutePath);
 | 
			
		||||
					str = StrUtil.ReplaceCaseInsensitive(str, UrlSpecialQuery,
 | 
			
		||||
						uri.Query);
 | 
			
		||||
				}
 | 
			
		||||
				catch(Exception) { } // Invalid URI
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			return str;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		private const string StrRemStart = @"{C:";
 | 
			
		||||
		private const string StrRemEnd = @"}";
 | 
			
		||||
		private static string RemoveComments(string strSeq)
 | 
			
		||||
		{
 | 
			
		||||
			string str = strSeq;
 | 
			
		||||
 | 
			
		||||
			while(true)
 | 
			
		||||
			{
 | 
			
		||||
				int iStart = str.IndexOf(StrRemStart, SprEngine.ScMethod);
 | 
			
		||||
				if(iStart < 0) break;
 | 
			
		||||
				int iEnd = str.IndexOf(StrRemEnd, iStart + 1, SprEngine.ScMethod);
 | 
			
		||||
				if(iEnd <= iStart) break;
 | 
			
		||||
 | 
			
		||||
				str = (str.Substring(0, iStart) + str.Substring(iEnd + StrRemEnd.Length));
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			return str;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		internal const string StrRefStart = @"{REF:";
 | 
			
		||||
		internal const string StrRefEnd = @"}";
 | 
			
		||||
		private static string FillRefPlaceholders(string strSeq, SprContext ctx,
 | 
			
		||||
			uint uRecursionLevel)
 | 
			
		||||
		{
 | 
			
		||||
			if(ctx.Database == null) return strSeq;
 | 
			
		||||
 | 
			
		||||
			string str = strSeq;
 | 
			
		||||
 | 
			
		||||
			int nOffset = 0;
 | 
			
		||||
			for(int iLoop = 0; iLoop < 20; ++iLoop)
 | 
			
		||||
			{
 | 
			
		||||
				str = SprEngine.FillRefsUsingCache(str, ctx);
 | 
			
		||||
 | 
			
		||||
				int nStart = str.IndexOf(StrRefStart, nOffset, SprEngine.ScMethod);
 | 
			
		||||
				if(nStart < 0) break;
 | 
			
		||||
				int nEnd = str.IndexOf(StrRefEnd, nStart + 1, SprEngine.ScMethod);
 | 
			
		||||
				if(nEnd <= nStart) break;
 | 
			
		||||
 | 
			
		||||
				string strFullRef = str.Substring(nStart, nEnd - nStart + 1);
 | 
			
		||||
				char chScan, chWanted;
 | 
			
		||||
				PwEntry peFound = FindRefTarget(strFullRef, ctx, out chScan, out chWanted);
 | 
			
		||||
 | 
			
		||||
				if(peFound != null)
 | 
			
		||||
				{
 | 
			
		||||
					string strInsData;
 | 
			
		||||
					if(chWanted == 'T')
 | 
			
		||||
						strInsData = peFound.Strings.ReadSafe(PwDefs.TitleField);
 | 
			
		||||
					else if(chWanted == 'U')
 | 
			
		||||
						strInsData = peFound.Strings.ReadSafe(PwDefs.UserNameField);
 | 
			
		||||
					else if(chWanted == 'A')
 | 
			
		||||
						strInsData = peFound.Strings.ReadSafe(PwDefs.UrlField);
 | 
			
		||||
					else if(chWanted == 'P')
 | 
			
		||||
						strInsData = peFound.Strings.ReadSafe(PwDefs.PasswordField);
 | 
			
		||||
					else if(chWanted == 'N')
 | 
			
		||||
						strInsData = peFound.Strings.ReadSafe(PwDefs.NotesField);
 | 
			
		||||
					else if(chWanted == 'I')
 | 
			
		||||
						strInsData = peFound.Uuid.ToHexString();
 | 
			
		||||
					else { nOffset = nStart + 1; continue; }
 | 
			
		||||
 | 
			
		||||
					if((chWanted == 'P') && !ctx.ForcePlainTextPasswords)
 | 
			
		||||
						strInsData = PwDefs.HiddenPassword;
 | 
			
		||||
 | 
			
		||||
					SprContext sprSub = ctx.WithoutContentTransformations();
 | 
			
		||||
					sprSub.Entry = peFound;
 | 
			
		||||
 | 
			
		||||
					string strInnerContent = SprEngine.CompileInternal(strInsData,
 | 
			
		||||
						sprSub, uRecursionLevel + 1);
 | 
			
		||||
					strInnerContent = SprEngine.TransformContent(strInnerContent, ctx);
 | 
			
		||||
 | 
			
		||||
					// str = str.Substring(0, nStart) + strInnerContent + str.Substring(nEnd + 1);
 | 
			
		||||
					SprEngine.AddRefToCache(strFullRef, strInnerContent, ctx);
 | 
			
		||||
					str = SprEngine.FillRefsUsingCache(str, ctx);
 | 
			
		||||
				}
 | 
			
		||||
				else { nOffset = nStart + 1; continue; }
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			return str;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		public static PwEntry FindRefTarget(string strFullRef, SprContext ctx,
 | 
			
		||||
			out char chScan, out char chWanted)
 | 
			
		||||
		{
 | 
			
		||||
			chScan = char.MinValue;
 | 
			
		||||
			chWanted = char.MinValue;
 | 
			
		||||
 | 
			
		||||
			if(strFullRef == null) { Debug.Assert(false); return null; }
 | 
			
		||||
			if(!strFullRef.StartsWith(StrRefStart, SprEngine.ScMethod) ||
 | 
			
		||||
				!strFullRef.EndsWith(StrRefEnd, SprEngine.ScMethod))
 | 
			
		||||
				return null;
 | 
			
		||||
			if((ctx == null) || (ctx.Database == null)) { Debug.Assert(false); return null; }
 | 
			
		||||
 | 
			
		||||
			string strRef = strFullRef.Substring(StrRefStart.Length,
 | 
			
		||||
				strFullRef.Length - StrRefStart.Length - StrRefEnd.Length);
 | 
			
		||||
			if(strRef.Length <= 4) return null;
 | 
			
		||||
			if(strRef[1] != '@') return null;
 | 
			
		||||
			if(strRef[3] != ':') return null;
 | 
			
		||||
 | 
			
		||||
			chScan = char.ToUpper(strRef[2]);
 | 
			
		||||
			chWanted = char.ToUpper(strRef[0]);
 | 
			
		||||
 | 
			
		||||
			SearchParameters sp = SearchParameters.None;
 | 
			
		||||
			sp.SearchString = strRef.Substring(4);
 | 
			
		||||
			if(chScan == 'T') sp.SearchInTitles = true;
 | 
			
		||||
			else if(chScan == 'U') sp.SearchInUserNames = true;
 | 
			
		||||
			else if(chScan == 'A') sp.SearchInUrls = true;
 | 
			
		||||
			else if(chScan == 'P') sp.SearchInPasswords = true;
 | 
			
		||||
			else if(chScan == 'N') sp.SearchInNotes = true;
 | 
			
		||||
			else if(chScan == 'I') sp.SearchInUuids = true;
 | 
			
		||||
			else if(chScan == 'O') sp.SearchInOther = true;
 | 
			
		||||
			else return null;
 | 
			
		||||
 | 
			
		||||
			PwObjectList<PwEntry> lFound = new PwObjectList<PwEntry>();
 | 
			
		||||
			ctx.Database.RootGroup.SearchEntries(sp, lFound);
 | 
			
		||||
 | 
			
		||||
			return ((lFound.UCount > 0) ? lFound.GetAt(0) : null);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		private static string FillRefsUsingCache(string strText, SprContext ctx)
 | 
			
		||||
		{
 | 
			
		||||
			string str = strText;
 | 
			
		||||
 | 
			
		||||
			foreach(KeyValuePair<string, string> kvp in ctx.RefsCache)
 | 
			
		||||
			{
 | 
			
		||||
				// str = str.Replace(kvp.Key, kvp.Value);
 | 
			
		||||
				str = StrUtil.ReplaceCaseInsensitive(str, kvp.Key, kvp.Value);
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			return str;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		private static void AddRefToCache(string strRef, string strValue,
 | 
			
		||||
			SprContext ctx)
 | 
			
		||||
		{
 | 
			
		||||
			if(strRef == null) { Debug.Assert(false); return; }
 | 
			
		||||
			if(strValue == null) { Debug.Assert(false); return; }
 | 
			
		||||
			if(ctx == null) { Debug.Assert(false); return; }
 | 
			
		||||
 | 
			
		||||
			// Only add if not exists, do not overwrite
 | 
			
		||||
			if(!ctx.RefsCache.ContainsKey(strRef))
 | 
			
		||||
				ctx.RefsCache.Add(strRef, strValue);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// internal static bool MightChange(string strText)
 | 
			
		||||
		// {
 | 
			
		||||
		//	if(string.IsNullOrEmpty(strText)) return false;
 | 
			
		||||
		//	return (strText.IndexOfAny(m_vPlhEscapes) >= 0);
 | 
			
		||||
		// }
 | 
			
		||||
 | 
			
		||||
		/// <summary>
 | 
			
		||||
		/// Fast probabilistic test whether a string might be
 | 
			
		||||
		/// changed when compiling with <c>SprCompileFlags.Deref</c>.
 | 
			
		||||
		/// </summary>
 | 
			
		||||
		internal static bool MightDeref(string strText)
 | 
			
		||||
		{
 | 
			
		||||
			if(strText == null) return false;
 | 
			
		||||
			return (strText.IndexOf('{') >= 0);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		internal static string DerefFn(string str, PwEntry pe)
 | 
			
		||||
		{
 | 
			
		||||
			if(!MightDeref(str)) return str;
 | 
			
		||||
 | 
			
		||||
			SprContext ctx = new SprContext(pe,
 | 
			
		||||
				App.getDB().pm,
 | 
			
		||||
				SprCompileFlags.Deref);
 | 
			
		||||
			// ctx.ForcePlainTextPasswords = false;
 | 
			
		||||
 | 
			
		||||
			return Compile(str, ctx);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@@ -162,6 +162,11 @@
 | 
			
		||||
    <Compile Include="EntryEditActivityState.cs" />
 | 
			
		||||
    <Compile Include="AttachmentContentProvider.cs" />
 | 
			
		||||
    <Compile Include="app\AppTask.cs" />
 | 
			
		||||
    <Compile Include="Utils\Spr\SprContext.cs" />
 | 
			
		||||
    <Compile Include="Utils\Spr\SprEncoding.cs" />
 | 
			
		||||
    <Compile Include="Utils\Spr\SprEngine.cs" />
 | 
			
		||||
    <Compile Include="Utils\Spr\SprEngine.PickChars.cs" />
 | 
			
		||||
    <Compile Include="Utils\EntryUtil.cs" />
 | 
			
		||||
  </ItemGroup>
 | 
			
		||||
  <ItemGroup>
 | 
			
		||||
    <None Include="Resources\AboutResources.txt" />
 | 
			
		||||
@@ -701,6 +706,7 @@
 | 
			
		||||
    <Folder Include="Resources\values-v14\" />
 | 
			
		||||
    <Folder Include="Resources\layout-v14\" />
 | 
			
		||||
    <Folder Include="Resources\anim\" />
 | 
			
		||||
    <Folder Include="Utils\Spr\" />
 | 
			
		||||
  </ItemGroup>
 | 
			
		||||
  <ItemGroup>
 | 
			
		||||
    <ProjectReference Include="..\KeePassLib2Android\KeePassLib2Android.csproj">
 | 
			
		||||
 
 | 
			
		||||
@@ -31,6 +31,7 @@ using Android.Preferences;
 | 
			
		||||
using KeePassLib;
 | 
			
		||||
using KeePassLib.Utility;
 | 
			
		||||
using Android.Views.InputMethods;
 | 
			
		||||
using KeePass.Util.Spr;
 | 
			
		||||
 | 
			
		||||
namespace keepass2android
 | 
			
		||||
{
 | 
			
		||||
@@ -152,7 +153,7 @@ namespace keepass2android
 | 
			
		||||
			if (prefs.GetBoolean(GetString(Resource.String.CopyToClipboardNotification_key), Resources.GetBoolean(Resource.Boolean.CopyToClipboardNotification_default)))
 | 
			
		||||
			{
 | 
			
		||||
 | 
			
		||||
				if (entry.Strings.ReadSafe(PwDefs.PasswordField).Length > 0)
 | 
			
		||||
				if (GetStringAndReplacePlaceholders(entry, PwDefs.PasswordField).Length > 0)
 | 
			
		||||
				{
 | 
			
		||||
					// only show notification if password is available
 | 
			
		||||
					Notification password = GetNotification(Intents.COPY_PASSWORD, Resource.String.copy_password, Resource.Drawable.notify, entryName);
 | 
			
		||||
@@ -163,7 +164,7 @@ namespace keepass2android
 | 
			
		||||
 | 
			
		||||
				}
 | 
			
		||||
				
 | 
			
		||||
				if (entry.Strings.ReadSafe(PwDefs.UserNameField).Length > 0)
 | 
			
		||||
				if (GetStringAndReplacePlaceholders(entry, PwDefs.UserNameField).Length > 0)
 | 
			
		||||
				{
 | 
			
		||||
					// only show notification if username is available
 | 
			
		||||
					Notification username = GetNotification(Intents.COPY_USERNAME, Resource.String.copy_username, Resource.Drawable.notify, entryName);
 | 
			
		||||
@@ -237,7 +238,8 @@ namespace keepass2android
 | 
			
		||||
			int i=0;
 | 
			
		||||
			foreach (string key in keys)
 | 
			
		||||
			{
 | 
			
		||||
				String value = entry.Strings.ReadSafe(key);
 | 
			
		||||
				String value = GetStringAndReplacePlaceholders(entry, key);
 | 
			
		||||
 | 
			
		||||
				if (value.Length > 0)
 | 
			
		||||
				{
 | 
			
		||||
					kbdataBuilder.AddPair(GetString(resIds[i]), value);
 | 
			
		||||
@@ -249,9 +251,12 @@ namespace keepass2android
 | 
			
		||||
			foreach (var pair in entry.Strings)
 | 
			
		||||
			{
 | 
			
		||||
				String key = pair.Key;
 | 
			
		||||
				
 | 
			
		||||
 | 
			
		||||
				var value = GetStringAndReplacePlaceholders(entry, key);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
				if (!PwDefs.IsStandardField(key)) {
 | 
			
		||||
					kbdataBuilder.AddPair(pair.Key, entry.Strings.ReadSafe(pair.Key));
 | 
			
		||||
					kbdataBuilder.AddPair(pair.Key, value);
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
@@ -263,6 +268,13 @@ namespace keepass2android
 | 
			
		||||
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		static string GetStringAndReplacePlaceholders(PwEntry entry, string key)
 | 
			
		||||
		{
 | 
			
		||||
			String value = entry.Strings.ReadSafe(key);
 | 
			
		||||
			value = SprEngine.Compile(value, new SprContext(entry, App.getDB().pm, SprCompileFlags.All));
 | 
			
		||||
			return value;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		public void OnWaitElementDeleted(int itemId)
 | 
			
		||||
		{
 | 
			
		||||
			mNumElementsToWaitFor--;
 | 
			
		||||
@@ -365,14 +377,14 @@ namespace keepass2android
 | 
			
		||||
				
 | 
			
		||||
				if (action.Equals(Intents.COPY_USERNAME))
 | 
			
		||||
				{
 | 
			
		||||
					String username = mEntry.Strings.ReadSafe(PwDefs.UserNameField);
 | 
			
		||||
					String username = GetStringAndReplacePlaceholders(mEntry, PwDefs.UserNameField);
 | 
			
		||||
					if (username.Length > 0)
 | 
			
		||||
					{
 | 
			
		||||
						mService.timeoutCopyToClipboard(username);
 | 
			
		||||
					}
 | 
			
		||||
				} else if (action.Equals(Intents.COPY_PASSWORD))
 | 
			
		||||
				{
 | 
			
		||||
					String password = mEntry.Strings.ReadSafe(PwDefs.PasswordField);
 | 
			
		||||
					String password = GetStringAndReplacePlaceholders(mEntry, PwDefs.PasswordField);
 | 
			
		||||
					if (password.Length > 0)
 | 
			
		||||
					{
 | 
			
		||||
						mService.timeoutCopyToClipboard(password);
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user