Files
KeePassDX/app/src/main/java/com/keepassdroid/utils/SprEngineV4.java
2018-03-22 23:06:05 +01:00

187 lines
5.8 KiB
Java

/*
* Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft.
*
* This file is part of KeePass DX.
*
* KeePass DX 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.
*
* KeePass DX 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 KeePass DX. If not, see <http://www.gnu.org/licenses/>.
*
*/
package com.keepassdroid.utils;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Map.Entry;
import com.keepassdroid.database.PwDatabase;
import com.keepassdroid.database.PwDatabaseV4;
import com.keepassdroid.database.PwEntry;
import com.keepassdroid.database.PwEntryV4;
import com.keepassdroid.database.SearchParametersV4;
public class SprEngineV4 {
private final int MAX_RECURSION_DEPTH = 12;
private final String STR_REF_START = "{REF:";
private final String STR_REF_END = "}";
public class TargetResult {
public PwEntryV4 entry;
public char wanted;
public TargetResult(PwEntryV4 entry, char wanted) {
this.entry = entry;
this.wanted = wanted;
}
}
public String compile(String text, PwEntry entry, PwDatabase database) {
SprContextV4 ctx = new SprContextV4((PwDatabaseV4)database, (PwEntryV4)entry);
return compileInternal(text, ctx, 0);
}
private String compileInternal(String text, SprContextV4 ctx, int recursionLevel) {
if (text == null) { return ""; }
if (ctx == null) { return ""; }
if (recursionLevel >= MAX_RECURSION_DEPTH) { return ""; }
return fillRefPlaceholders(text, ctx, recursionLevel);
}
private String fillRefPlaceholders(String text, SprContextV4 ctx, int recursionLevel) {
if (ctx.db == null) { return text; }
int offset = 0;
for (int i = 0; i < 20; ++i) {
text = fillRefsUsingCache(text, ctx);
int start = StrUtil.indexOfIgnoreCase(text, STR_REF_START, offset, Locale.ENGLISH);
if (start < 0) { break; }
int end = StrUtil.indexOfIgnoreCase(text, STR_REF_END, start + 1, Locale.ENGLISH);
if (end <= start) { break; }
String fullRef = text.substring(start, end - start + 1);
TargetResult result = findRefTarget(fullRef, ctx);
if (result != null) {
PwEntryV4 found = result.entry;
char wanted = result.wanted;
if (found != null) {
String data;
switch (wanted) {
case 'T':
data = found.getTitle();
break;
case 'U':
data = found.getUsername();
break;
case 'A':
data = found.getUrl();
break;
case 'P':
data = found.getPassword();
break;
case 'N':
data = found.getNotes();
break;
case 'I':
data = found.getUUID().toString();
break;
default:
offset = start + 1;
continue;
}
SprContextV4 subCtx = (SprContextV4) ctx.clone();
subCtx.entry = found;
String innerContent = compileInternal(data, subCtx, recursionLevel + 1);
addRefsToCache(fullRef, innerContent, ctx);
text = fillRefsUsingCache(text, ctx);
} else {
offset = start + 1;
continue;
}
}
}
return text;
}
public TargetResult findRefTarget(String fullRef, SprContextV4 ctx) {
if (fullRef == null) { return null; }
fullRef = fullRef.toUpperCase(Locale.ENGLISH);
if (!fullRef.startsWith(STR_REF_START) || !fullRef.endsWith(STR_REF_END)) {
return null;
}
String ref = fullRef.substring(STR_REF_START.length(), fullRef.length() - STR_REF_START.length() - STR_REF_END.length());
if (ref.length() <= 4) { return null; }
if (ref.charAt(1) != '@') { return null; }
if (ref.charAt(3) != ':') { return null; }
char scan = Character.MIN_VALUE;
char wanted = Character.MIN_VALUE;
scan = Character.toUpperCase(ref.charAt(2));
wanted = Character.toUpperCase(ref.charAt(0));
SearchParametersV4 sp = new SearchParametersV4();
sp.setupNone();
sp.searchString = ref.substring(4);
if (scan == 'T') { sp.searchInTitles = true; }
else if (scan == 'U') { sp.searchInUserNames = true; }
else if (scan == 'A') { sp.searchInUrls = true; }
else if (scan == 'P') { sp.searchInPasswords = true; }
else if (scan == 'N') { sp.searchInNotes = true; }
else if (scan == 'I') { sp.searchInUUIDs = true; }
else if (scan == 'O') { sp.searchInOther = true; }
else { return null; }
List<PwEntry> list = new ArrayList<PwEntry>();
ctx.db.rootGroup.searchEntries(sp, list);
if (list.size() > 0) {
return new TargetResult((PwEntryV4)list.get(0), wanted);
}
return null;
}
private void addRefsToCache(String ref, String value, SprContextV4 ctx) {
if (ref == null) { return; }
if (value == null) { return; }
if (ctx == null) { return; }
if (!ctx.refsCache.containsKey(ref)) {
ctx.refsCache.put(ref, value);
}
}
private String fillRefsUsingCache(String text, SprContextV4 ctx) {
for (Entry<String, String> entry : ctx.refsCache.entrySet()) {
text = StrUtil.replaceAllIgnoresCase(text, entry.getKey(), entry.getValue(), Locale.ENGLISH);
}
return text;
}
}