mirror of
https://github.com/moparisthebest/open-keychain
synced 2024-12-26 00:48:51 -05:00
Add temporary file storage as discussed in #665
Writable from OpenKeychain, readable worldwide. Should be used to write shared files to it by first creating the file using TemporaryStorageProvider.createFile and then write to the Uri returned.
This commit is contained in:
parent
50e72b196f
commit
3564773410
@ -49,6 +49,9 @@
|
|||||||
android:name="android.hardware.touchscreen"
|
android:name="android.hardware.touchscreen"
|
||||||
android:required="false" />
|
android:required="false" />
|
||||||
|
|
||||||
|
<permission android:name="org.sufficientlysecure.keychain.WRITE_TEMPORARY_STORAGE"/>
|
||||||
|
<uses-permission android:name="org.sufficientlysecure.keychain.WRITE_TEMPORARY_STORAGE"/>
|
||||||
|
|
||||||
<uses-permission android:name="android.permission.INTERNET" />
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||||
<uses-permission android:name="android.permission.NFC" />
|
<uses-permission android:name="android.permission.NFC" />
|
||||||
@ -484,6 +487,12 @@
|
|||||||
android:resource="@xml/custom_pgp_contacts_structure"/>
|
android:resource="@xml/custom_pgp_contacts_structure"/>
|
||||||
</service>
|
</service>
|
||||||
|
|
||||||
|
<provider
|
||||||
|
android:name=".provider.TemporaryStorageProvider"
|
||||||
|
android:authorities="org.sufficientlysecure.keychain.tempstorage"
|
||||||
|
android:writePermission="org.sufficientlysecure.keychain.WRITE_TEMPORARY_STORAGE"
|
||||||
|
android:exported="true"/>
|
||||||
|
|
||||||
</application>
|
</application>
|
||||||
|
|
||||||
</manifest>
|
</manifest>
|
||||||
|
@ -53,6 +53,8 @@ public final class Constants {
|
|||||||
|
|
||||||
public static boolean KITKAT = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;
|
public static boolean KITKAT = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;
|
||||||
|
|
||||||
|
public static int TEMPFILE_TTL = 24*60*60*1000; // 1 day
|
||||||
|
|
||||||
public static final class Path {
|
public static final class Path {
|
||||||
public static final File APP_DIR = new File(Environment.getExternalStorageDirectory(), "OpenKeychain");
|
public static final File APP_DIR = new File(Environment.getExternalStorageDirectory(), "OpenKeychain");
|
||||||
public static final File APP_DIR_FILE = new File(APP_DIR, "export.asc");
|
public static final File APP_DIR_FILE = new File(APP_DIR, "export.asc");
|
||||||
|
@ -28,6 +28,7 @@ import android.os.Environment;
|
|||||||
import org.spongycastle.jce.provider.BouncyCastleProvider;
|
import org.spongycastle.jce.provider.BouncyCastleProvider;
|
||||||
import org.sufficientlysecure.keychain.helper.Preferences;
|
import org.sufficientlysecure.keychain.helper.Preferences;
|
||||||
import org.sufficientlysecure.keychain.helper.TlsHelper;
|
import org.sufficientlysecure.keychain.helper.TlsHelper;
|
||||||
|
import org.sufficientlysecure.keychain.provider.TemporaryStorageProvider;
|
||||||
import org.sufficientlysecure.keychain.util.Log;
|
import org.sufficientlysecure.keychain.util.Log;
|
||||||
import org.sufficientlysecure.keychain.util.PRNGFixes;
|
import org.sufficientlysecure.keychain.util.PRNGFixes;
|
||||||
|
|
||||||
@ -85,6 +86,8 @@ public class KeychainApplication extends Application {
|
|||||||
Preferences.getPreferences(this).updateKeyServers();
|
Preferences.getPreferences(this).updateKeyServers();
|
||||||
|
|
||||||
TlsHelper.addStaticCA("pool.sks-keyservers.net", getAssets(), "sks-keyservers.netCA.cer");
|
TlsHelper.addStaticCA("pool.sks-keyservers.net", getAssets(), "sks-keyservers.netCA.cer");
|
||||||
|
|
||||||
|
TemporaryStorageProvider.cleanUp(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void setupAccountAsNeeded(Context context) {
|
public static void setupAccountAsNeeded(Context context) {
|
||||||
|
@ -0,0 +1,151 @@
|
|||||||
|
package org.sufficientlysecure.keychain.provider;
|
||||||
|
|
||||||
|
import android.content.ContentProvider;
|
||||||
|
import android.content.ContentValues;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.database.Cursor;
|
||||||
|
import android.database.MatrixCursor;
|
||||||
|
import android.database.sqlite.SQLiteDatabase;
|
||||||
|
import android.database.sqlite.SQLiteOpenHelper;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.os.ParcelFileDescriptor;
|
||||||
|
import android.provider.OpenableColumns;
|
||||||
|
import org.sufficientlysecure.keychain.Constants;
|
||||||
|
import org.sufficientlysecure.keychain.util.DatabaseUtil;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
public class TemporaryStorageProvider extends ContentProvider {
|
||||||
|
|
||||||
|
private static final String DB_NAME = "tempstorage.db";
|
||||||
|
private static final String TABLE_FILES = "files";
|
||||||
|
private static final String COLUMN_ID = "id";
|
||||||
|
private static final String COLUMN_NAME = "name";
|
||||||
|
private static final String COLUMN_TIME = "time";
|
||||||
|
private static final Uri BASE_URI = Uri.parse("content://org.sufficientlysecure.keychain.tempstorage/");
|
||||||
|
private static final int DB_VERSION = 1;
|
||||||
|
|
||||||
|
public static Uri createFile(Context context, String targetName) {
|
||||||
|
ContentValues contentValues = new ContentValues();
|
||||||
|
contentValues.put(COLUMN_NAME, targetName);
|
||||||
|
return context.getContentResolver().insert(BASE_URI, contentValues);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int cleanUp(Context context) {
|
||||||
|
return context.getContentResolver().delete(BASE_URI, COLUMN_TIME + "< ?",
|
||||||
|
new String[]{Long.toString(System.currentTimeMillis() - Constants.TEMPFILE_TTL)});
|
||||||
|
}
|
||||||
|
|
||||||
|
private class TemporaryStorageDatabase extends SQLiteOpenHelper {
|
||||||
|
|
||||||
|
public TemporaryStorageDatabase(Context context) {
|
||||||
|
super(context, DB_NAME, null, DB_VERSION);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate(SQLiteDatabase db) {
|
||||||
|
db.execSQL("CREATE TABLE IF NOT EXISTS " + TABLE_FILES + " (" +
|
||||||
|
COLUMN_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " +
|
||||||
|
COLUMN_NAME + " TEXT, " +
|
||||||
|
COLUMN_TIME + " INTEGER" +
|
||||||
|
");");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private TemporaryStorageDatabase db;
|
||||||
|
|
||||||
|
private File getFile(Uri uri) throws FileNotFoundException {
|
||||||
|
try {
|
||||||
|
return getFile(Integer.parseInt(uri.getLastPathSegment()));
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
throw new FileNotFoundException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private File getFile(int id) {
|
||||||
|
return new File(getContext().getCacheDir(), "temp/" + id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onCreate() {
|
||||||
|
db = new TemporaryStorageDatabase(getContext());
|
||||||
|
return new File(getContext().getCacheDir(), "temp").mkdirs();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
|
||||||
|
File file;
|
||||||
|
try {
|
||||||
|
file = getFile(uri);
|
||||||
|
} catch (FileNotFoundException e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
Cursor fileName = db.getReadableDatabase().query(TABLE_FILES, new String[]{COLUMN_NAME}, COLUMN_ID + "=?",
|
||||||
|
new String[]{uri.getLastPathSegment()}, null, null, null);
|
||||||
|
if (fileName != null) {
|
||||||
|
if (fileName.moveToNext()) {
|
||||||
|
MatrixCursor cursor =
|
||||||
|
new MatrixCursor(new String[]{OpenableColumns.DISPLAY_NAME, OpenableColumns.SIZE, "_data"});
|
||||||
|
cursor.newRow().add(fileName.getString(0)).add(file.length()).add(file.getAbsolutePath());
|
||||||
|
fileName.close();
|
||||||
|
return cursor;
|
||||||
|
}
|
||||||
|
fileName.close();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getType(Uri uri) {
|
||||||
|
return "*/*";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Uri insert(Uri uri, ContentValues values) {
|
||||||
|
if (!values.containsKey(COLUMN_TIME)) {
|
||||||
|
values.put(COLUMN_TIME, System.currentTimeMillis());
|
||||||
|
}
|
||||||
|
int insert = (int) db.getWritableDatabase().insert(TABLE_FILES, null, values);
|
||||||
|
try {
|
||||||
|
getFile(insert).createNewFile();
|
||||||
|
} catch (IOException e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return Uri.withAppendedPath(BASE_URI, Long.toString(insert));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int delete(Uri uri, String selection, String[] selectionArgs) {
|
||||||
|
if (uri.getLastPathSegment() != null) {
|
||||||
|
selection = DatabaseUtil.concatenateWhere(selection, COLUMN_ID + "=?");
|
||||||
|
selectionArgs = DatabaseUtil.appendSelectionArgs(selectionArgs, new String[]{uri.getLastPathSegment()});
|
||||||
|
}
|
||||||
|
Cursor files = db.getReadableDatabase().query(TABLE_FILES, new String[]{COLUMN_ID}, selection,
|
||||||
|
selectionArgs, null, null, null);
|
||||||
|
if (files != null) {
|
||||||
|
while (files.moveToNext()) {
|
||||||
|
getFile(files.getInt(0)).delete();
|
||||||
|
}
|
||||||
|
files.close();
|
||||||
|
return db.getWritableDatabase().delete(TABLE_FILES, selection, selectionArgs);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
|
||||||
|
throw new UnsupportedOperationException("Update not supported");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {
|
||||||
|
return openFileHelper(uri, mode);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,36 @@
|
|||||||
|
package org.sufficientlysecure.keychain.util;
|
||||||
|
|
||||||
|
import android.text.TextUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shamelessly copied from android.database.DatabaseUtils
|
||||||
|
*/
|
||||||
|
public class DatabaseUtil {
|
||||||
|
/**
|
||||||
|
* Concatenates two SQL WHERE clauses, handling empty or null values.
|
||||||
|
*/
|
||||||
|
public static String concatenateWhere(String a, String b) {
|
||||||
|
if (TextUtils.isEmpty(a)) {
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
if (TextUtils.isEmpty(b)) {
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
|
||||||
|
return "(" + a + ") AND (" + b + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Appends one set of selection args to another. This is useful when adding a selection
|
||||||
|
* argument to a user provided set.
|
||||||
|
*/
|
||||||
|
public static String[] appendSelectionArgs(String[] originalValues, String[] newValues) {
|
||||||
|
if (originalValues == null || originalValues.length == 0) {
|
||||||
|
return newValues;
|
||||||
|
}
|
||||||
|
String[] result = new String[originalValues.length + newValues.length ];
|
||||||
|
System.arraycopy(originalValues, 0, result, 0, originalValues.length);
|
||||||
|
System.arraycopy(newValues, 0, result, originalValues.length, newValues.length);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user