mirror of
https://github.com/Kunzisoft/KeePassDX.git
synced 2025-12-04 15:49:33 +01:00
Fix search
This commit is contained in:
@@ -37,7 +37,8 @@ import com.kunzisoft.keepass.icons.assignDatabaseIcon
|
|||||||
import com.kunzisoft.keepass.settings.PreferencesUtil
|
import com.kunzisoft.keepass.settings.PreferencesUtil
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
class SearchEntryCursorAdapter(context: Context, private val database: Database) : CursorAdapter(context, null, FLAG_REGISTER_CONTENT_OBSERVER) {
|
class SearchEntryCursorAdapter(context: Context, private val database: Database)
|
||||||
|
: CursorAdapter(context, null, FLAG_REGISTER_CONTENT_OBSERVER) {
|
||||||
|
|
||||||
private val cursorInflater: LayoutInflater = context.getSystemService(
|
private val cursorInflater: LayoutInflater = context.getSystemService(
|
||||||
Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
|
Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
|
||||||
@@ -109,7 +110,7 @@ class SearchEntryCursorAdapter(context: Context, private val database: Database)
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun runQueryOnBackgroundThread(constraint: CharSequence): Cursor? {
|
override fun runQueryOnBackgroundThread(constraint: CharSequence): Cursor? {
|
||||||
return database.searchEntry(constraint.toString())
|
return database.searchEntries(constraint.toString())
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getEntryFromPosition(position: Int): EntryVersioned? {
|
fun getEntryFromPosition(position: Int): EntryVersioned? {
|
||||||
|
|||||||
@@ -321,7 +321,7 @@ class Database {
|
|||||||
return searchHelper?.search(this, str, max)
|
return searchHelper?.search(this, str, max)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun searchEntry(query: String): Cursor? {
|
fun searchEntries(query: String): Cursor? {
|
||||||
|
|
||||||
var cursorV3: EntryCursorV3? = null
|
var cursorV3: EntryCursorV3? = null
|
||||||
var cursorV4: EntryCursorV4? = null
|
var cursorV4: EntryCursorV4? = null
|
||||||
|
|||||||
@@ -166,7 +166,7 @@ class PwEntryV4 : PwEntry<PwGroupV4, PwEntryV4>, NodeV4Interface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Decode a reference key woth the SprEngineV4
|
* Decode a reference key with the SprEngineV4
|
||||||
* @param decodeRef
|
* @param decodeRef
|
||||||
* @param key
|
* @param key
|
||||||
* @return
|
* @return
|
||||||
@@ -174,7 +174,7 @@ class PwEntryV4 : PwEntry<PwGroupV4, PwEntryV4>, NodeV4Interface {
|
|||||||
private fun decodeRefKey(decodeRef: Boolean, key: String): String {
|
private fun decodeRefKey(decodeRef: Boolean, key: String): String {
|
||||||
val text = fields.getProtectedStringValue(key)
|
val text = fields.getProtectedStringValue(key)
|
||||||
return if (decodeRef) {
|
return if (decodeRef) {
|
||||||
if (mDatabase == null) text else SprEngineV4().compile(text, this, mDatabase)
|
if (mDatabase == null) text else SprEngineV4().compile(text, this, mDatabase!!)
|
||||||
} else text
|
} else text
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,253 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2019 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 3 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.kunzisoft.keepass.database.element;
|
|
||||||
|
|
||||||
import com.kunzisoft.keepass.database.search.EntrySearchHandlerV4;
|
|
||||||
import com.kunzisoft.keepass.database.search.SearchParametersV4;
|
|
||||||
import com.kunzisoft.keepass.utils.StringUtil;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.Comparator;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Locale;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Map.Entry;
|
|
||||||
|
|
||||||
public class SprEngineV4 {
|
|
||||||
private static final int MAX_RECURSION_DEPTH = 12;
|
|
||||||
private static final String STR_REF_START = "{REF:";
|
|
||||||
private static 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class SprContextV4 {
|
|
||||||
|
|
||||||
public PwDatabaseV4 db;
|
|
||||||
public PwEntryV4 entry;
|
|
||||||
public Map<String, String> refsCache = new HashMap<>();
|
|
||||||
|
|
||||||
SprContextV4(PwDatabaseV4 db, PwEntryV4 entry) {
|
|
||||||
this.db = db;
|
|
||||||
this.entry = entry;
|
|
||||||
}
|
|
||||||
|
|
||||||
SprContextV4(SprContextV4 source) {
|
|
||||||
this.db = source.db;
|
|
||||||
this.entry = source.entry;
|
|
||||||
this.refsCache = source.refsCache;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public String compile(String text, PwEntryV4 entry, PwDatabase database) {
|
|
||||||
SprContextV4 ctx = new SprContextV4((PwDatabaseV4)database, entry);
|
|
||||||
|
|
||||||
return compileInternal(text, ctx, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
private String compileInternal(String text, SprContextV4 sprContextV4, int recursionLevel) {
|
|
||||||
if (text == null) { return ""; }
|
|
||||||
if (sprContextV4 == null) { return ""; }
|
|
||||||
if (recursionLevel >= MAX_RECURSION_DEPTH) { return ""; }
|
|
||||||
|
|
||||||
return fillRefPlaceholders(text, sprContextV4, recursionLevel);
|
|
||||||
}
|
|
||||||
|
|
||||||
private String fillRefPlaceholders(String text, SprContextV4 contextV4, int recursionLevel) {
|
|
||||||
|
|
||||||
if (contextV4.db == null) { return text; }
|
|
||||||
|
|
||||||
int offset = 0;
|
|
||||||
for (int i = 0; i < 20; ++i) {
|
|
||||||
text = fillRefsUsingCache(text, contextV4);
|
|
||||||
|
|
||||||
int start = StringUtil.INSTANCE.indexOfIgnoreCase(text, STR_REF_START, offset, Locale.ENGLISH);
|
|
||||||
if (start < 0) { break; }
|
|
||||||
int end = StringUtil.INSTANCE.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, contextV4);
|
|
||||||
|
|
||||||
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.getNodeId().toString();
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
offset = start + 1;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
SprContextV4 subCtx = new SprContextV4(contextV4);
|
|
||||||
subCtx.entry = found;
|
|
||||||
|
|
||||||
String innerContent = compileInternal(data, subCtx, recursionLevel + 1);
|
|
||||||
addRefsToCache(fullRef, innerContent, contextV4);
|
|
||||||
text = fillRefsUsingCache(text, contextV4);
|
|
||||||
} else {
|
|
||||||
offset = start + 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
return text;
|
|
||||||
}
|
|
||||||
|
|
||||||
private TargetResult findRefTarget(String fullRef, SprContextV4 contextV4) {
|
|
||||||
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.toUpperCase(ref.charAt(2));
|
|
||||||
char wanted = Character.toUpperCase(ref.charAt(0));
|
|
||||||
|
|
||||||
SearchParametersV4 searchParametersV4 = new SearchParametersV4();
|
|
||||||
searchParametersV4.setupNone();
|
|
||||||
|
|
||||||
searchParametersV4.setSearchString(ref.substring(4));
|
|
||||||
if (scan == 'T') { searchParametersV4.setSearchInTitles(true); }
|
|
||||||
else if (scan == 'U') { searchParametersV4.setSearchInUserNames(true); }
|
|
||||||
else if (scan == 'A') { searchParametersV4.setSearchInUrls(true); }
|
|
||||||
else if (scan == 'P') { searchParametersV4.setSearchInPasswords(true); }
|
|
||||||
else if (scan == 'N') { searchParametersV4.setSearchInNotes(true); }
|
|
||||||
else if (scan == 'I') { searchParametersV4.setSearchInUUIDs(true); }
|
|
||||||
else if (scan == 'O') { searchParametersV4.setSearchInOther(true); }
|
|
||||||
else { return null; }
|
|
||||||
|
|
||||||
List<PwEntryV4> list = new ArrayList<>();
|
|
||||||
// TODO type parameter
|
|
||||||
searchEntries(contextV4.db.getRootGroup(), searchParametersV4, list);
|
|
||||||
|
|
||||||
if (list.size() > 0) {
|
|
||||||
return new TargetResult(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 sprContextV4) {
|
|
||||||
for (Entry<String, String> entry : sprContextV4.refsCache.entrySet()) {
|
|
||||||
text = StringUtil.INSTANCE.replaceAllIgnoresCase(text, entry.getKey(), entry.getValue(), Locale.ENGLISH);
|
|
||||||
}
|
|
||||||
|
|
||||||
return text;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void searchEntries(PwGroupV4 root, SearchParametersV4 searchParametersV4, List<PwEntryV4> listStorage) {
|
|
||||||
if (searchParametersV4 == null) { return; }
|
|
||||||
if (listStorage == null) { return; }
|
|
||||||
|
|
||||||
List<String> terms = StringUtil.INSTANCE.splitStringTerms(searchParametersV4.getSearchString());
|
|
||||||
if (terms.size() <= 1 || searchParametersV4.getRegularExpression()) {
|
|
||||||
root.doForEachChild(new EntrySearchHandlerV4(searchParametersV4, listStorage), null);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Search longest term first
|
|
||||||
Comparator<String> stringLengthComparator = (lhs, rhs) -> lhs.length() - rhs.length();
|
|
||||||
Collections.sort(terms, stringLengthComparator);
|
|
||||||
|
|
||||||
String fullSearch = searchParametersV4.getSearchString();
|
|
||||||
List<PwEntryV4> childEntries = root.getChildEntries();
|
|
||||||
for (int i = 0; i < terms.size(); i ++) {
|
|
||||||
List<PwEntryV4> pgNew = new ArrayList<>();
|
|
||||||
|
|
||||||
searchParametersV4.setSearchString(terms.get(i));
|
|
||||||
|
|
||||||
boolean negate = false;
|
|
||||||
if (searchParametersV4.getSearchString().startsWith("-")) {
|
|
||||||
searchParametersV4.setSearchString(searchParametersV4.getSearchString().substring(1));
|
|
||||||
negate = searchParametersV4.getSearchString().length() > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!root.doForEachChild(new EntrySearchHandlerV4(searchParametersV4, pgNew), null)) {
|
|
||||||
childEntries = null;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
List<PwEntryV4> complement = new ArrayList<>();
|
|
||||||
if (negate) {
|
|
||||||
for (PwEntryV4 entry: childEntries) {
|
|
||||||
if (!pgNew.contains(entry)) {
|
|
||||||
complement.add(entry);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
childEntries = complement;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
childEntries = pgNew;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (childEntries != null) {
|
|
||||||
listStorage.addAll(childEntries);
|
|
||||||
}
|
|
||||||
searchParametersV4.setSearchString(fullSearch);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,258 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2019 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 3 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.kunzisoft.keepass.database.element
|
||||||
|
|
||||||
|
import com.kunzisoft.keepass.database.search.EntrySearchHandlerV4
|
||||||
|
import com.kunzisoft.keepass.database.search.SearchParametersV4
|
||||||
|
import com.kunzisoft.keepass.utils.StringUtil
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
class SprEngineV4 {
|
||||||
|
|
||||||
|
inner class TargetResult(var entry: PwEntryV4?, var wanted: Char)
|
||||||
|
|
||||||
|
private inner class SprContextV4 {
|
||||||
|
|
||||||
|
var databaseV4: PwDatabaseV4? = null
|
||||||
|
var entry: PwEntryV4
|
||||||
|
var refsCache: MutableMap<String, String> = HashMap()
|
||||||
|
|
||||||
|
internal constructor(db: PwDatabaseV4, entry: PwEntryV4) {
|
||||||
|
this.databaseV4 = db
|
||||||
|
this.entry = entry
|
||||||
|
}
|
||||||
|
|
||||||
|
internal constructor(source: SprContextV4) {
|
||||||
|
this.databaseV4 = source.databaseV4
|
||||||
|
this.entry = source.entry
|
||||||
|
this.refsCache = source.refsCache
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun compile(text: String, entry: PwEntryV4, database: PwDatabaseV4): String {
|
||||||
|
return compileInternal(text, SprContextV4(database, entry), 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun compileInternal(text: String?, sprContextV4: SprContextV4?, recursionLevel: Int): String {
|
||||||
|
if (text == null) {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
if (sprContextV4 == null) {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return if (recursionLevel >= MAX_RECURSION_DEPTH) {
|
||||||
|
""
|
||||||
|
} else fillRefPlaceholders(text, sprContextV4, recursionLevel)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun fillRefPlaceholders(text: String, contextV4: SprContextV4, recursionLevel: Int): String {
|
||||||
|
var text = text
|
||||||
|
|
||||||
|
if (contextV4.databaseV4 == null) {
|
||||||
|
return text
|
||||||
|
}
|
||||||
|
|
||||||
|
var offset = 0
|
||||||
|
for (i in 0..19) {
|
||||||
|
text = fillRefsUsingCache(text, contextV4)
|
||||||
|
|
||||||
|
val start = StringUtil.indexOfIgnoreCase(text!!, STR_REF_START, offset, Locale.ENGLISH)
|
||||||
|
if (start < 0) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
val end = StringUtil.indexOfIgnoreCase(text, STR_REF_END, start + 1, Locale.ENGLISH)
|
||||||
|
if (end <= start) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
val fullRef = text.substring(start, end - start + 1)
|
||||||
|
val result = findRefTarget(fullRef, contextV4)
|
||||||
|
|
||||||
|
if (result != null) {
|
||||||
|
val found = result.entry
|
||||||
|
val wanted = result.wanted
|
||||||
|
|
||||||
|
var data: String? = null
|
||||||
|
when (wanted) {
|
||||||
|
'T' -> data = found?.title
|
||||||
|
'U' -> data = found?.username
|
||||||
|
'A' -> data = found?.url
|
||||||
|
'P' -> data = found?.password
|
||||||
|
'N' -> data = found?.notes
|
||||||
|
'I' -> data = found?.nodeId.toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data != null && found != null) {
|
||||||
|
val subCtx = SprContextV4(contextV4)
|
||||||
|
subCtx.entry = found
|
||||||
|
|
||||||
|
val innerContent = compileInternal(data, subCtx, recursionLevel + 1)
|
||||||
|
addRefsToCache(fullRef, innerContent, contextV4)
|
||||||
|
text = fillRefsUsingCache(text, contextV4)
|
||||||
|
} else {
|
||||||
|
offset = start + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return text
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun findRefTarget(fullRef: String?, contextV4: SprContextV4): TargetResult? {
|
||||||
|
var fullRef: String? = fullRef ?: return null
|
||||||
|
|
||||||
|
fullRef = fullRef!!.toUpperCase(Locale.ENGLISH)
|
||||||
|
if (!fullRef.startsWith(STR_REF_START) || !fullRef.endsWith(STR_REF_END)) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
val 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[1] != '@') {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
if (ref[3] != ':') {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
val scan = Character.toUpperCase(ref[2])
|
||||||
|
val wanted = Character.toUpperCase(ref[0])
|
||||||
|
|
||||||
|
val searchParametersV4 = SearchParametersV4()
|
||||||
|
searchParametersV4.setupNone()
|
||||||
|
|
||||||
|
searchParametersV4.searchString = ref.substring(4)
|
||||||
|
if (scan == 'T') {
|
||||||
|
searchParametersV4.searchInTitles = true
|
||||||
|
} else if (scan == 'U') {
|
||||||
|
searchParametersV4.searchInUserNames = true
|
||||||
|
} else if (scan == 'A') {
|
||||||
|
searchParametersV4.searchInUrls = true
|
||||||
|
} else if (scan == 'P') {
|
||||||
|
searchParametersV4.searchInPasswords = true
|
||||||
|
} else if (scan == 'N') {
|
||||||
|
searchParametersV4.searchInNotes = true
|
||||||
|
} else if (scan == 'I') {
|
||||||
|
searchParametersV4.searchInUUIDs = true
|
||||||
|
} else if (scan == 'O') {
|
||||||
|
searchParametersV4.searchInOther = true
|
||||||
|
} else {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
val list = ArrayList<PwEntryV4>()
|
||||||
|
// TODO type parameter
|
||||||
|
searchEntries(contextV4.databaseV4!!.rootGroup, searchParametersV4, list)
|
||||||
|
|
||||||
|
return if (list.size > 0) {
|
||||||
|
TargetResult(list[0], wanted)
|
||||||
|
} else null
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun addRefsToCache(ref: String?, value: String?, ctx: SprContextV4?) {
|
||||||
|
if (ref == null) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (value == null) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (ctx == null) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ctx.refsCache.containsKey(ref)) {
|
||||||
|
ctx.refsCache[ref] = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun fillRefsUsingCache(text: String, sprContextV4: SprContextV4): String {
|
||||||
|
var newText = text
|
||||||
|
for ((key, value) in sprContextV4.refsCache) {
|
||||||
|
newText = StringUtil.replaceAllIgnoresCase(text, key, value, Locale.ENGLISH)
|
||||||
|
}
|
||||||
|
return newText
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun searchEntries(root: PwGroupV4?, searchParametersV4: SearchParametersV4?, listStorage: MutableList<PwEntryV4>?) {
|
||||||
|
if (searchParametersV4 == null) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (listStorage == null) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val terms = StringUtil.splitStringTerms(searchParametersV4.searchString)
|
||||||
|
if (terms.size <= 1 || searchParametersV4.regularExpression) {
|
||||||
|
root!!.doForEachChild(EntrySearchHandlerV4(searchParametersV4, listStorage), null)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Search longest term first
|
||||||
|
val stringLengthComparator = Comparator<String> { lhs, rhs -> lhs.length - rhs.length }
|
||||||
|
Collections.sort(terms, stringLengthComparator)
|
||||||
|
|
||||||
|
val fullSearch = searchParametersV4.searchString
|
||||||
|
var childEntries: List<PwEntryV4>? = root!!.getChildEntries()
|
||||||
|
for (i in terms.indices) {
|
||||||
|
val pgNew = ArrayList<PwEntryV4>()
|
||||||
|
|
||||||
|
searchParametersV4.searchString = terms[i]
|
||||||
|
|
||||||
|
var negate = false
|
||||||
|
if (searchParametersV4.searchString.startsWith("-")) {
|
||||||
|
searchParametersV4.searchString = searchParametersV4.searchString.substring(1)
|
||||||
|
negate = searchParametersV4.searchString.length > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!root.doForEachChild(EntrySearchHandlerV4(searchParametersV4, pgNew), null)) {
|
||||||
|
childEntries = null
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
val complement = ArrayList<PwEntryV4>()
|
||||||
|
if (negate) {
|
||||||
|
for (entry in childEntries!!) {
|
||||||
|
if (!pgNew.contains(entry)) {
|
||||||
|
complement.add(entry)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
childEntries = complement
|
||||||
|
} else {
|
||||||
|
childEntries = pgNew
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (childEntries != null) {
|
||||||
|
listStorage.addAll(childEntries)
|
||||||
|
}
|
||||||
|
searchParametersV4.searchString = fullSearch
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val MAX_RECURSION_DEPTH = 12
|
||||||
|
private const val STR_REF_START = "{REF:"
|
||||||
|
private const val STR_REF_END = "}"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -72,9 +72,13 @@ class SearchDbHelper(private val isOmitBackup: Boolean) {
|
|||||||
return searchGroup
|
return searchGroup
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun entryContainsString(entry: EntryVersioned, qStr: String, loc: Locale): Boolean {
|
private fun entryContainsString(entry: EntryVersioned, searchString: String, locale: Locale): Boolean {
|
||||||
// Search all strings in the entry
|
|
||||||
|
|
||||||
|
// Entry don't contains string if the search string is empty
|
||||||
|
if (searchString.isEmpty())
|
||||||
|
return false
|
||||||
|
|
||||||
|
// Search all strings in the entry
|
||||||
var iterator: EntrySearchStringIterator? = null
|
var iterator: EntrySearchStringIterator? = null
|
||||||
entry.pwEntryV3?.let {
|
entry.pwEntryV3?.let {
|
||||||
iterator = EntrySearchStringIteratorV3(it)
|
iterator = EntrySearchStringIteratorV3(it)
|
||||||
@@ -87,8 +91,7 @@ class SearchDbHelper(private val isOmitBackup: Boolean) {
|
|||||||
while (it.hasNext()) {
|
while (it.hasNext()) {
|
||||||
val str = it.next()
|
val str = it.next()
|
||||||
if (str.isNotEmpty()) {
|
if (str.isNotEmpty()) {
|
||||||
val lower = str.toLowerCase(loc)
|
if (str.toLowerCase(locale).contains(searchString)) {
|
||||||
if (lower.contains(qStr)) {
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ class EntrySearchStringIteratorV4 : EntrySearchStringIterator {
|
|||||||
|
|
||||||
private var mCurrent: String? = null
|
private var mCurrent: String? = null
|
||||||
private var mSetIterator: Iterator<Entry<String, ProtectedString>>? = null
|
private var mSetIterator: Iterator<Entry<String, ProtectedString>>? = null
|
||||||
private var mSearchParametersV4: SearchParametersV4? = null
|
private var mSearchParametersV4: SearchParametersV4
|
||||||
|
|
||||||
constructor(entry: PwEntryV4) {
|
constructor(entry: PwEntryV4) {
|
||||||
this.mSearchParametersV4 = SearchParametersV4()
|
this.mSearchParametersV4 = SearchParametersV4()
|
||||||
@@ -52,9 +52,9 @@ class EntrySearchStringIteratorV4 : EntrySearchStringIterator {
|
|||||||
throw NoSuchElementException("Past the end of the list.")
|
throw NoSuchElementException("Past the end of the list.")
|
||||||
}
|
}
|
||||||
|
|
||||||
val next = mCurrent
|
val next:String = mCurrent!!
|
||||||
advance()
|
advance()
|
||||||
return next!!
|
return next
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun advance() {
|
private fun advance() {
|
||||||
@@ -75,12 +75,12 @@ class EntrySearchStringIteratorV4 : EntrySearchStringIterator {
|
|||||||
|
|
||||||
private fun searchInField(key: String): Boolean {
|
private fun searchInField(key: String): Boolean {
|
||||||
return when (key) {
|
return when (key) {
|
||||||
PwEntryV4.STR_TITLE -> mSearchParametersV4!!.searchInTitles
|
PwEntryV4.STR_TITLE -> mSearchParametersV4.searchInTitles
|
||||||
PwEntryV4.STR_USERNAME -> mSearchParametersV4!!.searchInUserNames
|
PwEntryV4.STR_USERNAME -> mSearchParametersV4.searchInUserNames
|
||||||
PwEntryV4.STR_PASSWORD -> mSearchParametersV4!!.searchInPasswords
|
PwEntryV4.STR_PASSWORD -> mSearchParametersV4.searchInPasswords
|
||||||
PwEntryV4.STR_URL -> mSearchParametersV4!!.searchInUrls
|
PwEntryV4.STR_URL -> mSearchParametersV4.searchInUrls
|
||||||
PwEntryV4.STR_NOTES -> mSearchParametersV4!!.searchInNotes
|
PwEntryV4.STR_NOTES -> mSearchParametersV4.searchInNotes
|
||||||
else -> mSearchParametersV4!!.searchInOther
|
else -> mSearchParametersV4.searchInOther
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -69,14 +69,10 @@ object StringUtil {
|
|||||||
return indexOfIgnoreCase(text, search, 0, locale)
|
return indexOfIgnoreCase(text, search, 0, locale)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun replaceAllIgnoresCase(text: String?, find: String?, newText: String?, locale: Locale): String? {
|
fun replaceAllIgnoresCase(text: String, find: String, newText: String, locale: Locale): String {
|
||||||
var currentText = text
|
var currentText = text
|
||||||
if (currentText == null || find == null || newText == null) {
|
|
||||||
return currentText
|
|
||||||
}
|
|
||||||
|
|
||||||
var pos = 0
|
var pos = 0
|
||||||
while (pos < currentText!!.length) {
|
while (pos < currentText.length) {
|
||||||
pos = indexOfIgnoreCase(currentText, find, pos, locale)
|
pos = indexOfIgnoreCase(currentText, find, pos, locale)
|
||||||
if (pos < 0) {
|
if (pos < 0) {
|
||||||
break
|
break
|
||||||
|
|||||||
Reference in New Issue
Block a user