mirror of
https://github.com/moparisthebest/keepass2android
synced 2024-11-22 17:22:17 -05:00
Replace (most) placeholders before copying to clipboard/keyboard
This commit is contained in:
parent
7e8e261dbb
commit
98e1727fcc
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);
|
||||
@ -250,8 +252,11 @@ namespace keepass2android
|
||||
{
|
||||
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);
|
||||
|
Loading…
Reference in New Issue
Block a user