117 lines
4.0 KiB
Java
117 lines
4.0 KiB
Java
package org.julianfamily.keepassgo;
|
|
|
|
import android.content.Context;
|
|
import android.util.JsonReader;
|
|
import android.util.JsonWriter;
|
|
import android.util.Log;
|
|
|
|
import java.io.File;
|
|
import java.io.FileInputStream;
|
|
import java.io.FileOutputStream;
|
|
import java.io.IOException;
|
|
import java.io.InputStreamReader;
|
|
import java.io.OutputStreamWriter;
|
|
import java.nio.charset.StandardCharsets;
|
|
import java.time.Instant;
|
|
import java.util.LinkedHashMap;
|
|
import java.util.Map;
|
|
|
|
final class AutofillBindingStore {
|
|
private static final String TAG = "KeePassGOAutofill";
|
|
|
|
private AutofillBindingStore() {
|
|
}
|
|
|
|
static String entryIDForTarget(Context context, String rawTarget) {
|
|
Bindings bindings = read(context);
|
|
return bindings.apps.getOrDefault(normalize(rawTarget), "");
|
|
}
|
|
|
|
static void rememberBinding(Context context, String rawTarget, String entryID) {
|
|
String target = normalize(rawTarget);
|
|
if (target.isEmpty() || entryID == null || entryID.trim().isEmpty()) {
|
|
return;
|
|
}
|
|
Bindings bindings = read(context);
|
|
bindings.updatedAt = Instant.now().toString();
|
|
bindings.apps.put(target, entryID.trim());
|
|
write(context, bindings);
|
|
}
|
|
|
|
private static Bindings read(Context context) {
|
|
File path = path(context);
|
|
if (!path.isFile()) {
|
|
return new Bindings();
|
|
}
|
|
try (JsonReader reader = new JsonReader(new InputStreamReader(new FileInputStream(path), StandardCharsets.UTF_8))) {
|
|
Bindings bindings = new Bindings();
|
|
reader.beginObject();
|
|
while (reader.hasNext()) {
|
|
String name = reader.nextName();
|
|
if ("updatedAt".equals(name)) {
|
|
bindings.updatedAt = nextString(reader);
|
|
continue;
|
|
}
|
|
if ("apps".equals(name)) {
|
|
reader.beginObject();
|
|
while (reader.hasNext()) {
|
|
bindings.apps.put(normalize(reader.nextName()), nextString(reader));
|
|
}
|
|
reader.endObject();
|
|
continue;
|
|
}
|
|
reader.skipValue();
|
|
}
|
|
reader.endObject();
|
|
return bindings;
|
|
} catch (IOException err) {
|
|
Log.e(TAG, "failed to read autofill bindings", err);
|
|
return new Bindings();
|
|
}
|
|
}
|
|
|
|
private static void write(Context context, Bindings bindings) {
|
|
File path = path(context);
|
|
File parent = path.getParentFile();
|
|
if (parent != null && !parent.exists() && !parent.mkdirs()) {
|
|
Log.e(TAG, "failed to create autofill binding directory " + parent.getAbsolutePath());
|
|
return;
|
|
}
|
|
try (JsonWriter writer = new JsonWriter(new OutputStreamWriter(new FileOutputStream(path, false), StandardCharsets.UTF_8))) {
|
|
writer.setIndent(" ");
|
|
writer.beginObject();
|
|
writer.name("updatedAt").value(bindings.updatedAt);
|
|
writer.name("apps");
|
|
writer.beginObject();
|
|
for (Map.Entry<String, String> entry : bindings.apps.entrySet()) {
|
|
writer.name(entry.getKey()).value(entry.getValue());
|
|
}
|
|
writer.endObject();
|
|
writer.endObject();
|
|
} catch (IOException err) {
|
|
Log.e(TAG, "failed to write autofill bindings", err);
|
|
}
|
|
}
|
|
|
|
private static String nextString(JsonReader reader) throws IOException {
|
|
if (reader.peek() == android.util.JsonToken.NULL) {
|
|
reader.nextNull();
|
|
return "";
|
|
}
|
|
return reader.nextString();
|
|
}
|
|
|
|
private static File path(Context context) {
|
|
return new File(new File(context.getFilesDir(), "keepassgo"), "autofill-bindings.json");
|
|
}
|
|
|
|
private static String normalize(String rawTarget) {
|
|
return rawTarget == null ? "" : rawTarget.trim();
|
|
}
|
|
|
|
private static final class Bindings {
|
|
String updatedAt = "";
|
|
final Map<String, String> apps = new LinkedHashMap<>();
|
|
}
|
|
}
|