382 lines
16 KiB
Java
382 lines
16 KiB
Java
/*
|
|
* Copyright (C) 2010 Thialfihar <thi@thialfihar.org>
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
package org.thialfihar.android.apg.provider;
|
|
|
|
import java.io.File;
|
|
import java.io.FileNotFoundException;
|
|
import java.util.HashMap;
|
|
|
|
import org.thialfihar.android.apg.Id;
|
|
|
|
import android.content.ContentProvider;
|
|
import android.content.ContentValues;
|
|
import android.content.UriMatcher;
|
|
import android.database.Cursor;
|
|
import android.database.DatabaseUtils;
|
|
import android.database.sqlite.SQLiteQueryBuilder;
|
|
import android.net.Uri;
|
|
import android.os.ParcelFileDescriptor;
|
|
import android.text.TextUtils;
|
|
|
|
public class DataProvider extends ContentProvider {
|
|
public static final String AUTHORITY = "org.thialfihar.android.apg.provider";
|
|
|
|
private static final int PUBLIC_KEY_RING = 101;
|
|
private static final int PUBLIC_KEY_RING_ID = 102;
|
|
private static final int PUBLIC_KEY_RING_BY_KEY_ID = 103;
|
|
private static final int PUBLIC_KEY_RING_BY_EMAILS = 104;
|
|
private static final int PUBLIC_KEY_RING_KEY = 111;
|
|
private static final int PUBLIC_KEY_RING_KEY_RANK = 112;
|
|
private static final int PUBLIC_KEY_RING_USER_ID = 121;
|
|
private static final int PUBLIC_KEY_RING_USER_ID_RANK = 122;
|
|
|
|
private static final int SECRET_KEY_RING = 201;
|
|
private static final int SECRET_KEY_RING_ID = 202;
|
|
private static final int SECRET_KEY_RING_BY_KEY_ID = 203;
|
|
private static final int SECRET_KEY_RING_BY_EMAILS = 204;
|
|
private static final int SECRET_KEY_RING_KEY = 211;
|
|
private static final int SECRET_KEY_RING_KEY_RANK = 212;
|
|
private static final int SECRET_KEY_RING_USER_ID = 221;
|
|
private static final int SECRET_KEY_RING_USER_ID_RANK = 222;
|
|
|
|
private static final int DATA_STREAM = 301;
|
|
|
|
private static final String PUBLIC_KEY_RING_CONTENT_DIR_TYPE =
|
|
"vnd.android.cursor.dir/vnd.thialfihar.apg.public.key_ring";
|
|
private static final String PUBLIC_KEY_RING_CONTENT_ITEM_TYPE =
|
|
"vnd.android.cursor.item/vnd.thialfihar.apg.public.key_ring";
|
|
|
|
private static final String PUBLIC_KEY_CONTENT_DIR_TYPE =
|
|
"vnd.android.cursor.dir/vnd.thialfihar.apg.public.key";
|
|
private static final String PUBLIC_KEY_CONTENT_ITEM_TYPE =
|
|
"vnd.android.cursor.item/vnd.thialfihar.apg.public.key";
|
|
|
|
private static final String SECRET_KEY_RING_CONTENT_DIR_TYPE =
|
|
"vnd.android.cursor.dir/vnd.thialfihar.apg.secret.key_ring";
|
|
private static final String SECRET_KEY_RING_CONTENT_ITEM_TYPE =
|
|
"vnd.android.cursor.item/vnd.thialfihar.apg.secret.key_ring";
|
|
|
|
private static final String SECRET_KEY_CONTENT_DIR_TYPE =
|
|
"vnd.android.cursor.dir/vnd.thialfihar.apg.secret.key";
|
|
private static final String SECRET_KEY_CONTENT_ITEM_TYPE =
|
|
"vnd.android.cursor.item/vnd.thialfihar.apg.secret.key";
|
|
|
|
private static final String USER_ID_CONTENT_DIR_TYPE =
|
|
"vnd.android.cursor.dir/vnd.thialfihar.apg.user_id";
|
|
private static final String USER_ID_CONTENT_ITEM_TYPE =
|
|
"vnd.android.cursor.item/vnd.thialfihar.apg.user_id";
|
|
|
|
public static final String _ID = "_id";
|
|
public static final String MASTER_KEY_ID = "master_key_id";
|
|
public static final String KEY_ID = "key_id";
|
|
public static final String USER_ID = "user_id";
|
|
|
|
private static final UriMatcher mUriMatcher;
|
|
|
|
private Database mDb;
|
|
|
|
static {
|
|
mUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
|
|
mUriMatcher.addURI(AUTHORITY, "key_rings/public/key_id/*", PUBLIC_KEY_RING_BY_KEY_ID);
|
|
mUriMatcher.addURI(AUTHORITY, "key_rings/public/emails/*", PUBLIC_KEY_RING_BY_EMAILS);
|
|
|
|
mUriMatcher.addURI(AUTHORITY, "key_rings/public/*/keys", PUBLIC_KEY_RING_KEY);
|
|
mUriMatcher.addURI(AUTHORITY, "key_rings/public/*/keys/#", PUBLIC_KEY_RING_KEY_RANK);
|
|
|
|
mUriMatcher.addURI(AUTHORITY, "key_rings/public/*/user_ids", PUBLIC_KEY_RING_USER_ID);
|
|
mUriMatcher.addURI(AUTHORITY, "key_rings/public/*/user_ids/#", PUBLIC_KEY_RING_USER_ID_RANK);
|
|
|
|
mUriMatcher.addURI(AUTHORITY, "key_rings/public", PUBLIC_KEY_RING);
|
|
mUriMatcher.addURI(AUTHORITY, "key_rings/public/*", PUBLIC_KEY_RING_ID);
|
|
|
|
mUriMatcher.addURI(AUTHORITY, "key_rings/secret/key_id/*", SECRET_KEY_RING_BY_KEY_ID);
|
|
mUriMatcher.addURI(AUTHORITY, "key_rings/secret/emails/*", SECRET_KEY_RING_BY_EMAILS);
|
|
|
|
mUriMatcher.addURI(AUTHORITY, "key_rings/secret/*/keys", SECRET_KEY_RING_KEY);
|
|
mUriMatcher.addURI(AUTHORITY, "key_rings/secret/*/keys/#", SECRET_KEY_RING_KEY_RANK);
|
|
|
|
mUriMatcher.addURI(AUTHORITY, "key_rings/secret/*/user_ids", SECRET_KEY_RING_USER_ID);
|
|
mUriMatcher.addURI(AUTHORITY, "key_rings/secret/*/user_ids/#", SECRET_KEY_RING_USER_ID_RANK);
|
|
|
|
mUriMatcher.addURI(AUTHORITY, "key_rings/secret", SECRET_KEY_RING);
|
|
mUriMatcher.addURI(AUTHORITY, "key_rings/secret/*", SECRET_KEY_RING_ID);
|
|
|
|
mUriMatcher.addURI(AUTHORITY, "data/*", DATA_STREAM);
|
|
}
|
|
|
|
@Override
|
|
public boolean onCreate() {
|
|
mDb = new Database(getContext());
|
|
return true;
|
|
}
|
|
|
|
@Override
|
|
public Cursor query(Uri uri, String[] projection, String selection,
|
|
String[] selectionArgs, String sortOrder) {
|
|
// TODO: implement the others, then use them for the lists
|
|
SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
|
|
HashMap<String, String> projectionMap = new HashMap<String, String>();
|
|
|
|
int match = mUriMatcher.match(uri);
|
|
int type;
|
|
switch (match) {
|
|
case PUBLIC_KEY_RING:
|
|
case PUBLIC_KEY_RING_ID:
|
|
case PUBLIC_KEY_RING_BY_KEY_ID:
|
|
case PUBLIC_KEY_RING_BY_EMAILS:
|
|
case PUBLIC_KEY_RING_KEY:
|
|
case PUBLIC_KEY_RING_KEY_RANK:
|
|
case PUBLIC_KEY_RING_USER_ID:
|
|
case PUBLIC_KEY_RING_USER_ID_RANK:
|
|
type = Id.database.type_public;
|
|
break;
|
|
|
|
case SECRET_KEY_RING:
|
|
case SECRET_KEY_RING_ID:
|
|
case SECRET_KEY_RING_BY_KEY_ID:
|
|
case SECRET_KEY_RING_BY_EMAILS:
|
|
case SECRET_KEY_RING_KEY:
|
|
case SECRET_KEY_RING_KEY_RANK:
|
|
case SECRET_KEY_RING_USER_ID:
|
|
case SECRET_KEY_RING_USER_ID_RANK:
|
|
type = Id.database.type_secret;
|
|
break;
|
|
|
|
default: {
|
|
throw new IllegalArgumentException("Unknown URI " + uri);
|
|
}
|
|
}
|
|
|
|
qb.appendWhere(KeyRings.TABLE_NAME + "." + KeyRings.TYPE + " = " + type);
|
|
|
|
switch (match) {
|
|
case PUBLIC_KEY_RING_ID:
|
|
case SECRET_KEY_RING_ID: {
|
|
qb.appendWhere(" AND " +
|
|
KeyRings.TABLE_NAME + "." + KeyRings.MASTER_KEY_ID + " = ");
|
|
qb.appendWhereEscapeString(uri.getPathSegments().get(2));
|
|
|
|
// break omitted intentionally
|
|
}
|
|
|
|
case PUBLIC_KEY_RING:
|
|
case SECRET_KEY_RING: {
|
|
qb.setTables(KeyRings.TABLE_NAME + " INNER JOIN " + Keys.TABLE_NAME + " ON " +
|
|
"(" + KeyRings.TABLE_NAME + "." + KeyRings._ID + " = " +
|
|
Keys.TABLE_NAME + "." + Keys.KEY_RING_ID + " AND " +
|
|
Keys.TABLE_NAME + "." + Keys.IS_MASTER_KEY + " = '1'" +
|
|
") " +
|
|
" INNER JOIN " + UserIds.TABLE_NAME + " ON " +
|
|
"(" + Keys.TABLE_NAME + "." + Keys._ID + " = " +
|
|
UserIds.TABLE_NAME + "." + UserIds.KEY_ID + " AND " +
|
|
UserIds.TABLE_NAME + "." + UserIds.RANK + " = '0') ");
|
|
|
|
projectionMap.put(_ID,
|
|
KeyRings.TABLE_NAME + "." + KeyRings._ID);
|
|
projectionMap.put(MASTER_KEY_ID,
|
|
KeyRings.TABLE_NAME + "." + KeyRings.MASTER_KEY_ID);
|
|
projectionMap.put(USER_ID,
|
|
UserIds.TABLE_NAME + "." + UserIds.USER_ID);
|
|
|
|
if (TextUtils.isEmpty(sortOrder)) {
|
|
sortOrder = UserIds.TABLE_NAME + "." + UserIds.USER_ID + " ASC";
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case SECRET_KEY_RING_BY_KEY_ID:
|
|
case PUBLIC_KEY_RING_BY_KEY_ID: {
|
|
qb.setTables(Keys.TABLE_NAME + " AS tmp INNER JOIN " +
|
|
KeyRings.TABLE_NAME + " ON (" +
|
|
KeyRings.TABLE_NAME + "." + KeyRings._ID + " = " +
|
|
"tmp." + Keys.KEY_RING_ID + ")" +
|
|
" INNER JOIN " + Keys.TABLE_NAME + " ON " +
|
|
"(" + KeyRings.TABLE_NAME + "." + KeyRings._ID + " = " +
|
|
Keys.TABLE_NAME + "." + Keys.KEY_RING_ID + " AND " +
|
|
Keys.TABLE_NAME + "." + Keys.IS_MASTER_KEY + " = '1'" +
|
|
") " +
|
|
" INNER JOIN " + UserIds.TABLE_NAME + " ON " +
|
|
"(" + Keys.TABLE_NAME + "." + Keys._ID + " = " +
|
|
UserIds.TABLE_NAME + "." + UserIds.KEY_ID + " AND " +
|
|
UserIds.TABLE_NAME + "." + UserIds.RANK + " = '0') ");
|
|
|
|
projectionMap.put(_ID,
|
|
KeyRings.TABLE_NAME + "." + KeyRings._ID);
|
|
projectionMap.put(MASTER_KEY_ID,
|
|
KeyRings.TABLE_NAME + "." + KeyRings.MASTER_KEY_ID);
|
|
projectionMap.put(USER_ID,
|
|
UserIds.TABLE_NAME + "." + UserIds.USER_ID);
|
|
|
|
qb.appendWhere(" AND tmp." + Keys.KEY_ID + " = ");
|
|
qb.appendWhereEscapeString(uri.getPathSegments().get(3));
|
|
|
|
break;
|
|
}
|
|
|
|
case SECRET_KEY_RING_BY_EMAILS:
|
|
case PUBLIC_KEY_RING_BY_EMAILS: {
|
|
qb.setTables(KeyRings.TABLE_NAME + " INNER JOIN " + Keys.TABLE_NAME + " ON " +
|
|
"(" + KeyRings.TABLE_NAME + "." + KeyRings._ID + " = " +
|
|
Keys.TABLE_NAME + "." + Keys.KEY_RING_ID + " AND " +
|
|
Keys.TABLE_NAME + "." + Keys.IS_MASTER_KEY + " = '1'" +
|
|
") " +
|
|
" INNER JOIN " + UserIds.TABLE_NAME + " ON " +
|
|
"(" + Keys.TABLE_NAME + "." + Keys._ID + " = " +
|
|
UserIds.TABLE_NAME + "." + UserIds.KEY_ID + " AND " +
|
|
UserIds.TABLE_NAME + "." + UserIds.RANK + " = '0') ");
|
|
|
|
projectionMap.put(_ID,
|
|
KeyRings.TABLE_NAME + "." + KeyRings._ID);
|
|
projectionMap.put(MASTER_KEY_ID,
|
|
KeyRings.TABLE_NAME + "." + KeyRings.MASTER_KEY_ID);
|
|
projectionMap.put(USER_ID,
|
|
UserIds.TABLE_NAME + "." + UserIds.USER_ID);
|
|
|
|
String emails = uri.getPathSegments().get(3);
|
|
String chunks[] = emails.split(" *, *");
|
|
boolean gotCondition = false;
|
|
String emailWhere = "";
|
|
for (int i = 0; i < chunks.length; ++i) {
|
|
if (chunks[i].length() == 0) {
|
|
continue;
|
|
}
|
|
if (i != 0) {
|
|
emailWhere += " OR ";
|
|
}
|
|
emailWhere += "tmp." + UserIds.USER_ID + " LIKE ";
|
|
// match '*<email>', so it has to be at the *end* of the user id
|
|
emailWhere += DatabaseUtils.sqlEscapeString("%<" + chunks[i] + ">");
|
|
gotCondition = true;
|
|
}
|
|
|
|
if (gotCondition) {
|
|
qb.appendWhere(" AND EXISTS (SELECT tmp." + UserIds._ID +
|
|
" FROM " + UserIds.TABLE_NAME +
|
|
" AS tmp WHERE tmp." + UserIds.KEY_ID + " = " +
|
|
Keys.TABLE_NAME + "." + Keys._ID +
|
|
" AND (" + emailWhere + "))");
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
default: {
|
|
throw new IllegalArgumentException("Unknown URI " + uri);
|
|
}
|
|
}
|
|
|
|
qb.setProjectionMap(projectionMap);
|
|
|
|
// If no sort order is specified use the default
|
|
String orderBy;
|
|
if (TextUtils.isEmpty(sortOrder)) {
|
|
orderBy = null;
|
|
} else {
|
|
orderBy = sortOrder;
|
|
}
|
|
|
|
//System.out.println(qb.buildQuery(projection, selection, selectionArgs, null, null, sortOrder, null).replace("WHERE", "WHERE\n"));
|
|
Cursor c = qb.query(mDb.db(), projection, selection, selectionArgs, null, null, orderBy);
|
|
|
|
// Tell the cursor what uri to watch, so it knows when its source data changes
|
|
c.setNotificationUri(getContext().getContentResolver(), uri);
|
|
return c;
|
|
}
|
|
|
|
@Override
|
|
public String getType(Uri uri) {
|
|
switch (mUriMatcher.match(uri)) {
|
|
case PUBLIC_KEY_RING:
|
|
case PUBLIC_KEY_RING_BY_EMAILS:
|
|
return PUBLIC_KEY_RING_CONTENT_DIR_TYPE;
|
|
|
|
case PUBLIC_KEY_RING_ID:
|
|
return PUBLIC_KEY_RING_CONTENT_ITEM_TYPE;
|
|
|
|
case PUBLIC_KEY_RING_BY_KEY_ID:
|
|
return PUBLIC_KEY_RING_CONTENT_ITEM_TYPE;
|
|
|
|
case PUBLIC_KEY_RING_KEY:
|
|
return PUBLIC_KEY_CONTENT_DIR_TYPE;
|
|
|
|
case PUBLIC_KEY_RING_KEY_RANK:
|
|
return PUBLIC_KEY_CONTENT_ITEM_TYPE;
|
|
|
|
case PUBLIC_KEY_RING_USER_ID:
|
|
return USER_ID_CONTENT_DIR_TYPE;
|
|
|
|
case PUBLIC_KEY_RING_USER_ID_RANK:
|
|
return USER_ID_CONTENT_ITEM_TYPE;
|
|
|
|
case SECRET_KEY_RING:
|
|
case SECRET_KEY_RING_BY_EMAILS:
|
|
return SECRET_KEY_RING_CONTENT_DIR_TYPE;
|
|
|
|
case SECRET_KEY_RING_ID:
|
|
return SECRET_KEY_RING_CONTENT_ITEM_TYPE;
|
|
|
|
case SECRET_KEY_RING_BY_KEY_ID:
|
|
return SECRET_KEY_RING_CONTENT_ITEM_TYPE;
|
|
|
|
case SECRET_KEY_RING_KEY:
|
|
return SECRET_KEY_CONTENT_DIR_TYPE;
|
|
|
|
case SECRET_KEY_RING_KEY_RANK:
|
|
return SECRET_KEY_CONTENT_ITEM_TYPE;
|
|
|
|
case SECRET_KEY_RING_USER_ID:
|
|
return USER_ID_CONTENT_DIR_TYPE;
|
|
|
|
case SECRET_KEY_RING_USER_ID_RANK:
|
|
return USER_ID_CONTENT_ITEM_TYPE;
|
|
|
|
default:
|
|
throw new IllegalArgumentException("Unknown URI " + uri);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public Uri insert(Uri uri, ContentValues initialValues) {
|
|
// not supported
|
|
return null;
|
|
}
|
|
|
|
@Override
|
|
public int delete(Uri uri, String where, String[] whereArgs) {
|
|
// not supported
|
|
return 0;
|
|
}
|
|
|
|
@Override
|
|
public int update(Uri uri, ContentValues values, String where, String[] whereArgs) {
|
|
// not supported
|
|
return 0;
|
|
}
|
|
|
|
@Override
|
|
public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {
|
|
int match = mUriMatcher.match(uri);
|
|
if (match != DATA_STREAM) {
|
|
throw new FileNotFoundException();
|
|
}
|
|
String fileName = uri.getPathSegments().get(1);
|
|
File file = new File(getContext().getFilesDir().getAbsolutePath(), fileName);
|
|
return ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY);
|
|
}
|
|
}
|