package org.bitseal.database;
import java.util.ArrayList;
import org.bitseal.data.Pubkey;
import org.bitseal.util.ByteFormatter;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.util.Base64;
import android.util.Log;
/**
* A singleton class which controls the creation, reading, updating, and
* deletion of stored Pubkey objects.
*
* @author Jonathan Coe
*/
public class PubkeyProvider
{
private static final String TAG = "PUBKEY_PROVIDER";
private static PubkeyProvider sPubkeyProvider;
private Context mAppContext;
private static ContentResolver mContentResolver;
private PubkeyProvider(Context appContext)
{
mAppContext = appContext;
mContentResolver = mAppContext.getContentResolver();
}
/**
* Returns an instance of this singleton class.
*
* @param c - A Context object for the currently running application
*/
public static PubkeyProvider get(Context c)
{
if (sPubkeyProvider == null)
{
Context appContext = c.getApplicationContext();
sPubkeyProvider = new PubkeyProvider(appContext);
}
return sPubkeyProvider;
}
/**
* Takes a Pubkey object and adds it to the app's
* SQLite database as a new record, returning the ID of the
* newly created record.
*
* @param p - The Pubkey object to be added
*
* @return id - A long value representing the ID of the newly
* created record
*/
public long addPubkey(Pubkey p)
{
int belongsToMe = 0;
if (p.belongsToMe())
{
belongsToMe = 1;
}
ContentValues values = new ContentValues();
values.put(PubkeysTable.COLUMN_BELONGS_TO_ME, belongsToMe);
values.put(PubkeysTable.COLUMN_POW_NONCE, p.getPOWNonce());
values.put(PubkeysTable.COLUMN_EXPIRATION_TIME, p.getExpirationTime());
values.put(PubkeysTable.COLUMN_OBJECT_TYPE, p.getObjectType());
values.put(PubkeysTable.COLUMN_OBJECT_VERSION, p.getObjectVersion());
values.put(PubkeysTable.COLUMN_STREAM_NUMBER, p.getStreamNumber());
values.put(PubkeysTable.COLUMN_CORRESPONDING_ADDRESS_ID, p.getCorrespondingAddressId());
values.put(PubkeysTable.COLUMN_RIPE_HASH, Base64.encodeToString(p.getRipeHash(), Base64.DEFAULT));
values.put(PubkeysTable.COLUMN_BEHAVIOUR_BITFIELD, p.getBehaviourBitfield());
values.put(PubkeysTable.COLUMN_PUBLIC_SIGNING_KEY, Base64.encodeToString(p.getPublicSigningKey(), Base64.DEFAULT));
values.put(PubkeysTable.COLUMN_PUBLIC_ENCRYPTION_KEY, Base64.encodeToString(p.getPublicEncryptionKey(), Base64.DEFAULT));
values.put(PubkeysTable.COLUMN_NONCE_TRIALS_PER_BYTE, p.getNonceTrialsPerByte());
values.put(PubkeysTable.COLUMN_EXTRA_BYTES, p.getExtraBytes());
values.put(PubkeysTable.COLUMN_SIGNATURE_LENGTH, p.getSignatureLength());
values.put(PubkeysTable.COLUMN_SIGNATURE, Base64.encodeToString(p.getSignature(), Base64.DEFAULT));
Uri insertionUri = mContentResolver.insert(DatabaseContentProvider.CONTENT_URI_PUBKEYS, values);
// Parse the ID of the newly created record from the insertion URI
String uriString = insertionUri.toString();
String idString = uriString.substring(uriString.indexOf("/") + 1);
long id = Long.parseLong(idString);
Log.i(TAG, "Pubkey with ripe hash " + ByteFormatter.byteArrayToHexString(p.getRipeHash()) + " and ID " + id + " saved to database");
return id;
}
/**
* Finds all Pubkeys in the application's database that match the given field
*
* @param columnName - A String specifying the name of the column in the database that
* should be used to find matching records. See the PubkeysTable class to find
* the relevant column name.
* @param searchString - A String specifying the value to search for. There are 4 use cases
* for this:<br>
* 1) The value to search for is a String (e.g. A label from the UI). In this case the value
* can be passed in directly.<br>
* 2) The value to search for is an int or long. In this case you should use String.valueOf(x)
* and pass in the resulting String.<br>
* 3) The value to search for is a boolean. In this case you should pass in the String "0" for
* false or the String "1" for true. <br>
* 4) The value to search for is a byte[]. In this case you should encode the byte[] into a
* Base64 encoded String using the class android.util.Base64 and pass in the resulting String.<br><br>
*
* <b>NOTE:</b> The above String conversion is very clumsy, but seems to be necessary. See
* https://stackoverflow.com/questions/20911760/android-how-to-query-sqlitedatabase-with-non-string-selection-args
*
* @return An ArrayList containing Pubkey objects populated with the data from
* the database search
*/
public ArrayList<Pubkey> searchPubkeys(String columnName, String searchString)
{
ArrayList<Pubkey> matchingRecords = new ArrayList<Pubkey>();
// Specify which columns from the table we are interested in
String[] projection = {
PubkeysTable.COLUMN_ID,
PubkeysTable.COLUMN_BELONGS_TO_ME,
PubkeysTable.COLUMN_POW_NONCE,
PubkeysTable.COLUMN_EXPIRATION_TIME,
PubkeysTable.COLUMN_OBJECT_TYPE,
PubkeysTable.COLUMN_OBJECT_VERSION,
PubkeysTable.COLUMN_STREAM_NUMBER,
PubkeysTable.COLUMN_CORRESPONDING_ADDRESS_ID,
PubkeysTable.COLUMN_RIPE_HASH,
PubkeysTable.COLUMN_BEHAVIOUR_BITFIELD,
PubkeysTable.COLUMN_PUBLIC_SIGNING_KEY,
PubkeysTable.COLUMN_PUBLIC_ENCRYPTION_KEY,
PubkeysTable.COLUMN_NONCE_TRIALS_PER_BYTE,
PubkeysTable.COLUMN_EXTRA_BYTES,
PubkeysTable.COLUMN_SIGNATURE_LENGTH,
PubkeysTable.COLUMN_SIGNATURE};
// Query the database via the ContentProvider
Cursor cursor = mContentResolver.query(
DatabaseContentProvider.CONTENT_URI_PUBKEYS,
projection,
PubkeysTable.TABLE_PUBKEYS + "." + columnName + " = ? ",
new String[]{searchString},
null);
if (cursor.moveToFirst())
{
do
{
long id = cursor.getLong(0);
int belongsToMeValue = cursor.getInt(1);
boolean belongsToMe = false;
if (belongsToMeValue == 1)
{
belongsToMe = true;
}
long powNonce = cursor.getLong(2);
long time = cursor.getLong(3);
int objectType = cursor.getInt(4);
int objectVersion = cursor.getInt(5);
int streamNumber = cursor.getInt(6);
long correspondingAddressId = cursor.getLong(7);
byte[] ripeHash = Base64.decode(cursor.getString(8), Base64.DEFAULT);
int behaviourBitfield = cursor.getInt(9);
byte[] publicSigningKey = Base64.decode(cursor.getString(10), Base64.DEFAULT);
byte[] publicEncryptionKey = Base64.decode(cursor.getString(11), Base64.DEFAULT);
int nonceTrialsPerByte = cursor.getInt(12);
int extraBytes = cursor.getInt(13);
int signatureLength = cursor.getInt(14);
byte[] signature = Base64.decode(cursor.getString(15), Base64.DEFAULT);
Pubkey p = new Pubkey();
p.setId(id);
p.setBelongsToMe(belongsToMe);
p.setPOWNonce(powNonce);
p.setExpirationTime(time);
p.setObjectType(objectType);
p.setObjectVersion(objectVersion);
p.setStreamNumber(streamNumber);
p.setCorrespondingAddressId(correspondingAddressId);
p.setRipeHash(ripeHash);
p.setBehaviourBitfield(behaviourBitfield);
p.setPublicSigningKey(publicSigningKey);
p.setPublicEncryptionKey(publicEncryptionKey);
p.setNonceTrialsPerByte(nonceTrialsPerByte);
p.setExtraBytes(extraBytes);
p.setSignatureLength(signatureLength);
p.setSignature(signature);
matchingRecords.add(p);
}
while (cursor.moveToNext());
}
else
{
Log.i(TAG, "Unable to find any Pubkeys with the value " + searchString + " in the " + columnName + " column");
cursor.close();
return matchingRecords;
}
cursor.close();
return matchingRecords;
}
/**
* Searches the database for the Pubkey with the given ID.
* This method will return exactly one Pubkey object or throw
* a RuntimeException.
*
* @param id - A long value representing the Pubkey's ID.
*
* @return The Pubkey object with the given ID.
*/
public Pubkey searchForSingleRecord(long id)
{
ArrayList<Pubkey> retrievedRecords = searchPubkeys(PubkeysTable.COLUMN_ID, String.valueOf(id));
if (retrievedRecords.size() != 1)
{
throw new RuntimeException("There should be exactly 1 record found in this search. Instead " + retrievedRecords.size() + " records were found");
}
else
{
return retrievedRecords.get(0);
}
}
/**
* Returns an ArrayList containing all the Pubkeys stored in the
* application's database
*
* @return An ArrayList containing one Pubkey object for
* each record in the Pubkeys table.
*/
public ArrayList<Pubkey> getAllPubkeys()
{
ArrayList<Pubkey> pubkeys = new ArrayList<Pubkey>();
// Specify which columns from the table we are interested in
String[] projection = {
PubkeysTable.COLUMN_ID,
PubkeysTable.COLUMN_BELONGS_TO_ME,
PubkeysTable.COLUMN_POW_NONCE,
PubkeysTable.COLUMN_EXPIRATION_TIME,
PubkeysTable.COLUMN_OBJECT_TYPE,
PubkeysTable.COLUMN_OBJECT_VERSION,
PubkeysTable.COLUMN_STREAM_NUMBER,
PubkeysTable.COLUMN_CORRESPONDING_ADDRESS_ID,
PubkeysTable.COLUMN_RIPE_HASH,
PubkeysTable.COLUMN_BEHAVIOUR_BITFIELD,
PubkeysTable.COLUMN_PUBLIC_SIGNING_KEY,
PubkeysTable.COLUMN_PUBLIC_ENCRYPTION_KEY,
PubkeysTable.COLUMN_NONCE_TRIALS_PER_BYTE,
PubkeysTable.COLUMN_EXTRA_BYTES,
PubkeysTable.COLUMN_SIGNATURE_LENGTH,
PubkeysTable.COLUMN_SIGNATURE};
// Query the database via the ContentProvider
Cursor cursor = mContentResolver.query(
DatabaseContentProvider.CONTENT_URI_PUBKEYS,
projection,
null,
null,
null);
if (cursor.moveToFirst())
{
do
{
long id = cursor.getLong(0);
int belongsToMeValue = cursor.getInt(1);
boolean belongsToMe = false;
if (belongsToMeValue == 1)
{
belongsToMe = true;
}
long powNonce = cursor.getLong(2);
long time = cursor.getLong(3);
int objectType = cursor.getInt(4);
int objectVersion = cursor.getInt(5);
int streamNumber = cursor.getInt(6);
long correspondingAddressId = cursor.getLong(7);
byte[] ripeHash = Base64.decode(cursor.getString(8), Base64.DEFAULT);
int behaviourBitfield = cursor.getInt(9);
byte[] publicSigningKey = Base64.decode(cursor.getString(10), Base64.DEFAULT);
byte[] publicEncryptionKey = Base64.decode(cursor.getString(11), Base64.DEFAULT);
int nonceTrialsPerByte = cursor.getInt(12);
int extraBytes = cursor.getInt(13);
int signatureLength = cursor.getInt(14);
byte[] signature = Base64.decode(cursor.getString(15), Base64.DEFAULT);
Pubkey p = new Pubkey();
p.setId(id);
p.setBelongsToMe(belongsToMe);
p.setPOWNonce(powNonce);
p.setExpirationTime(time);
p.setObjectType(objectType);
p.setObjectVersion(objectVersion);
p.setStreamNumber(streamNumber);
p.setCorrespondingAddressId(correspondingAddressId);
p.setRipeHash(ripeHash);
p.setBehaviourBitfield(behaviourBitfield);
p.setPublicSigningKey(publicSigningKey);
p.setPublicEncryptionKey(publicEncryptionKey);
p.setNonceTrialsPerByte(nonceTrialsPerByte);
p.setExtraBytes(extraBytes);
p.setSignatureLength(signatureLength);
p.setSignature(signature);
pubkeys.add(p);
}
while (cursor.moveToNext());
}
cursor.close();
return pubkeys;
}
/**
* Updates the database record for a given Pubkey object<br><br>
*
* <b>NOTE:</b> This method uses the given Pubkey's ID field to determine
* which record in the database to update
*
* @param p - The Pubkey object to be updated
*/
public void updatePubkey(Pubkey p)
{
int belongsToMe = 0;
if (p.belongsToMe())
{
belongsToMe = 1;
}
ContentValues values = new ContentValues();
values.put(PubkeysTable.COLUMN_BELONGS_TO_ME, belongsToMe);
values.put(PubkeysTable.COLUMN_POW_NONCE, p.getPOWNonce());
values.put(PubkeysTable.COLUMN_EXPIRATION_TIME, p.getExpirationTime());
values.put(PubkeysTable.COLUMN_OBJECT_TYPE, p.getObjectType());
values.put(PubkeysTable.COLUMN_OBJECT_VERSION, p.getObjectVersion());
values.put(PubkeysTable.COLUMN_STREAM_NUMBER, p.getStreamNumber());
values.put(PubkeysTable.COLUMN_CORRESPONDING_ADDRESS_ID, p.getCorrespondingAddressId());
values.put(PubkeysTable.COLUMN_RIPE_HASH, Base64.encodeToString(p.getRipeHash(), Base64.DEFAULT));
values.put(PubkeysTable.COLUMN_BEHAVIOUR_BITFIELD, p.getBehaviourBitfield());
values.put(PubkeysTable.COLUMN_PUBLIC_SIGNING_KEY, Base64.encodeToString(p.getPublicSigningKey(), Base64.DEFAULT));
values.put(PubkeysTable.COLUMN_PUBLIC_ENCRYPTION_KEY, Base64.encodeToString(p.getPublicEncryptionKey(), Base64.DEFAULT));
values.put(PubkeysTable.COLUMN_NONCE_TRIALS_PER_BYTE, p.getNonceTrialsPerByte());
values.put(PubkeysTable.COLUMN_EXTRA_BYTES, p.getExtraBytes());
values.put(PubkeysTable.COLUMN_SIGNATURE_LENGTH, p.getSignatureLength());
values.put(PubkeysTable.COLUMN_SIGNATURE, Base64.encodeToString(p.getSignature(), Base64.DEFAULT));
long id = p.getId();
// Query the database via the ContentProvider and update the record with the matching ID
mContentResolver.update(DatabaseContentProvider.CONTENT_URI_PUBKEYS,
values,
PubkeysTable.COLUMN_ID + " = ? ",
new String[]{String.valueOf(id)});
Log.i(TAG, "Pubkey ID " + id + " updated");
}
/**
* Deletes a Pubkey object from the application's SQLite database<br><br>
*
* <b>NOTE:</b> This method uses the given Pubkey's ID field to determine
* which record in the database to delete
*
* @param p - The Pubkey object to be deleted
*/
public void deletePubkey(Pubkey p)
{
long id = p.getId();
// Query the database via the ContentProvider and delete the record with the matching ID
int recordsDeleted = mContentResolver.delete(
DatabaseContentProvider.CONTENT_URI_PUBKEYS,
PubkeysTable.COLUMN_ID + " = ? ",
new String[]{String.valueOf(id)});
Log.i(TAG, recordsDeleted + " Pubkey(s) deleted from database");
}
/**
* Deletes all Pubkeys from the database
*/
public void deleteAllPubkeys()
{
// Query the database via the ContentProvider and delete all the records
int recordsDeleted = mContentResolver.delete(
DatabaseContentProvider.CONTENT_URI_PUBKEYS,
null,
null);
Log.i(TAG, recordsDeleted + " Pubkey(s) deleted from database");
}
}