227 lines
7.9 KiB
Java
227 lines
7.9 KiB
Java
package org.julianfamily.keepassgo;
|
|
|
|
import android.app.Activity;
|
|
import android.content.ClipData;
|
|
import android.content.Intent;
|
|
import android.database.Cursor;
|
|
import android.net.Uri;
|
|
import android.os.Bundle;
|
|
import android.provider.OpenableColumns;
|
|
import android.util.Log;
|
|
|
|
import java.io.File;
|
|
import java.io.FileOutputStream;
|
|
import java.io.FileInputStream;
|
|
import java.io.IOException;
|
|
import java.io.InputStream;
|
|
import java.nio.charset.StandardCharsets;
|
|
import java.util.ArrayList;
|
|
|
|
public final class SharedVaultImportActivity extends Activity {
|
|
private static final String TAG = "KeePassGOImport";
|
|
private static final String DEFAULT_NAME = "shared-vault.kdbx";
|
|
private static final String PENDING_SHARED_VAULT = "pending-shared-vault.kdbx";
|
|
private static final String PENDING_SHARED_VAULT_NAME = "pending-shared-vault-name.txt";
|
|
private static final String PENDING_SHARED_LOOKUP = "pending-shared-lookup.txt";
|
|
|
|
@Override
|
|
protected void onCreate(Bundle state) {
|
|
super.onCreate(state);
|
|
handleIntent(getIntent());
|
|
launchMainActivity();
|
|
finish();
|
|
}
|
|
|
|
@Override
|
|
protected void onNewIntent(Intent intent) {
|
|
super.onNewIntent(intent);
|
|
setIntent(intent);
|
|
handleIntent(intent);
|
|
launchMainActivity();
|
|
finish();
|
|
}
|
|
|
|
private void handleIntent(Intent intent) {
|
|
logIntent(intent);
|
|
String sharedLookup = resolveSharedLookup(intent);
|
|
if (!sharedLookup.isEmpty()) {
|
|
try {
|
|
persistPendingLookup(sharedLookup);
|
|
Log.i(TAG, "queued shared lookup target");
|
|
} catch (IOException | RuntimeException err) {
|
|
Log.e(TAG, "failed to queue shared lookup target", err);
|
|
}
|
|
return;
|
|
}
|
|
Uri uri = resolveSharedUri(intent);
|
|
if (uri == null) {
|
|
Log.i(TAG, "no shared vault URI on intent");
|
|
return;
|
|
}
|
|
try {
|
|
persistPendingImport(uri);
|
|
Log.i(TAG, "queued shared vault import from " + uri);
|
|
} catch (IOException | RuntimeException err) {
|
|
Log.e(TAG, "failed to queue shared vault import", err);
|
|
}
|
|
}
|
|
|
|
private Uri resolveSharedUri(Intent intent) {
|
|
if (intent == null) {
|
|
return null;
|
|
}
|
|
String action = intent.getAction();
|
|
if (Intent.ACTION_SEND.equals(action)) {
|
|
Uri extraStream = intent.getParcelableExtra(Intent.EXTRA_STREAM);
|
|
if (extraStream != null) {
|
|
return extraStream;
|
|
}
|
|
}
|
|
if (Intent.ACTION_SEND_MULTIPLE.equals(action)) {
|
|
ArrayList<Uri> streams = intent.getParcelableArrayListExtra(Intent.EXTRA_STREAM);
|
|
if (streams != null && !streams.isEmpty()) {
|
|
return streams.get(0);
|
|
}
|
|
}
|
|
if (Intent.ACTION_VIEW.equals(action)) {
|
|
Uri data = intent.getData();
|
|
if (data != null) {
|
|
return data;
|
|
}
|
|
}
|
|
ClipData clipData = intent.getClipData();
|
|
if (clipData != null && clipData.getItemCount() > 0) {
|
|
Uri clipUri = clipData.getItemAt(0).getUri();
|
|
if (clipUri != null) {
|
|
return clipUri;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
private String resolveSharedLookup(Intent intent) {
|
|
if (intent == null) {
|
|
return "";
|
|
}
|
|
String action = intent.getAction();
|
|
if (Intent.ACTION_SEND.equals(action) && "text/plain".equalsIgnoreCase(intent.getType())) {
|
|
CharSequence extraText = intent.getCharSequenceExtra(Intent.EXTRA_TEXT);
|
|
if (extraText != null) {
|
|
return extraText.toString().trim();
|
|
}
|
|
}
|
|
if (Intent.ACTION_VIEW.equals(action)) {
|
|
Uri data = intent.getData();
|
|
if (data != null) {
|
|
String scheme = data.getScheme();
|
|
if ("http".equalsIgnoreCase(scheme) || "https".equalsIgnoreCase(scheme)) {
|
|
return data.toString();
|
|
}
|
|
}
|
|
}
|
|
return "";
|
|
}
|
|
|
|
private void persistPendingImport(Uri uri) throws IOException {
|
|
File dir = new File(getFilesDir(), "keepassgo");
|
|
if (!dir.exists() && !dir.mkdirs()) {
|
|
throw new IOException("failed to create " + dir.getAbsolutePath());
|
|
}
|
|
File pendingFile = new File(dir, PENDING_SHARED_VAULT);
|
|
try (InputStream in = openSharedInputStream(uri)) {
|
|
if (in == null) {
|
|
throw new IOException("failed to open shared vault stream");
|
|
}
|
|
try (FileOutputStream out = new FileOutputStream(pendingFile, false)) {
|
|
byte[] buffer = new byte[8192];
|
|
int count;
|
|
while ((count = in.read(buffer)) >= 0) {
|
|
out.write(buffer, 0, count);
|
|
}
|
|
}
|
|
}
|
|
|
|
File nameFile = new File(dir, PENDING_SHARED_VAULT_NAME);
|
|
try (FileOutputStream out = new FileOutputStream(nameFile, false)) {
|
|
out.write(resolveDisplayName(uri).getBytes(StandardCharsets.UTF_8));
|
|
}
|
|
}
|
|
|
|
private void persistPendingLookup(String lookup) throws IOException {
|
|
File dir = new File(getFilesDir(), "keepassgo");
|
|
if (!dir.exists() && !dir.mkdirs()) {
|
|
throw new IOException("failed to create " + dir.getAbsolutePath());
|
|
}
|
|
File pendingFile = new File(dir, PENDING_SHARED_LOOKUP);
|
|
try (FileOutputStream out = new FileOutputStream(pendingFile, false)) {
|
|
out.write(lookup.getBytes(StandardCharsets.UTF_8));
|
|
}
|
|
}
|
|
|
|
private InputStream openSharedInputStream(Uri uri) throws IOException {
|
|
if ("file".equalsIgnoreCase(uri.getScheme())) {
|
|
String path = uri.getPath();
|
|
if (path == null || path.trim().isEmpty()) {
|
|
throw new IOException("file URI is missing a path");
|
|
}
|
|
return new FileInputStream(new File(path));
|
|
}
|
|
return getContentResolver().openInputStream(uri);
|
|
}
|
|
|
|
private String resolveDisplayName(Uri uri) {
|
|
String displayName = queryDisplayName(uri);
|
|
if (!displayName.isEmpty()) {
|
|
return displayName;
|
|
}
|
|
String lastSegment = uri.getLastPathSegment();
|
|
if (lastSegment != null && !lastSegment.trim().isEmpty()) {
|
|
return lastSegment.trim();
|
|
}
|
|
return DEFAULT_NAME;
|
|
}
|
|
|
|
private String queryDisplayName(Uri uri) {
|
|
Cursor cursor = null;
|
|
try {
|
|
cursor = getContentResolver().query(uri, new String[]{OpenableColumns.DISPLAY_NAME}, null, null, null);
|
|
if (cursor != null && cursor.moveToFirst()) {
|
|
int index = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME);
|
|
if (index >= 0) {
|
|
String value = cursor.getString(index);
|
|
if (value != null) {
|
|
return value.trim();
|
|
}
|
|
}
|
|
}
|
|
} catch (RuntimeException err) {
|
|
Log.w(TAG, "failed to query display name", err);
|
|
} finally {
|
|
if (cursor != null) {
|
|
cursor.close();
|
|
}
|
|
}
|
|
return "";
|
|
}
|
|
|
|
private void logIntent(Intent intent) {
|
|
if (intent == null) {
|
|
return;
|
|
}
|
|
Log.i(TAG, "intent action=" + intent.getAction()
|
|
+ " type=" + intent.getType()
|
|
+ " data=" + intent.getData()
|
|
+ " flags=0x" + Integer.toHexString(intent.getFlags()));
|
|
ClipData clipData = intent.getClipData();
|
|
if (clipData != null) {
|
|
Log.i(TAG, "intent clip items=" + clipData.getItemCount());
|
|
}
|
|
}
|
|
|
|
private void launchMainActivity() {
|
|
Intent launch = new Intent();
|
|
launch.setClassName(this, "org.gioui.GioActivity");
|
|
startActivity(launch);
|
|
}
|
|
}
|