2015-01-15 21:35:35 +00:00
|
|
|
package org.thoughtcrime.securesms.database;
|
|
|
|
|
|
|
|
import android.content.ContentValues;
|
|
|
|
import android.content.Context;
|
|
|
|
import android.database.Cursor;
|
|
|
|
import android.database.sqlite.SQLiteDatabase;
|
|
|
|
import android.database.sqlite.SQLiteOpenHelper;
|
|
|
|
import android.text.TextUtils;
|
|
|
|
import android.util.Log;
|
|
|
|
|
|
|
|
import org.thoughtcrime.securesms.database.documents.Document;
|
|
|
|
import org.thoughtcrime.securesms.database.documents.IdentityKeyMismatch;
|
|
|
|
import org.thoughtcrime.securesms.database.documents.IdentityKeyMismatchList;
|
|
|
|
import org.thoughtcrime.securesms.util.JsonUtils;
|
2016-03-23 17:34:41 +00:00
|
|
|
import org.whispersystems.libsignal.IdentityKey;
|
2015-01-15 21:35:35 +00:00
|
|
|
|
|
|
|
import java.io.IOException;
|
|
|
|
import java.util.ArrayList;
|
|
|
|
import java.util.Iterator;
|
|
|
|
import java.util.List;
|
|
|
|
|
|
|
|
public abstract class MessagingDatabase extends Database implements MmsSmsColumns {
|
|
|
|
|
|
|
|
private static final String TAG = MessagingDatabase.class.getSimpleName();
|
|
|
|
|
|
|
|
public MessagingDatabase(Context context, SQLiteOpenHelper databaseHelper) {
|
|
|
|
super(context, databaseHelper);
|
|
|
|
}
|
|
|
|
|
|
|
|
protected abstract String getTableName();
|
|
|
|
|
2017-07-26 16:59:15 +00:00
|
|
|
public void setMismatchedIdentity(long messageId, final Address address, final IdentityKey identityKey) {
|
2015-05-26 20:12:11 +00:00
|
|
|
List<IdentityKeyMismatch> items = new ArrayList<IdentityKeyMismatch>() {{
|
2017-07-26 16:59:15 +00:00
|
|
|
add(new IdentityKeyMismatch(address, identityKey));
|
2015-05-26 20:12:11 +00:00
|
|
|
}};
|
|
|
|
|
|
|
|
IdentityKeyMismatchList document = new IdentityKeyMismatchList(items);
|
|
|
|
|
|
|
|
SQLiteDatabase database = databaseHelper.getWritableDatabase();
|
|
|
|
database.beginTransaction();
|
|
|
|
|
|
|
|
try {
|
|
|
|
setDocument(database, messageId, MISMATCHED_IDENTITIES, document);
|
|
|
|
|
|
|
|
database.setTransactionSuccessful();
|
|
|
|
} catch (IOException ioe) {
|
|
|
|
Log.w(TAG, ioe);
|
|
|
|
} finally {
|
|
|
|
database.endTransaction();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-07-26 16:59:15 +00:00
|
|
|
public void addMismatchedIdentity(long messageId, Address address, IdentityKey identityKey) {
|
2015-01-15 21:35:35 +00:00
|
|
|
try {
|
|
|
|
addToDocument(messageId, MISMATCHED_IDENTITIES,
|
2017-07-26 16:59:15 +00:00
|
|
|
new IdentityKeyMismatch(address, identityKey),
|
2015-01-15 21:35:35 +00:00
|
|
|
IdentityKeyMismatchList.class);
|
|
|
|
} catch (IOException e) {
|
|
|
|
Log.w(TAG, e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-07-26 16:59:15 +00:00
|
|
|
public void removeMismatchedIdentity(long messageId, Address address, IdentityKey identityKey) {
|
2015-01-15 21:35:35 +00:00
|
|
|
try {
|
|
|
|
removeFromDocument(messageId, MISMATCHED_IDENTITIES,
|
2017-07-26 16:59:15 +00:00
|
|
|
new IdentityKeyMismatch(address, identityKey),
|
2015-01-15 21:35:35 +00:00
|
|
|
IdentityKeyMismatchList.class);
|
|
|
|
} catch (IOException e) {
|
|
|
|
Log.w(TAG, e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
protected <D extends Document<I>, I> void removeFromDocument(long messageId, String column, I object, Class<D> clazz) throws IOException {
|
|
|
|
SQLiteDatabase database = databaseHelper.getWritableDatabase();
|
|
|
|
database.beginTransaction();
|
|
|
|
|
|
|
|
try {
|
|
|
|
D document = getDocument(database, messageId, column, clazz);
|
|
|
|
Iterator<I> iterator = document.getList().iterator();
|
|
|
|
|
|
|
|
while (iterator.hasNext()) {
|
|
|
|
I item = iterator.next();
|
|
|
|
|
|
|
|
if (item.equals(object)) {
|
|
|
|
iterator.remove();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
setDocument(database, messageId, column, document);
|
|
|
|
database.setTransactionSuccessful();
|
|
|
|
} finally {
|
|
|
|
database.endTransaction();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
protected <T extends Document<I>, I> void addToDocument(long messageId, String column, final I object, Class<T> clazz) throws IOException {
|
|
|
|
List<I> list = new ArrayList<I>() {{
|
|
|
|
add(object);
|
|
|
|
}};
|
|
|
|
|
|
|
|
addToDocument(messageId, column, list, clazz);
|
|
|
|
}
|
|
|
|
|
|
|
|
protected <T extends Document<I>, I> void addToDocument(long messageId, String column, List<I> objects, Class<T> clazz) throws IOException {
|
|
|
|
SQLiteDatabase database = databaseHelper.getWritableDatabase();
|
|
|
|
database.beginTransaction();
|
|
|
|
|
|
|
|
try {
|
|
|
|
T document = getDocument(database, messageId, column, clazz);
|
|
|
|
document.getList().addAll(objects);
|
|
|
|
setDocument(database, messageId, column, document);
|
|
|
|
|
|
|
|
database.setTransactionSuccessful();
|
|
|
|
} finally {
|
|
|
|
database.endTransaction();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private void setDocument(SQLiteDatabase database, long messageId, String column, Document document) throws IOException {
|
|
|
|
ContentValues contentValues = new ContentValues();
|
|
|
|
|
|
|
|
if (document == null || document.size() == 0) {
|
|
|
|
contentValues.put(column, (String)null);
|
|
|
|
} else {
|
|
|
|
contentValues.put(column, JsonUtils.toJson(document));
|
|
|
|
}
|
|
|
|
|
|
|
|
database.update(getTableName(), contentValues, ID_WHERE, new String[] {String.valueOf(messageId)});
|
|
|
|
}
|
|
|
|
|
|
|
|
private <D extends Document> D getDocument(SQLiteDatabase database, long messageId,
|
|
|
|
String column, Class<D> clazz)
|
|
|
|
{
|
|
|
|
Cursor cursor = null;
|
|
|
|
|
|
|
|
try {
|
|
|
|
cursor = database.query(getTableName(), new String[] {column},
|
|
|
|
ID_WHERE, new String[] {String.valueOf(messageId)},
|
|
|
|
null, null, null);
|
|
|
|
|
|
|
|
if (cursor != null && cursor.moveToNext()) {
|
|
|
|
String document = cursor.getString(cursor.getColumnIndexOrThrow(column));
|
|
|
|
|
|
|
|
try {
|
|
|
|
if (!TextUtils.isEmpty(document)) {
|
|
|
|
return JsonUtils.fromJson(document, clazz);
|
|
|
|
}
|
|
|
|
} catch (IOException e) {
|
|
|
|
Log.w(TAG, e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
try {
|
|
|
|
return clazz.newInstance();
|
2015-05-26 20:12:11 +00:00
|
|
|
} catch (InstantiationException e) {
|
|
|
|
throw new AssertionError(e);
|
|
|
|
} catch (IllegalAccessException e) {
|
2015-01-15 21:35:35 +00:00
|
|
|
throw new AssertionError(e);
|
|
|
|
}
|
|
|
|
|
|
|
|
} finally {
|
|
|
|
if (cursor != null)
|
|
|
|
cursor.close();
|
|
|
|
}
|
|
|
|
}
|
2016-02-20 01:07:41 +00:00
|
|
|
|
|
|
|
public static class SyncMessageId {
|
|
|
|
|
2017-07-26 16:59:15 +00:00
|
|
|
private final Address address;
|
2016-02-20 01:07:41 +00:00
|
|
|
private final long timetamp;
|
|
|
|
|
2017-07-26 16:59:15 +00:00
|
|
|
public SyncMessageId(Address address, long timetamp) {
|
2016-10-10 18:13:37 +00:00
|
|
|
this.address = address;
|
2016-02-20 01:07:41 +00:00
|
|
|
this.timetamp = timetamp;
|
|
|
|
}
|
|
|
|
|
2017-07-26 16:59:15 +00:00
|
|
|
public Address getAddress() {
|
2016-02-20 01:07:41 +00:00
|
|
|
return address;
|
|
|
|
}
|
|
|
|
|
|
|
|
public long getTimetamp() {
|
|
|
|
return timetamp;
|
|
|
|
}
|
|
|
|
}
|
2016-10-10 18:13:37 +00:00
|
|
|
|
|
|
|
public static class ExpirationInfo {
|
|
|
|
|
|
|
|
private final long id;
|
|
|
|
private final long expiresIn;
|
|
|
|
private final long expireStarted;
|
|
|
|
private final boolean mms;
|
|
|
|
|
|
|
|
public ExpirationInfo(long id, long expiresIn, long expireStarted, boolean mms) {
|
|
|
|
this.id = id;
|
|
|
|
this.expiresIn = expiresIn;
|
|
|
|
this.expireStarted = expireStarted;
|
|
|
|
this.mms = mms;
|
|
|
|
}
|
|
|
|
|
|
|
|
public long getId() {
|
|
|
|
return id;
|
|
|
|
}
|
|
|
|
|
|
|
|
public long getExpiresIn() {
|
|
|
|
return expiresIn;
|
|
|
|
}
|
|
|
|
|
|
|
|
public long getExpireStarted() {
|
|
|
|
return expireStarted;
|
|
|
|
}
|
|
|
|
|
|
|
|
public boolean isMms() {
|
|
|
|
return mms;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public static class MarkedMessageInfo {
|
|
|
|
|
|
|
|
private final SyncMessageId syncMessageId;
|
|
|
|
private final ExpirationInfo expirationInfo;
|
|
|
|
|
|
|
|
public MarkedMessageInfo(SyncMessageId syncMessageId, ExpirationInfo expirationInfo) {
|
|
|
|
this.syncMessageId = syncMessageId;
|
|
|
|
this.expirationInfo = expirationInfo;
|
|
|
|
}
|
|
|
|
|
|
|
|
public SyncMessageId getSyncMessageId() {
|
|
|
|
return syncMessageId;
|
|
|
|
}
|
|
|
|
|
|
|
|
public ExpirationInfo getExpirationInfo() {
|
|
|
|
return expirationInfo;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-01-22 21:52:36 +00:00
|
|
|
public static class InsertResult {
|
|
|
|
private final long messageId;
|
|
|
|
private final long threadId;
|
|
|
|
|
|
|
|
public InsertResult(long messageId, long threadId) {
|
|
|
|
this.messageId = messageId;
|
|
|
|
this.threadId = threadId;
|
|
|
|
}
|
|
|
|
|
|
|
|
public long getMessageId() {
|
|
|
|
return messageId;
|
|
|
|
}
|
|
|
|
|
|
|
|
public long getThreadId() {
|
|
|
|
return threadId;
|
|
|
|
}
|
|
|
|
}
|
2015-01-15 21:35:35 +00:00
|
|
|
}
|