mirror of
https://github.com/oxen-io/session-android.git
synced 2024-12-22 07:57:30 +00:00
Removed tabs
This commit is contained in:
parent
030b39cd9c
commit
82f8ba3584
@ -36,14 +36,15 @@ import org.thoughtcrime.securesms.util.MemoryCleaner;
|
|||||||
*
|
*
|
||||||
* @author Moxie Marlinspike
|
* @author Moxie Marlinspike
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public class PassphraseCreateActivity extends PassphraseActivity {
|
public class PassphraseCreateActivity extends PassphraseActivity {
|
||||||
|
|
||||||
private EditText passphraseEdit;
|
private EditText passphraseEdit;
|
||||||
private EditText passphraseRepeatEdit;
|
private EditText passphraseRepeatEdit;
|
||||||
private Button okButton;
|
private Button okButton;
|
||||||
private Button cancelButton;
|
private Button cancelButton;
|
||||||
|
|
||||||
public PassphraseCreateActivity() { }
|
public PassphraseCreateActivity() { }
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
|
@ -177,11 +177,11 @@ public class ReviewIdentitiesActivity extends SherlockListActivity {
|
|||||||
boolean valid;
|
boolean valid;
|
||||||
|
|
||||||
String identityKeyString = cursor.getString(cursor.getColumnIndexOrThrow(IdentityDatabase.IDENTITY_KEY));
|
String identityKeyString = cursor.getString(cursor.getColumnIndexOrThrow(IdentityDatabase.IDENTITY_KEY));
|
||||||
String identityName = cursor.getString(cursor.getColumnIndexOrThrow(IdentityDatabase.IDENTITY_NAME));
|
String identityName = cursor.getString(cursor.getColumnIndexOrThrow(IdentityDatabase.IDENTITY_NAME));
|
||||||
|
|
||||||
try {
|
try {
|
||||||
String mac = cursor.getString(cursor.getColumnIndexOrThrow(IdentityDatabase.MAC));
|
String mac = cursor.getString(cursor.getColumnIndexOrThrow(IdentityDatabase.MAC));
|
||||||
valid = masterCipher.verifyMacFor(identityName + identityKeyString, Base64.decode(mac));
|
valid = masterCipher.verifyMacFor(identityName + identityKeyString, Base64.decode(mac));
|
||||||
identityKey = new IdentityKey(Base64.decode(identityKeyString), 0);
|
identityKey = new IdentityKey(Base64.decode(identityKeyString), 0);
|
||||||
} catch (InvalidKeyException ike) {
|
} catch (InvalidKeyException ike) {
|
||||||
Log.w("ReviewIdentitiesActivity",ike);
|
Log.w("ReviewIdentitiesActivity",ike);
|
||||||
|
@ -86,7 +86,7 @@ public class CanonicalAddressDatabase {
|
|||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
if (cursor != null)
|
if (cursor != null)
|
||||||
cursor.close();
|
cursor.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -154,12 +154,12 @@ public class CanonicalAddressDatabase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(SQLiteDatabase db) {
|
public void onCreate(SQLiteDatabase db) {
|
||||||
db.execSQL(DATABASE_CREATE);
|
db.execSQL(DATABASE_CREATE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
|
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Copyright (C) 2011 Whisper Systems
|
* Copyright (C) 2011 Whisper Systems
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
@ -10,40 +10,41 @@
|
|||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
* GNU General Public License for more details.
|
* GNU General Public License for more details.
|
||||||
*
|
*
|
||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
package org.thoughtcrime.securesms.database;
|
package org.thoughtcrime.securesms.database;
|
||||||
|
|
||||||
import java.io.UnsupportedEncodingException;
|
import android.content.ContentValues;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
import ws.com.google.android.mms.pdu.CharacterSets;
|
import ws.com.google.android.mms.pdu.CharacterSets;
|
||||||
import ws.com.google.android.mms.pdu.EncodedStringValue;
|
import ws.com.google.android.mms.pdu.EncodedStringValue;
|
||||||
import android.content.ContentValues;
|
|
||||||
import android.util.Log;
|
import java.io.UnsupportedEncodingException;
|
||||||
|
|
||||||
public class ContentValuesBuilder {
|
public class ContentValuesBuilder {
|
||||||
|
|
||||||
private final ContentValues contentValues;
|
private final ContentValues contentValues;
|
||||||
|
|
||||||
public ContentValuesBuilder(ContentValues contentValues) {
|
public ContentValuesBuilder(ContentValues contentValues) {
|
||||||
this.contentValues = contentValues;
|
this.contentValues = contentValues;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void add(String key, String charsetKey, EncodedStringValue value) {
|
public void add(String key, String charsetKey, EncodedStringValue value) {
|
||||||
if (value != null) {
|
if (value != null) {
|
||||||
contentValues.put(key, toIsoString(value.getTextString()));
|
contentValues.put(key, toIsoString(value.getTextString()));
|
||||||
contentValues.put(charsetKey, value.getCharacterSet());
|
contentValues.put(charsetKey, value.getCharacterSet());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void add(String contentKey, byte[] value) {
|
public void add(String contentKey, byte[] value) {
|
||||||
if (value != null) {
|
if (value != null) {
|
||||||
contentValues.put(contentKey, toIsoString(value));
|
contentValues.put(contentKey, toIsoString(value));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void add(String contentKey, int b) {
|
public void add(String contentKey, int b) {
|
||||||
if (b != 0)
|
if (b != 0)
|
||||||
contentValues.put(contentKey, b);
|
contentValues.put(contentKey, b);
|
||||||
@ -53,11 +54,11 @@ public class ContentValuesBuilder {
|
|||||||
if (value != -1L)
|
if (value != -1L)
|
||||||
contentValues.put(contentKey, value);
|
contentValues.put(contentKey, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ContentValues getContentValues() {
|
public ContentValues getContentValues() {
|
||||||
return contentValues;
|
return contentValues;
|
||||||
}
|
}
|
||||||
|
|
||||||
private String toIsoString(byte[] bytes) {
|
private String toIsoString(byte[] bytes) {
|
||||||
try {
|
try {
|
||||||
return new String(bytes, CharacterSets.MIMENAME_ISO_8859_1);
|
return new String(bytes, CharacterSets.MIMENAME_ISO_8859_1);
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Copyright (C) 2011 Whisper Systems
|
* Copyright (C) 2011 Whisper Systems
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
@ -10,50 +10,50 @@
|
|||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
* GNU General Public License for more details.
|
* GNU General Public License for more details.
|
||||||
*
|
*
|
||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
package org.thoughtcrime.securesms.database;
|
package org.thoughtcrime.securesms.database;
|
||||||
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
import android.database.sqlite.SQLiteOpenHelper;
|
import android.database.sqlite.SQLiteOpenHelper;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
public abstract class Database {
|
public abstract class Database {
|
||||||
|
|
||||||
protected static final String ID_WHERE = "_id = ?";
|
protected static final String ID_WHERE = "_id = ?";
|
||||||
private static final String CONVERSATION_URI = "content://textsecure/thread/";
|
private static final String CONVERSATION_URI = "content://textsecure/thread/";
|
||||||
private static final String CONVERSATION_LIST_URI = "content://textsecure/conversation-list";
|
private static final String CONVERSATION_LIST_URI = "content://textsecure/conversation-list";
|
||||||
|
|
||||||
protected final SQLiteOpenHelper databaseHelper;
|
protected final SQLiteOpenHelper databaseHelper;
|
||||||
protected final Context context;
|
protected final Context context;
|
||||||
|
|
||||||
public Database(Context context, SQLiteOpenHelper databaseHelper) {
|
public Database(Context context, SQLiteOpenHelper databaseHelper) {
|
||||||
this.context = context;
|
this.context = context;
|
||||||
this.databaseHelper = databaseHelper;
|
this.databaseHelper = databaseHelper;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void notifyConversationListeners(Set<Long> threadIds) {
|
protected void notifyConversationListeners(Set<Long> threadIds) {
|
||||||
for (long threadId : threadIds)
|
for (long threadId : threadIds)
|
||||||
notifyConversationListeners(threadId);
|
notifyConversationListeners(threadId);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void notifyConversationListeners(long threadId) {
|
protected void notifyConversationListeners(long threadId) {
|
||||||
context.getContentResolver().notifyChange(Uri.parse(CONVERSATION_URI + threadId), null);
|
context.getContentResolver().notifyChange(Uri.parse(CONVERSATION_URI + threadId), null);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void notifyConversationListListeners() {
|
protected void notifyConversationListListeners() {
|
||||||
context.getContentResolver().notifyChange(Uri.parse(CONVERSATION_LIST_URI), null);
|
context.getContentResolver().notifyChange(Uri.parse(CONVERSATION_LIST_URI), null);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void setNotifyConverationListeners(Cursor cursor, long threadId) {
|
protected void setNotifyConverationListeners(Cursor cursor, long threadId) {
|
||||||
cursor.setNotificationUri(context.getContentResolver(), Uri.parse(CONVERSATION_URI + threadId));
|
cursor.setNotificationUri(context.getContentResolver(), Uri.parse(CONVERSATION_URI + threadId));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void setNotifyConverationListListeners(Cursor cursor) {
|
protected void setNotifyConverationListListeners(Cursor cursor) {
|
||||||
cursor.setNotificationUri(context.getContentResolver(), Uri.parse(CONVERSATION_LIST_URI));
|
cursor.setNotificationUri(context.getContentResolver(), Uri.parse(CONVERSATION_LIST_URI));
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Copyright (C) 2011 Whisper Systems
|
* Copyright (C) 2011 Whisper Systems
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
@ -10,30 +10,31 @@
|
|||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
* GNU General Public License for more details.
|
* GNU General Public License for more details.
|
||||||
*
|
*
|
||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
package org.thoughtcrime.securesms.database;
|
package org.thoughtcrime.securesms.database;
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.crypto.MasterSecret;
|
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.database.sqlite.SQLiteDatabase;
|
import android.database.sqlite.SQLiteDatabase;
|
||||||
import android.database.sqlite.SQLiteOpenHelper;
|
|
||||||
import android.database.sqlite.SQLiteDatabase.CursorFactory;
|
import android.database.sqlite.SQLiteDatabase.CursorFactory;
|
||||||
|
import android.database.sqlite.SQLiteOpenHelper;
|
||||||
|
|
||||||
|
import org.thoughtcrime.securesms.crypto.MasterSecret;
|
||||||
|
|
||||||
public class DatabaseFactory {
|
public class DatabaseFactory {
|
||||||
|
|
||||||
private static final int INTRODUCED_IDENTITIES_VERSION = 2;
|
private static final int INTRODUCED_IDENTITIES_VERSION = 2;
|
||||||
private static final int DATABASE_VERSION = 2;
|
private static final int DATABASE_VERSION = 2;
|
||||||
|
|
||||||
private static final String DATABASE_NAME = "messages.db";
|
private static final String DATABASE_NAME = "messages.db";
|
||||||
private static final Object lock = new Object();
|
private static final Object lock = new Object();
|
||||||
|
|
||||||
private static DatabaseFactory instance;
|
private static DatabaseFactory instance;
|
||||||
private static EncryptingMmsDatabase encryptingMmsInstance;
|
private static EncryptingMmsDatabase encryptingMmsInstance;
|
||||||
private static EncryptingPartDatabase encryptingPartInstance;
|
private static EncryptingPartDatabase encryptingPartInstance;
|
||||||
|
|
||||||
private final DatabaseHelper databaseHelper;
|
private final DatabaseHelper databaseHelper;
|
||||||
|
|
||||||
private final SmsDatabase sms;
|
private final SmsDatabase sms;
|
||||||
@ -45,70 +46,70 @@ public class DatabaseFactory {
|
|||||||
private final MmsAddressDatabase mmsAddress;
|
private final MmsAddressDatabase mmsAddress;
|
||||||
private final MmsSmsDatabase mmsSmsDatabase;
|
private final MmsSmsDatabase mmsSmsDatabase;
|
||||||
private final IdentityDatabase identityDatabase;
|
private final IdentityDatabase identityDatabase;
|
||||||
|
|
||||||
public static DatabaseFactory getInstance(Context context) {
|
public static DatabaseFactory getInstance(Context context) {
|
||||||
synchronized (lock) {
|
synchronized (lock) {
|
||||||
if (instance == null)
|
if (instance == null)
|
||||||
instance = new DatabaseFactory(context);
|
instance = new DatabaseFactory(context);
|
||||||
|
|
||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static MmsSmsDatabase getMmsSmsDatabase(Context context) {
|
public static MmsSmsDatabase getMmsSmsDatabase(Context context) {
|
||||||
return getInstance(context).mmsSmsDatabase;
|
return getInstance(context).mmsSmsDatabase;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ThreadDatabase getThreadDatabase(Context context) {
|
public static ThreadDatabase getThreadDatabase(Context context) {
|
||||||
return getInstance(context).thread;
|
return getInstance(context).thread;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static SmsDatabase getSmsDatabase(Context context) {
|
public static SmsDatabase getSmsDatabase(Context context) {
|
||||||
return getInstance(context).sms;
|
return getInstance(context).sms;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static MmsDatabase getMmsDatabase(Context context) {
|
public static MmsDatabase getMmsDatabase(Context context) {
|
||||||
return getInstance(context).mms;
|
return getInstance(context).mms;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static CanonicalAddressDatabase getAddressDatabase(Context context) {
|
public static CanonicalAddressDatabase getAddressDatabase(Context context) {
|
||||||
return getInstance(context).address;
|
return getInstance(context).address;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static EncryptingSmsDatabase getEncryptingSmsDatabase(Context context) {
|
public static EncryptingSmsDatabase getEncryptingSmsDatabase(Context context) {
|
||||||
return getInstance(context).encryptingSms;
|
return getInstance(context).encryptingSms;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static EncryptingMmsDatabase getEncryptingMmsDatabase(Context context, MasterSecret masterSecret) {
|
public static EncryptingMmsDatabase getEncryptingMmsDatabase(Context context, MasterSecret masterSecret) {
|
||||||
synchronized (lock) {
|
synchronized (lock) {
|
||||||
if (encryptingMmsInstance == null) {
|
if (encryptingMmsInstance == null) {
|
||||||
DatabaseFactory factory = getInstance(context);
|
DatabaseFactory factory = getInstance(context);
|
||||||
encryptingMmsInstance = new EncryptingMmsDatabase(context, factory.databaseHelper, masterSecret);
|
encryptingMmsInstance = new EncryptingMmsDatabase(context, factory.databaseHelper, masterSecret);
|
||||||
}
|
}
|
||||||
|
|
||||||
return encryptingMmsInstance;
|
return encryptingMmsInstance;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static PartDatabase getPartDatabase(Context context) {
|
public static PartDatabase getPartDatabase(Context context) {
|
||||||
return getInstance(context).part;
|
return getInstance(context).part;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static EncryptingPartDatabase getEncryptingPartDatabase(Context context, MasterSecret masterSecret) {
|
public static EncryptingPartDatabase getEncryptingPartDatabase(Context context, MasterSecret masterSecret) {
|
||||||
synchronized (lock) {
|
synchronized (lock) {
|
||||||
if (encryptingPartInstance == null) {
|
if (encryptingPartInstance == null) {
|
||||||
DatabaseFactory factory = getInstance(context);
|
DatabaseFactory factory = getInstance(context);
|
||||||
encryptingPartInstance = new EncryptingPartDatabase(context, factory.databaseHelper, masterSecret);
|
encryptingPartInstance = new EncryptingPartDatabase(context, factory.databaseHelper, masterSecret);
|
||||||
}
|
}
|
||||||
|
|
||||||
return encryptingPartInstance;
|
return encryptingPartInstance;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static MmsAddressDatabase getMmsAddressDatabase(Context context) {
|
public static MmsAddressDatabase getMmsAddressDatabase(Context context) {
|
||||||
return getInstance(context).mmsAddress;
|
return getInstance(context).mmsAddress;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IdentityDatabase getIdentityDatabase(Context context) {
|
public static IdentityDatabase getIdentityDatabase(Context context) {
|
||||||
return getInstance(context).identityDatabase;
|
return getInstance(context).identityDatabase;
|
||||||
}
|
}
|
||||||
@ -125,13 +126,13 @@ public class DatabaseFactory {
|
|||||||
this.mmsSmsDatabase = new MmsSmsDatabase(context, databaseHelper);
|
this.mmsSmsDatabase = new MmsSmsDatabase(context, databaseHelper);
|
||||||
this.identityDatabase = new IdentityDatabase(context, databaseHelper);
|
this.identityDatabase = new IdentityDatabase(context, databaseHelper);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void close() {
|
public void close() {
|
||||||
databaseHelper.close();
|
databaseHelper.close();
|
||||||
address.close();
|
address.close();
|
||||||
instance = null;
|
instance = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class DatabaseHelper extends SQLiteOpenHelper {
|
private static class DatabaseHelper extends SQLiteOpenHelper {
|
||||||
|
|
||||||
public DatabaseHelper(Context context, String name, CursorFactory factory, int version) {
|
public DatabaseHelper(Context context, String name, CursorFactory factory, int version) {
|
||||||
@ -146,14 +147,13 @@ public class DatabaseFactory {
|
|||||||
db.execSQL(ThreadDatabase.CREATE_TABLE);
|
db.execSQL(ThreadDatabase.CREATE_TABLE);
|
||||||
db.execSQL(MmsAddressDatabase.CREATE_TABLE);
|
db.execSQL(MmsAddressDatabase.CREATE_TABLE);
|
||||||
db.execSQL(IdentityDatabase.CREATE_TABLE);
|
db.execSQL(IdentityDatabase.CREATE_TABLE);
|
||||||
// db.execSQL(CanonicalAddress.CREATE_TABLE);
|
// db.execSQL(CanonicalAddress.CREATE_TABLE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
|
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
|
||||||
if (oldVersion < INTRODUCED_IDENTITIES_VERSION)
|
if (oldVersion < INTRODUCED_IDENTITIES_VERSION)
|
||||||
db.execSQL(IdentityDatabase.CREATE_TABLE);
|
db.execSQL(IdentityDatabase.CREATE_TABLE);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Copyright (C) 2011 Whisper Systems
|
* Copyright (C) 2011 Whisper Systems
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
@ -10,28 +10,28 @@
|
|||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
* GNU General Public License for more details.
|
* GNU General Public License for more details.
|
||||||
*
|
*
|
||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
package org.thoughtcrime.securesms.database;
|
package org.thoughtcrime.securesms.database;
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.crypto.MasterSecret;
|
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.database.sqlite.SQLiteOpenHelper;
|
import android.database.sqlite.SQLiteOpenHelper;
|
||||||
|
|
||||||
|
import org.thoughtcrime.securesms.crypto.MasterSecret;
|
||||||
|
|
||||||
public class EncryptingMmsDatabase extends MmsDatabase {
|
public class EncryptingMmsDatabase extends MmsDatabase {
|
||||||
|
|
||||||
private final MasterSecret masterSecret;
|
private final MasterSecret masterSecret;
|
||||||
|
|
||||||
public EncryptingMmsDatabase(Context context, SQLiteOpenHelper databaseHelper, MasterSecret masterSecret) {
|
public EncryptingMmsDatabase(Context context, SQLiteOpenHelper databaseHelper, MasterSecret masterSecret) {
|
||||||
super(context, databaseHelper);
|
super(context, databaseHelper);
|
||||||
this.masterSecret = masterSecret;
|
this.masterSecret = masterSecret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected PartDatabase getPartDatabase() {
|
protected PartDatabase getPartDatabase() {
|
||||||
return DatabaseFactory.getEncryptingPartDatabase(context, masterSecret);
|
return DatabaseFactory.getEncryptingPartDatabase(context, masterSecret);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Copyright (C) 2011 Whisper Systems
|
* Copyright (C) 2011 Whisper Systems
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
@ -10,30 +10,31 @@
|
|||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
* GNU General Public License for more details.
|
* GNU General Public License for more details.
|
||||||
*
|
*
|
||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
package org.thoughtcrime.securesms.database;
|
package org.thoughtcrime.securesms.database;
|
||||||
|
|
||||||
import java.io.File;
|
import android.content.Context;
|
||||||
import java.io.FileInputStream;
|
import android.database.sqlite.SQLiteOpenHelper;
|
||||||
import java.io.FileNotFoundException;
|
import android.util.Log;
|
||||||
import java.io.FileOutputStream;
|
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.crypto.DecryptingPartInputStream;
|
import org.thoughtcrime.securesms.crypto.DecryptingPartInputStream;
|
||||||
import org.thoughtcrime.securesms.crypto.EncryptingPartOutputStream;
|
import org.thoughtcrime.securesms.crypto.EncryptingPartOutputStream;
|
||||||
import org.thoughtcrime.securesms.crypto.MasterSecret;
|
import org.thoughtcrime.securesms.crypto.MasterSecret;
|
||||||
|
|
||||||
import ws.com.google.android.mms.pdu.PduPart;
|
import ws.com.google.android.mms.pdu.PduPart;
|
||||||
import android.content.Context;
|
|
||||||
import android.database.sqlite.SQLiteOpenHelper;
|
import java.io.File;
|
||||||
import android.util.Log;
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
|
||||||
public class EncryptingPartDatabase extends PartDatabase {
|
public class EncryptingPartDatabase extends PartDatabase {
|
||||||
|
|
||||||
private final MasterSecret masterSecret;
|
private final MasterSecret masterSecret;
|
||||||
|
|
||||||
public EncryptingPartDatabase(Context context, SQLiteOpenHelper databaseHelper, MasterSecret masterSecret) {
|
public EncryptingPartDatabase(Context context, SQLiteOpenHelper databaseHelper, MasterSecret masterSecret) {
|
||||||
super(context, databaseHelper);
|
super(context, databaseHelper);
|
||||||
this.masterSecret = masterSecret;
|
this.masterSecret = masterSecret;
|
||||||
@ -44,14 +45,14 @@ public class EncryptingPartDatabase extends PartDatabase {
|
|||||||
Log.w("EncryptingPartDatabase", "Getting part at: " + path.getAbsolutePath());
|
Log.w("EncryptingPartDatabase", "Getting part at: " + path.getAbsolutePath());
|
||||||
if (!part.getEncrypted())
|
if (!part.getEncrypted())
|
||||||
return super.getPartInputStream(path, part);
|
return super.getPartInputStream(path, part);
|
||||||
|
|
||||||
return new DecryptingPartInputStream(path, masterSecret);
|
return new DecryptingPartInputStream(path, masterSecret);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected FileOutputStream getPartOutputStream(File path, PduPart part) throws FileNotFoundException {
|
protected FileOutputStream getPartOutputStream(File path, PduPart part) throws FileNotFoundException {
|
||||||
Log.w("EncryptingPartDatabase", "Writing part to: " + path.getAbsolutePath());
|
Log.w("EncryptingPartDatabase", "Writing part to: " + path.getAbsolutePath());
|
||||||
part.setEncrypted(true);
|
part.setEncrypted(true);
|
||||||
return new EncryptingPartOutputStream(path, masterSecret);
|
return new EncryptingPartOutputStream(path, masterSecret);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Copyright (C) 2011 Whisper Systems
|
* Copyright (C) 2011 Whisper Systems
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
@ -10,66 +10,66 @@
|
|||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
* GNU General Public License for more details.
|
* GNU General Public License for more details.
|
||||||
*
|
*
|
||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
package org.thoughtcrime.securesms.database;
|
package org.thoughtcrime.securesms.database;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.database.sqlite.SQLiteOpenHelper;
|
||||||
|
import android.telephony.SmsMessage;
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.crypto.AsymmetricMasterCipher;
|
import org.thoughtcrime.securesms.crypto.AsymmetricMasterCipher;
|
||||||
import org.thoughtcrime.securesms.crypto.AsymmetricMasterSecret;
|
import org.thoughtcrime.securesms.crypto.AsymmetricMasterSecret;
|
||||||
import org.thoughtcrime.securesms.crypto.MasterCipher;
|
import org.thoughtcrime.securesms.crypto.MasterCipher;
|
||||||
import org.thoughtcrime.securesms.crypto.MasterSecret;
|
import org.thoughtcrime.securesms.crypto.MasterSecret;
|
||||||
import org.thoughtcrime.securesms.protocol.Prefix;
|
import org.thoughtcrime.securesms.protocol.Prefix;
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.database.sqlite.SQLiteOpenHelper;
|
|
||||||
import android.telephony.SmsMessage;
|
|
||||||
|
|
||||||
public class EncryptingSmsDatabase extends SmsDatabase {
|
public class EncryptingSmsDatabase extends SmsDatabase {
|
||||||
|
|
||||||
public EncryptingSmsDatabase(Context context, SQLiteOpenHelper databaseHelper) {
|
public EncryptingSmsDatabase(Context context, SQLiteOpenHelper databaseHelper) {
|
||||||
super(context, databaseHelper);
|
super(context, databaseHelper);
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getAsymmetricEncryptedBody(AsymmetricMasterSecret masterSecret, String body) {
|
private String getAsymmetricEncryptedBody(AsymmetricMasterSecret masterSecret, String body) {
|
||||||
AsymmetricMasterCipher bodyCipher = new AsymmetricMasterCipher(masterSecret);
|
AsymmetricMasterCipher bodyCipher = new AsymmetricMasterCipher(masterSecret);
|
||||||
return Prefix.ASYMMETRIC_LOCAL_ENCRYPT + bodyCipher.encryptBody(body);
|
return Prefix.ASYMMETRIC_LOCAL_ENCRYPT + bodyCipher.encryptBody(body);
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getEncryptedBody(MasterSecret masterSecret, String body) {
|
private String getEncryptedBody(MasterSecret masterSecret, String body) {
|
||||||
MasterCipher bodyCipher = new MasterCipher(masterSecret);
|
MasterCipher bodyCipher = new MasterCipher(masterSecret);
|
||||||
return Prefix.SYMMETRIC_ENCRYPT + bodyCipher.encryptBody(body);
|
return Prefix.SYMMETRIC_ENCRYPT + bodyCipher.encryptBody(body);
|
||||||
}
|
}
|
||||||
|
|
||||||
private long insertMessageSent(MasterSecret masterSecret, String address, long threadId, String body, long date, int type) {
|
private long insertMessageSent(MasterSecret masterSecret, String address, long threadId, String body, long date, int type) {
|
||||||
String encryptedBody = getEncryptedBody(masterSecret, body);
|
String encryptedBody = getEncryptedBody(masterSecret, body);
|
||||||
return insertMessageSent(address, threadId, encryptedBody, date, type);
|
return insertMessageSent(address, threadId, encryptedBody, date, type);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void updateSecureMessageBody(MasterSecret masterSecret, long messageId, String body) {
|
public void updateSecureMessageBody(MasterSecret masterSecret, long messageId, String body) {
|
||||||
String encryptedBody = getEncryptedBody(masterSecret, body);
|
String encryptedBody = getEncryptedBody(masterSecret, body);
|
||||||
updateMessageBodyAndType(messageId, encryptedBody, Types.SECURE_RECEIVED_TYPE);
|
updateMessageBodyAndType(messageId, encryptedBody, Types.SECURE_RECEIVED_TYPE);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void updateMessageBody(MasterSecret masterSecret, long messageId, String body) {
|
public void updateMessageBody(MasterSecret masterSecret, long messageId, String body) {
|
||||||
String encryptedBody = getEncryptedBody(masterSecret, body);
|
String encryptedBody = getEncryptedBody(masterSecret, body);
|
||||||
updateMessageBodyAndType(messageId, encryptedBody, Types.INBOX_TYPE);
|
updateMessageBodyAndType(messageId, encryptedBody, Types.INBOX_TYPE);
|
||||||
}
|
}
|
||||||
|
|
||||||
public long insertMessageSent(MasterSecret masterSecret, String address, long threadId, String body, long date) {
|
public long insertMessageSent(MasterSecret masterSecret, String address, long threadId, String body, long date) {
|
||||||
return insertMessageSent(masterSecret, address, threadId, body, date, Types.ENCRYPTED_OUTBOX_TYPE);
|
return insertMessageSent(masterSecret, address, threadId, body, date, Types.ENCRYPTED_OUTBOX_TYPE);
|
||||||
}
|
}
|
||||||
|
|
||||||
public long insertSecureMessageSent(MasterSecret masterSecret, String address, long threadId, String body, long date) {
|
public long insertSecureMessageSent(MasterSecret masterSecret, String address, long threadId, String body, long date) {
|
||||||
return insertMessageSent(masterSecret, address, threadId, body, date, Types.ENCRYPTING_TYPE);
|
return insertMessageSent(masterSecret, address, threadId, body, date, Types.ENCRYPTING_TYPE);
|
||||||
}
|
}
|
||||||
|
|
||||||
public long insertMessageReceived(MasterSecret masterSecret, SmsMessage message, String body) {
|
public long insertMessageReceived(MasterSecret masterSecret, SmsMessage message, String body) {
|
||||||
String encryptedBody = getEncryptedBody(masterSecret, body);
|
String encryptedBody = getEncryptedBody(masterSecret, body);
|
||||||
return insertMessageReceived(message, encryptedBody);
|
return insertMessageReceived(message, encryptedBody);
|
||||||
}
|
}
|
||||||
|
|
||||||
public long insertMessageReceived(AsymmetricMasterSecret masterSecret, SmsMessage message, String body) {
|
public long insertMessageReceived(AsymmetricMasterSecret masterSecret, SmsMessage message, String body) {
|
||||||
String encryptedBody = getAsymmetricEncryptedBody(masterSecret, body);
|
String encryptedBody = getAsymmetricEncryptedBody(masterSecret, body);
|
||||||
return insertSecureMessageReceived(message, encryptedBody);
|
return insertSecureMessageReceived(message, encryptedBody);
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Copyright (C) 2011 Whisper Systems
|
* Copyright (C) 2011 Whisper Systems
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
@ -10,7 +10,7 @@
|
|||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
* GNU General Public License for more details.
|
* GNU General Public License for more details.
|
||||||
*
|
*
|
||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
@ -21,7 +21,7 @@ import org.thoughtcrime.securesms.recipients.Recipient;
|
|||||||
import org.thoughtcrime.securesms.recipients.Recipients;
|
import org.thoughtcrime.securesms.recipients.Recipients;
|
||||||
|
|
||||||
public class MessageRecord {
|
public class MessageRecord {
|
||||||
|
|
||||||
private long id;
|
private long id;
|
||||||
private long threadId;
|
private long threadId;
|
||||||
private Recipient messageRecipient;
|
private Recipient messageRecipient;
|
||||||
@ -35,7 +35,7 @@ public class MessageRecord {
|
|||||||
private boolean keyExchange;
|
private boolean keyExchange;
|
||||||
private boolean processedKeyExchange;
|
private boolean processedKeyExchange;
|
||||||
private boolean staleKeyExchange;
|
private boolean staleKeyExchange;
|
||||||
|
|
||||||
public MessageRecord(MessageRecord copy) {
|
public MessageRecord(MessageRecord copy) {
|
||||||
this.id = copy.id;
|
this.id = copy.id;
|
||||||
this.threadId = copy.threadId;
|
this.threadId = copy.threadId;
|
||||||
@ -50,7 +50,7 @@ public class MessageRecord {
|
|||||||
this.keyExchange = copy.keyExchange;
|
this.keyExchange = copy.keyExchange;
|
||||||
this.processedKeyExchange = copy.processedKeyExchange;
|
this.processedKeyExchange = copy.processedKeyExchange;
|
||||||
}
|
}
|
||||||
|
|
||||||
public MessageRecord(long id, Recipients recipients, long date, long type, long threadId) {
|
public MessageRecord(long id, Recipients recipients, long date, long type, long threadId) {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.date = date;
|
this.date = date;
|
||||||
@ -58,7 +58,7 @@ public class MessageRecord {
|
|||||||
this.recipients = recipients;
|
this.recipients = recipients;
|
||||||
this.threadId = threadId;
|
this.threadId = threadId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public MessageRecord(long id, Recipients recipients, long date, long count, boolean read, long threadId) {
|
public MessageRecord(long id, Recipients recipients, long date, long count, boolean read, long threadId) {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.threadId = threadId;
|
this.threadId = threadId;
|
||||||
@ -67,115 +67,115 @@ public class MessageRecord {
|
|||||||
this.count = count;
|
this.count = count;
|
||||||
this.read = read;
|
this.read = read;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setOnConversationItem(ConversationItem item) {
|
public void setOnConversationItem(ConversationItem item) {
|
||||||
item.setMessageRecord(this);
|
item.setMessageRecord(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isMms() {
|
public boolean isMms() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getType() {
|
public long getType() {
|
||||||
return type;
|
return type;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setMessageRecipient(Recipient recipient) {
|
public void setMessageRecipient(Recipient recipient) {
|
||||||
this.messageRecipient = recipient;
|
this.messageRecipient = recipient;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Recipient getMessageRecipient() {
|
public Recipient getMessageRecipient() {
|
||||||
return this.messageRecipient;
|
return this.messageRecipient;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setEmphasis(boolean emphasis) {
|
public void setEmphasis(boolean emphasis) {
|
||||||
this.emphasis = emphasis;
|
this.emphasis = emphasis;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean getEmphasis() {
|
public boolean getEmphasis() {
|
||||||
return this.emphasis;
|
return this.emphasis;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setId(long id) {
|
public void setId(long id) {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setBody(String body) {
|
public void setBody(String body) {
|
||||||
this.body = body;
|
this.body = body;
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getThreadId() {
|
public long getThreadId() {
|
||||||
return threadId;
|
return threadId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getId() {
|
public long getId() {
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Recipients getRecipients() {
|
public Recipients getRecipients() {
|
||||||
return recipients;
|
return recipients;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getBody() {
|
public String getBody() {
|
||||||
return body;
|
return body;
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getDate() {
|
public long getDate() {
|
||||||
return date;
|
return date;
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getCount() {
|
public long getCount() {
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean getRead() {
|
public boolean getRead() {
|
||||||
return read;
|
return read;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isStaleKeyExchange() {
|
public boolean isStaleKeyExchange() {
|
||||||
return this.staleKeyExchange;
|
return this.staleKeyExchange;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setStaleKeyExchange(boolean staleKeyExchange) {
|
public void setStaleKeyExchange(boolean staleKeyExchange) {
|
||||||
this.staleKeyExchange = staleKeyExchange;
|
this.staleKeyExchange = staleKeyExchange;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isProcessedKeyExchange() {
|
public boolean isProcessedKeyExchange() {
|
||||||
return processedKeyExchange;
|
return processedKeyExchange;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setProcessedKeyExchange(boolean processedKeyExchange) {
|
public void setProcessedKeyExchange(boolean processedKeyExchange) {
|
||||||
this.processedKeyExchange = processedKeyExchange;
|
this.processedKeyExchange = processedKeyExchange;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isKeyExchange() {
|
public boolean isKeyExchange() {
|
||||||
return keyExchange || processedKeyExchange || staleKeyExchange;
|
return keyExchange || processedKeyExchange || staleKeyExchange;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setKeyExchange(boolean keyExchange) {
|
public void setKeyExchange(boolean keyExchange) {
|
||||||
this.keyExchange = keyExchange;
|
this.keyExchange = keyExchange;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isFailedDecryptType() {
|
public boolean isFailedDecryptType() {
|
||||||
return type == SmsDatabase.Types.FAILED_DECRYPT_TYPE;
|
return type == SmsDatabase.Types.FAILED_DECRYPT_TYPE;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isFailed() {
|
public boolean isFailed() {
|
||||||
return SmsDatabase.Types.isFailedMessageType(type);
|
return SmsDatabase.Types.isFailedMessageType(type);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isOutgoing() {
|
public boolean isOutgoing() {
|
||||||
return SmsDatabase.Types.isOutgoingMessageType(type);
|
return SmsDatabase.Types.isOutgoingMessageType(type);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isPending() {
|
public boolean isPending() {
|
||||||
return SmsDatabase.Types.isPendingMessageType(type);
|
return SmsDatabase.Types.isPendingMessageType(type);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isSecure() {
|
public boolean isSecure() {
|
||||||
return SmsDatabase.Types.isSecureType(type);
|
return SmsDatabase.Types.isSecureType(type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Copyright (C) 2011 Whisper Systems
|
* Copyright (C) 2011 Whisper Systems
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
@ -10,17 +10,12 @@
|
|||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
* GNU General Public License for more details.
|
* GNU General Public License for more details.
|
||||||
*
|
*
|
||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
package org.thoughtcrime.securesms.database;
|
package org.thoughtcrime.securesms.database;
|
||||||
|
|
||||||
import java.io.UnsupportedEncodingException;
|
|
||||||
|
|
||||||
import ws.com.google.android.mms.pdu.CharacterSets;
|
|
||||||
import ws.com.google.android.mms.pdu.EncodedStringValue;
|
|
||||||
import ws.com.google.android.mms.pdu.PduHeaders;
|
|
||||||
import android.content.ContentValues;
|
import android.content.ContentValues;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
@ -28,17 +23,23 @@ import android.database.sqlite.SQLiteDatabase;
|
|||||||
import android.database.sqlite.SQLiteOpenHelper;
|
import android.database.sqlite.SQLiteOpenHelper;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
|
import ws.com.google.android.mms.pdu.CharacterSets;
|
||||||
|
import ws.com.google.android.mms.pdu.EncodedStringValue;
|
||||||
|
import ws.com.google.android.mms.pdu.PduHeaders;
|
||||||
|
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
|
|
||||||
public class MmsAddressDatabase extends Database {
|
public class MmsAddressDatabase extends Database {
|
||||||
|
|
||||||
private static final String TABLE_NAME = "mms_addresses";
|
private static final String TABLE_NAME = "mms_addresses";
|
||||||
private static final String ID = "_id";
|
private static final String ID = "_id";
|
||||||
private static final String MMS_ID = "mms_id";
|
private static final String MMS_ID = "mms_id";
|
||||||
private static final String TYPE = "type";
|
private static final String TYPE = "type";
|
||||||
private static final String ADDRESS = "address";
|
private static final String ADDRESS = "address";
|
||||||
private static final String ADDRESS_CHARSET = "address_charset";
|
private static final String ADDRESS_CHARSET = "address_charset";
|
||||||
|
|
||||||
public static final String CREATE_TABLE = "CREATE TABLE " + TABLE_NAME + " (" + ID + " INTEGER PRIMARY KEY, " +
|
public static final String CREATE_TABLE = "CREATE TABLE " + TABLE_NAME + " (" + ID + " INTEGER PRIMARY KEY, " +
|
||||||
MMS_ID + " INTEGER, " + TYPE + " INTEGER, " + ADDRESS + " TEXT, " +
|
MMS_ID + " INTEGER, " + TYPE + " INTEGER, " + ADDRESS + " TEXT, " +
|
||||||
ADDRESS_CHARSET + " INTEGER);";
|
ADDRESS_CHARSET + " INTEGER);";
|
||||||
|
|
||||||
public MmsAddressDatabase(Context context, SQLiteOpenHelper databaseHelper) {
|
public MmsAddressDatabase(Context context, SQLiteOpenHelper databaseHelper) {
|
||||||
@ -56,7 +57,7 @@ public class MmsAddressDatabase extends Database {
|
|||||||
database.insert(TABLE_NAME, null, contentValues);
|
database.insert(TABLE_NAME, null, contentValues);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void insertAddress(long messageId, int type, EncodedStringValue[] addresses) {
|
private void insertAddress(long messageId, int type, EncodedStringValue[] addresses) {
|
||||||
if (addresses != null) {
|
if (addresses != null) {
|
||||||
for (int i=0;i<addresses.length;i++) {
|
for (int i=0;i<addresses.length;i++) {
|
||||||
@ -64,53 +65,53 @@ public class MmsAddressDatabase extends Database {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addAddress(Cursor cursor, PduHeaders headers) {
|
private void addAddress(Cursor cursor, PduHeaders headers) {
|
||||||
long type = cursor.getLong(cursor.getColumnIndexOrThrow(TYPE));
|
long type = cursor.getLong(cursor.getColumnIndexOrThrow(TYPE));
|
||||||
String address = cursor.getString(cursor.getColumnIndexOrThrow(ADDRESS));
|
String address = cursor.getString(cursor.getColumnIndexOrThrow(ADDRESS));
|
||||||
long charset = cursor.getLong(cursor.getColumnIndexOrThrow(ADDRESS_CHARSET));
|
long charset = cursor.getLong(cursor.getColumnIndexOrThrow(ADDRESS_CHARSET));
|
||||||
|
|
||||||
EncodedStringValue encodedAddress = new EncodedStringValue((int)charset, getBytes(address));
|
EncodedStringValue encodedAddress = new EncodedStringValue((int)charset, getBytes(address));
|
||||||
|
|
||||||
if (type == PduHeaders.FROM)
|
if (type == PduHeaders.FROM)
|
||||||
headers.setEncodedStringValue(encodedAddress, PduHeaders.FROM);
|
headers.setEncodedStringValue(encodedAddress, PduHeaders.FROM);
|
||||||
else
|
else
|
||||||
headers.appendEncodedStringValue(encodedAddress, (int)type);
|
headers.appendEncodedStringValue(encodedAddress, (int)type);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void insertAddressesForId(long messageId, PduHeaders headers) {
|
public void insertAddressesForId(long messageId, PduHeaders headers) {
|
||||||
insertAddress(messageId, PduHeaders.FROM, headers.getEncodedStringValue(PduHeaders.FROM));
|
insertAddress(messageId, PduHeaders.FROM, headers.getEncodedStringValue(PduHeaders.FROM));
|
||||||
insertAddress(messageId, PduHeaders.TO, headers.getEncodedStringValues(PduHeaders.TO));
|
insertAddress(messageId, PduHeaders.TO, headers.getEncodedStringValues(PduHeaders.TO));
|
||||||
insertAddress(messageId, PduHeaders.CC, headers.getEncodedStringValues(PduHeaders.CC));
|
insertAddress(messageId, PduHeaders.CC, headers.getEncodedStringValues(PduHeaders.CC));
|
||||||
insertAddress(messageId, PduHeaders.BCC, headers.getEncodedStringValues(PduHeaders.BCC));
|
insertAddress(messageId, PduHeaders.BCC, headers.getEncodedStringValues(PduHeaders.BCC));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void getAddressesForId(long messageId, PduHeaders headers) {
|
public void getAddressesForId(long messageId, PduHeaders headers) {
|
||||||
SQLiteDatabase database = databaseHelper.getReadableDatabase();
|
SQLiteDatabase database = databaseHelper.getReadableDatabase();
|
||||||
Cursor cursor = null;
|
Cursor cursor = null;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
cursor = database.query(TABLE_NAME, null, MMS_ID + " = ?", new String[] {messageId+""}, null, null, null);
|
cursor = database.query(TABLE_NAME, null, MMS_ID + " = ?", new String[] {messageId+""}, null, null, null);
|
||||||
|
|
||||||
while (cursor != null && cursor.moveToNext()) {
|
while (cursor != null && cursor.moveToNext()) {
|
||||||
addAddress(cursor, headers);
|
addAddress(cursor, headers);
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
if (cursor != null)
|
if (cursor != null)
|
||||||
cursor.close();
|
cursor.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void deleteAddressesForId(long messageId) {
|
public void deleteAddressesForId(long messageId) {
|
||||||
SQLiteDatabase database = databaseHelper.getWritableDatabase();
|
SQLiteDatabase database = databaseHelper.getWritableDatabase();
|
||||||
database.delete(TABLE_NAME, MMS_ID + " = ?", new String[] {messageId+""});
|
database.delete(TABLE_NAME, MMS_ID + " = ?", new String[] {messageId+""});
|
||||||
}
|
}
|
||||||
|
|
||||||
public void deleteAllAddresses() {
|
public void deleteAllAddresses() {
|
||||||
SQLiteDatabase database = databaseHelper.getWritableDatabase();
|
SQLiteDatabase database = databaseHelper.getWritableDatabase();
|
||||||
database.delete(TABLE_NAME, null, null);
|
database.delete(TABLE_NAME, null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
private byte[] getBytes(String data) {
|
private byte[] getBytes(String data) {
|
||||||
try {
|
try {
|
||||||
return data.getBytes(CharacterSets.MIMENAME_ISO_8859_1);
|
return data.getBytes(CharacterSets.MIMENAME_ISO_8859_1);
|
||||||
@ -120,7 +121,6 @@ public class MmsAddressDatabase extends Database {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private String toIsoString(byte[] bytes) {
|
private String toIsoString(byte[] bytes) {
|
||||||
try {
|
try {
|
||||||
return new String(bytes, CharacterSets.MIMENAME_ISO_8859_1);
|
return new String(bytes, CharacterSets.MIMENAME_ISO_8859_1);
|
||||||
@ -129,7 +129,4 @@ public class MmsAddressDatabase extends Database {
|
|||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -268,11 +268,11 @@ public class MmsDatabase extends Database {
|
|||||||
|
|
||||||
while (cursor.moveToNext()) {
|
while (cursor.moveToNext()) {
|
||||||
long messageId = cursor.getLong(cursor.getColumnIndexOrThrow(ID));
|
long messageId = cursor.getLong(cursor.getColumnIndexOrThrow(ID));
|
||||||
long outboxType = cursor.getLong(cursor.getColumnIndexOrThrow(MESSAGE_BOX));
|
long outboxType = cursor.getLong(cursor.getColumnIndexOrThrow(MESSAGE_BOX));
|
||||||
PduHeaders headers = getHeadersFromCursor(cursor);
|
PduHeaders headers = getHeadersFromCursor(cursor);
|
||||||
addr.getAddressesForId(messageId, headers);
|
addr.getAddressesForId(messageId, headers);
|
||||||
PduBody body = parts.getParts(messageId, true);
|
PduBody body = parts.getParts(messageId, true);
|
||||||
requests[i++] = new SendReq(headers, body, messageId, outboxType);
|
requests[i++] = new SendReq(headers, body, messageId, outboxType);
|
||||||
}
|
}
|
||||||
|
|
||||||
return requests;
|
return requests;
|
||||||
@ -342,7 +342,7 @@ public class MmsDatabase extends Database {
|
|||||||
ContentValues contentValues = getContentValuesFromHeader(headers);
|
ContentValues contentValues = getContentValuesFromHeader(headers);
|
||||||
|
|
||||||
if (!isSecure) contentValues.put(MESSAGE_BOX, Types.MESSAGE_BOX_OUTBOX);
|
if (!isSecure) contentValues.put(MESSAGE_BOX, Types.MESSAGE_BOX_OUTBOX);
|
||||||
else contentValues.put(MESSAGE_BOX, Types.MESSAGE_BOX_SECURE_OUTBOX);
|
else contentValues.put(MESSAGE_BOX, Types.MESSAGE_BOX_SECURE_OUTBOX);
|
||||||
|
|
||||||
contentValues.put(THREAD_ID, threadId);
|
contentValues.put(THREAD_ID, threadId);
|
||||||
contentValues.put(READ, 1);
|
contentValues.put(READ, 1);
|
||||||
@ -412,7 +412,6 @@ public class MmsDatabase extends Database {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void deleteAllThreads() {
|
public void deleteAllThreads() {
|
||||||
DatabaseFactory.getPartDatabase(context).deleteAllParts();
|
DatabaseFactory.getPartDatabase(context).deleteAllParts();
|
||||||
DatabaseFactory.getMmsAddressDatabase(context).deleteAllAddresses();
|
DatabaseFactory.getMmsAddressDatabase(context).deleteAllAddresses();
|
||||||
@ -448,7 +447,7 @@ public class MmsDatabase extends Database {
|
|||||||
return headers;
|
return headers;
|
||||||
} finally {
|
} finally {
|
||||||
if (cursor != null)
|
if (cursor != null)
|
||||||
cursor.close();
|
cursor.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -535,9 +534,8 @@ public class MmsDatabase extends Database {
|
|||||||
public static final int DOWNLOAD_INITIALIZED = 1;
|
public static final int DOWNLOAD_INITIALIZED = 1;
|
||||||
public static final int DOWNLOAD_NO_CONNECTIVITY = 2;
|
public static final int DOWNLOAD_NO_CONNECTIVITY = 2;
|
||||||
public static final int DOWNLOAD_CONNECTING = 3;
|
public static final int DOWNLOAD_CONNECTING = 3;
|
||||||
public static final int DOWNLOAD_SOFT_FAILURE = 4;
|
public static final int DOWNLOAD_SOFT_FAILURE = 4;
|
||||||
public static final int DOWNLOAD_HARD_FAILURE = 5;
|
public static final int DOWNLOAD_HARD_FAILURE = 5;
|
||||||
|
|
||||||
|
|
||||||
public static boolean isSecureMmsBox(long mailbox) {
|
public static boolean isSecureMmsBox(long mailbox) {
|
||||||
return mailbox == Types.MESSAGE_BOX_SECURE_OUTBOX || mailbox == Types.MESSAGE_BOX_SECURE_SENT || mailbox == Types.MESSAGE_BOX_SECURE_INBOX;
|
return mailbox == Types.MESSAGE_BOX_SECURE_OUTBOX || mailbox == Types.MESSAGE_BOX_SECURE_SENT || mailbox == Types.MESSAGE_BOX_SECURE_INBOX;
|
||||||
@ -574,9 +572,5 @@ public class MmsDatabase extends Database {
|
|||||||
public static boolean isHardError(int status) {
|
public static boolean isHardError(int status) {
|
||||||
return status == DOWNLOAD_HARD_FAILURE;
|
return status == DOWNLOAD_HARD_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Copyright (C) 2011 Whisper Systems
|
* Copyright (C) 2011 Whisper Systems
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
@ -10,15 +10,12 @@
|
|||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
* GNU General Public License for more details.
|
* GNU General Public License for more details.
|
||||||
*
|
*
|
||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
package org.thoughtcrime.securesms.database;
|
package org.thoughtcrime.securesms.database;
|
||||||
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
import android.database.sqlite.SQLiteDatabase;
|
import android.database.sqlite.SQLiteDatabase;
|
||||||
@ -26,54 +23,57 @@ import android.database.sqlite.SQLiteOpenHelper;
|
|||||||
import android.database.sqlite.SQLiteQueryBuilder;
|
import android.database.sqlite.SQLiteQueryBuilder;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
public class MmsSmsDatabase extends Database {
|
public class MmsSmsDatabase extends Database {
|
||||||
|
|
||||||
public static final String TRANSPORT = "transport_type";
|
public static final String TRANSPORT = "transport_type";
|
||||||
|
|
||||||
public MmsSmsDatabase(Context context, SQLiteOpenHelper databaseHelper) {
|
public MmsSmsDatabase(Context context, SQLiteOpenHelper databaseHelper) {
|
||||||
super(context, databaseHelper);
|
super(context, databaseHelper);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Cursor getConversation(long threadId) {
|
public Cursor getConversation(long threadId) {
|
||||||
String[] projection = {"_id", "body", "type", "address", "subject", "normalized_date AS date", "m_type", "msg_box", "transport_type"};
|
String[] projection = {"_id", "body", "type", "address", "subject", "normalized_date AS date", "m_type", "msg_box", "transport_type"};
|
||||||
String order = "normalized_date ASC";
|
String order = "normalized_date ASC";
|
||||||
String selection = "thread_id = " + threadId;
|
String selection = "thread_id = " + threadId;
|
||||||
|
|
||||||
Cursor cursor = queryTables(projection, selection, order, null);
|
Cursor cursor = queryTables(projection, selection, order, null);
|
||||||
setNotifyConverationListeners(cursor, threadId);
|
setNotifyConverationListeners(cursor, threadId);
|
||||||
|
|
||||||
return cursor;
|
return cursor;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Cursor getConversationSnippet(long threadId) {
|
public Cursor getConversationSnippet(long threadId) {
|
||||||
String[] projection = {"_id", "body", "type", "address", "subject", "normalized_date AS date", "m_type", "msg_box", "transport_type"};
|
String[] projection = {"_id", "body", "type", "address", "subject", "normalized_date AS date", "m_type", "msg_box", "transport_type"};
|
||||||
String order = "normalized_date DESC";
|
String order = "normalized_date DESC";
|
||||||
String selection = "thread_id = " + threadId;
|
String selection = "thread_id = " + threadId;
|
||||||
|
|
||||||
Cursor cursor = queryTables(projection, selection, order, "1");
|
Cursor cursor = queryTables(projection, selection, order, "1");
|
||||||
return cursor;
|
return cursor;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Cursor getUnread() {
|
public Cursor getUnread() {
|
||||||
String[] projection = {"_id", "body", "read", "type", "address", "subject", "thread_id", "normalized_date AS date", "m_type", "msg_box", "transport_type"};
|
String[] projection = {"_id", "body", "read", "type", "address", "subject", "thread_id", "normalized_date AS date", "m_type", "msg_box", "transport_type"};
|
||||||
String order = "normalized_date ASC";
|
String order = "normalized_date ASC";
|
||||||
String selection = "read = 0";
|
String selection = "read = 0";
|
||||||
|
|
||||||
Cursor cursor = queryTables(projection, selection, order, null);
|
Cursor cursor = queryTables(projection, selection, order, null);
|
||||||
return cursor;
|
return cursor;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getConversationCount(long threadId) {
|
public int getConversationCount(long threadId) {
|
||||||
int count = DatabaseFactory.getSmsDatabase(context).getMessageCountForThread(threadId);
|
int count = DatabaseFactory.getSmsDatabase(context).getMessageCountForThread(threadId);
|
||||||
count += DatabaseFactory.getMmsDatabase(context).getMessageCountForThread(threadId);
|
count += DatabaseFactory.getMmsDatabase(context).getMessageCountForThread(threadId);
|
||||||
|
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Cursor queryTables(String[] projection, String selection, String order, String limit) {
|
private Cursor queryTables(String[] projection, String selection, String order, String limit) {
|
||||||
String[] mmsProjection = {"date * 1000 AS normalized_date", "_id", "body", "read", "thread_id", "type", "address", "subject", "date", "m_type", "msg_box", "transport_type"};
|
String[] mmsProjection = {"date * 1000 AS normalized_date", "_id", "body", "read", "thread_id", "type", "address", "subject", "date", "m_type", "msg_box", "transport_type"};
|
||||||
String[] smsProjection = {"date * 1 AS normalized_date", "_id", "body", "read", "thread_id", "type", "address", "subject", "date", "m_type", "msg_box", "transport_type"};
|
String[] smsProjection = {"date * 1 AS normalized_date", "_id", "body", "read", "thread_id", "type", "address", "subject", "date", "m_type", "msg_box", "transport_type"};
|
||||||
|
|
||||||
SQLiteQueryBuilder mmsQueryBuilder = new SQLiteQueryBuilder();
|
SQLiteQueryBuilder mmsQueryBuilder = new SQLiteQueryBuilder();
|
||||||
SQLiteQueryBuilder smsQueryBuilder = new SQLiteQueryBuilder();
|
SQLiteQueryBuilder smsQueryBuilder = new SQLiteQueryBuilder();
|
||||||
|
|
||||||
@ -82,7 +82,7 @@ public class MmsSmsDatabase extends Database {
|
|||||||
|
|
||||||
mmsQueryBuilder.setTables(MmsDatabase.TABLE_NAME);
|
mmsQueryBuilder.setTables(MmsDatabase.TABLE_NAME);
|
||||||
smsQueryBuilder.setTables(SmsDatabase.TABLE_NAME);
|
smsQueryBuilder.setTables(SmsDatabase.TABLE_NAME);
|
||||||
|
|
||||||
Set<String> mmsColumnsPresent = new HashSet<String>();
|
Set<String> mmsColumnsPresent = new HashSet<String>();
|
||||||
mmsColumnsPresent.add("_id");
|
mmsColumnsPresent.add("_id");
|
||||||
mmsColumnsPresent.add("m_type");
|
mmsColumnsPresent.add("m_type");
|
||||||
@ -90,7 +90,7 @@ public class MmsSmsDatabase extends Database {
|
|||||||
mmsColumnsPresent.add("date");
|
mmsColumnsPresent.add("date");
|
||||||
mmsColumnsPresent.add("read");
|
mmsColumnsPresent.add("read");
|
||||||
mmsColumnsPresent.add("thread_id");
|
mmsColumnsPresent.add("thread_id");
|
||||||
|
|
||||||
Set<String> smsColumnsPresent = new HashSet<String>();
|
Set<String> smsColumnsPresent = new HashSet<String>();
|
||||||
smsColumnsPresent.add("_id");
|
smsColumnsPresent.add("_id");
|
||||||
smsColumnsPresent.add("body");
|
smsColumnsPresent.add("body");
|
||||||
@ -104,19 +104,19 @@ public class MmsSmsDatabase extends Database {
|
|||||||
String mmsSubQuery = mmsQueryBuilder.buildUnionSubQuery("transport_type", mmsProjection, mmsColumnsPresent, 0, "mms", selection, null, null, null);
|
String mmsSubQuery = mmsQueryBuilder.buildUnionSubQuery("transport_type", mmsProjection, mmsColumnsPresent, 0, "mms", selection, null, null, null);
|
||||||
String smsSubQuery = smsQueryBuilder.buildUnionSubQuery("transport_type", smsProjection, smsColumnsPresent, 0, "sms", selection, null, null, null);
|
String smsSubQuery = smsQueryBuilder.buildUnionSubQuery("transport_type", smsProjection, smsColumnsPresent, 0, "sms", selection, null, null, null);
|
||||||
|
|
||||||
SQLiteQueryBuilder unionQueryBuilder = new SQLiteQueryBuilder();
|
SQLiteQueryBuilder unionQueryBuilder = new SQLiteQueryBuilder();
|
||||||
String unionQuery = unionQueryBuilder.buildUnionQuery(new String[] {smsSubQuery, mmsSubQuery}, order, null);
|
String unionQuery = unionQueryBuilder.buildUnionQuery(new String[] {smsSubQuery, mmsSubQuery}, order, null);
|
||||||
|
|
||||||
SQLiteQueryBuilder outerQueryBuilder = new SQLiteQueryBuilder();
|
SQLiteQueryBuilder outerQueryBuilder = new SQLiteQueryBuilder();
|
||||||
outerQueryBuilder.setTables("(" + unionQuery + ")");
|
outerQueryBuilder.setTables("(" + unionQuery + ")");
|
||||||
|
|
||||||
String query = outerQueryBuilder.buildQuery(projection, null, null, null, null, null, limit);
|
String query = outerQueryBuilder.buildQuery(projection, null, null, null, null, null, limit);
|
||||||
|
|
||||||
Log.w("MmsSmsDatabase", "Executing query: " + query);
|
Log.w("MmsSmsDatabase", "Executing query: " + query);
|
||||||
SQLiteDatabase db = databaseHelper.getReadableDatabase();
|
SQLiteDatabase db = databaseHelper.getReadableDatabase();
|
||||||
Cursor cursor = db.rawQuery(query, null);
|
Cursor cursor = db.rawQuery(query, null);
|
||||||
return cursor;
|
return cursor;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Copyright (C) 2011 Whisper Systems
|
* Copyright (C) 2011 Whisper Systems
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
@ -10,12 +10,28 @@
|
|||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
* GNU General Public License for more details.
|
* GNU General Public License for more details.
|
||||||
*
|
*
|
||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
package org.thoughtcrime.securesms.database;
|
package org.thoughtcrime.securesms.database;
|
||||||
|
|
||||||
|
import android.content.ContentUris;
|
||||||
|
import android.content.ContentValues;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.database.Cursor;
|
||||||
|
import android.database.sqlite.SQLiteDatabase;
|
||||||
|
import android.database.sqlite.SQLiteOpenHelper;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import org.thoughtcrime.securesms.providers.PartProvider;
|
||||||
|
|
||||||
|
import ws.com.google.android.mms.ContentType;
|
||||||
|
import ws.com.google.android.mms.MmsException;
|
||||||
|
import ws.com.google.android.mms.pdu.CharacterSets;
|
||||||
|
import ws.com.google.android.mms.pdu.PduBody;
|
||||||
|
import ws.com.google.android.mms.pdu.PduPart;
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
@ -25,21 +41,6 @@ import java.io.IOException;
|
|||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.UnsupportedEncodingException;
|
import java.io.UnsupportedEncodingException;
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.providers.PartProvider;
|
|
||||||
|
|
||||||
import ws.com.google.android.mms.ContentType;
|
|
||||||
import ws.com.google.android.mms.MmsException;
|
|
||||||
import ws.com.google.android.mms.pdu.CharacterSets;
|
|
||||||
import ws.com.google.android.mms.pdu.PduBody;
|
|
||||||
import ws.com.google.android.mms.pdu.PduPart;
|
|
||||||
import android.content.ContentUris;
|
|
||||||
import android.content.ContentValues;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.database.Cursor;
|
|
||||||
import android.database.sqlite.SQLiteDatabase;
|
|
||||||
import android.database.sqlite.SQLiteOpenHelper;
|
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
public class PartDatabase extends Database {
|
public class PartDatabase extends Database {
|
||||||
|
|
||||||
private static final String TABLE_NAME = "part";
|
private static final String TABLE_NAME = "part";
|
||||||
@ -61,77 +62,77 @@ public class PartDatabase extends Database {
|
|||||||
public static final String CREATE_TABLE = "CREATE TABLE " + TABLE_NAME + " (" + ID + " INTEGER PRIMARY KEY, " +
|
public static final String CREATE_TABLE = "CREATE TABLE " + TABLE_NAME + " (" + ID + " INTEGER PRIMARY KEY, " +
|
||||||
MMS_ID + " INTEGER, " + SEQUENCE + " INTEGER DEFAULT 0, " +
|
MMS_ID + " INTEGER, " + SEQUENCE + " INTEGER DEFAULT 0, " +
|
||||||
CONTENT_TYPE + " TEXT, " + NAME + " TEXT, " + CHARSET + " INTEGER, " +
|
CONTENT_TYPE + " TEXT, " + NAME + " TEXT, " + CHARSET + " INTEGER, " +
|
||||||
CONTENT_DISPOSITION + " TEXT, " + FILENAME + " TEXT, " + CONTENT_ID + " TEXT, " +
|
CONTENT_DISPOSITION + " TEXT, " + FILENAME + " TEXT, " + CONTENT_ID + " TEXT, " +
|
||||||
CONTENT_LOCATION + " TEXT, " + CONTENT_TYPE_START + " INTEGER, " +
|
CONTENT_LOCATION + " TEXT, " + CONTENT_TYPE_START + " INTEGER, " +
|
||||||
CONTENT_TYPE_TYPE + " TEXT, " + ENCRYPTED + " INTEGER, " + DATA + " TEXT);";
|
CONTENT_TYPE_TYPE + " TEXT, " + ENCRYPTED + " INTEGER, " + DATA + " TEXT);";
|
||||||
|
|
||||||
public PartDatabase(Context context, SQLiteOpenHelper databaseHelper) {
|
public PartDatabase(Context context, SQLiteOpenHelper databaseHelper) {
|
||||||
super(context, databaseHelper);
|
super(context, databaseHelper);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void getPartValues(PduPart part, Cursor cursor) {
|
private void getPartValues(PduPart part, Cursor cursor) {
|
||||||
int charsetColumn = cursor.getColumnIndexOrThrow(CHARSET);
|
int charsetColumn = cursor.getColumnIndexOrThrow(CHARSET);
|
||||||
|
|
||||||
if (!cursor.isNull(charsetColumn))
|
if (!cursor.isNull(charsetColumn))
|
||||||
part.setCharset(cursor.getInt(charsetColumn));
|
part.setCharset(cursor.getInt(charsetColumn));
|
||||||
|
|
||||||
int contentTypeColumn = cursor.getColumnIndexOrThrow(CONTENT_TYPE);
|
int contentTypeColumn = cursor.getColumnIndexOrThrow(CONTENT_TYPE);
|
||||||
|
|
||||||
if (!cursor.isNull(contentTypeColumn))
|
if (!cursor.isNull(contentTypeColumn))
|
||||||
part.setContentType(getBytes(cursor.getString(contentTypeColumn)));
|
part.setContentType(getBytes(cursor.getString(contentTypeColumn)));
|
||||||
|
|
||||||
int nameColumn = cursor.getColumnIndexOrThrow(NAME);
|
int nameColumn = cursor.getColumnIndexOrThrow(NAME);
|
||||||
|
|
||||||
if (!cursor.isNull(nameColumn))
|
if (!cursor.isNull(nameColumn))
|
||||||
part.setName(getBytes(cursor.getString(nameColumn)));
|
part.setName(getBytes(cursor.getString(nameColumn)));
|
||||||
|
|
||||||
int fileNameColumn = cursor.getColumnIndexOrThrow(FILENAME);
|
int fileNameColumn = cursor.getColumnIndexOrThrow(FILENAME);
|
||||||
|
|
||||||
if (!cursor.isNull(fileNameColumn))
|
if (!cursor.isNull(fileNameColumn))
|
||||||
part.setFilename(getBytes(cursor.getString(fileNameColumn)));
|
part.setFilename(getBytes(cursor.getString(fileNameColumn)));
|
||||||
|
|
||||||
int contentDispositionColumn = cursor.getColumnIndexOrThrow(CONTENT_DISPOSITION);
|
int contentDispositionColumn = cursor.getColumnIndexOrThrow(CONTENT_DISPOSITION);
|
||||||
|
|
||||||
if (!cursor.isNull(contentDispositionColumn))
|
if (!cursor.isNull(contentDispositionColumn))
|
||||||
part.setContentDisposition(getBytes(cursor.getString(contentDispositionColumn)));
|
part.setContentDisposition(getBytes(cursor.getString(contentDispositionColumn)));
|
||||||
|
|
||||||
int contentIdColumn = cursor.getColumnIndexOrThrow(CONTENT_ID);
|
int contentIdColumn = cursor.getColumnIndexOrThrow(CONTENT_ID);
|
||||||
|
|
||||||
if (!cursor.isNull(contentIdColumn))
|
if (!cursor.isNull(contentIdColumn))
|
||||||
part.setContentId(getBytes(cursor.getString(contentIdColumn)));
|
part.setContentId(getBytes(cursor.getString(contentIdColumn)));
|
||||||
|
|
||||||
int contentLocationColumn = cursor.getColumnIndexOrThrow(CONTENT_LOCATION);
|
int contentLocationColumn = cursor.getColumnIndexOrThrow(CONTENT_LOCATION);
|
||||||
|
|
||||||
if (!cursor.isNull(contentLocationColumn))
|
if (!cursor.isNull(contentLocationColumn))
|
||||||
part.setContentLocation(getBytes(cursor.getString(contentLocationColumn)));
|
part.setContentLocation(getBytes(cursor.getString(contentLocationColumn)));
|
||||||
|
|
||||||
int encryptedColumn = cursor.getColumnIndexOrThrow(ENCRYPTED);
|
int encryptedColumn = cursor.getColumnIndexOrThrow(ENCRYPTED);
|
||||||
|
|
||||||
if (!cursor.isNull(encryptedColumn))
|
if (!cursor.isNull(encryptedColumn))
|
||||||
part.setEncrypted(cursor.getInt(encryptedColumn) == 1);
|
part.setEncrypted(cursor.getInt(encryptedColumn) == 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private ContentValues getContentValuesForPart(PduPart part) throws MmsException {
|
private ContentValues getContentValuesForPart(PduPart part) throws MmsException {
|
||||||
ContentValues contentValues = new ContentValues();
|
ContentValues contentValues = new ContentValues();
|
||||||
|
|
||||||
if (part.getCharset() != 0 ) {
|
if (part.getCharset() != 0 ) {
|
||||||
contentValues.put(CHARSET, part.getCharset());
|
contentValues.put(CHARSET, part.getCharset());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (part.getContentType() != null) {
|
if (part.getContentType() != null) {
|
||||||
contentValues.put(CONTENT_TYPE, toIsoString(part.getContentType()));
|
contentValues.put(CONTENT_TYPE, toIsoString(part.getContentType()));
|
||||||
|
|
||||||
if (toIsoString(part.getContentType()).equals(ContentType.APP_SMIL))
|
if (toIsoString(part.getContentType()).equals(ContentType.APP_SMIL))
|
||||||
contentValues.put(SEQUENCE, -1);
|
contentValues.put(SEQUENCE, -1);
|
||||||
} else {
|
} else {
|
||||||
throw new MmsException("There is no content type for this part.");
|
throw new MmsException("There is no content type for this part.");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (part.getName() != null) {
|
if (part.getName() != null) {
|
||||||
contentValues.put(NAME, new String(part.getName()));
|
contentValues.put(NAME, new String(part.getName()));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (part.getFilename() != null) {
|
if (part.getFilename() != null) {
|
||||||
contentValues.put(FILENAME, new String(part.getFilename()));
|
contentValues.put(FILENAME, new String(part.getFilename()));
|
||||||
}
|
}
|
||||||
@ -147,33 +148,33 @@ public class PartDatabase extends Database {
|
|||||||
if (part.getContentLocation() != null) {
|
if (part.getContentLocation() != null) {
|
||||||
contentValues.put(CONTENT_LOCATION, toIsoString(part.getContentLocation()));
|
contentValues.put(CONTENT_LOCATION, toIsoString(part.getContentLocation()));
|
||||||
}
|
}
|
||||||
|
|
||||||
contentValues.put(ENCRYPTED, part.getEncrypted() ? 1 : 0);
|
contentValues.put(ENCRYPTED, part.getEncrypted() ? 1 : 0);
|
||||||
|
|
||||||
return contentValues;
|
return contentValues;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected FileInputStream getPartInputStream(File file, PduPart part) throws FileNotFoundException {
|
protected FileInputStream getPartInputStream(File file, PduPart part) throws FileNotFoundException {
|
||||||
Log.w("PartDatabase", "Reading non-encrypted part from: " + file.getAbsolutePath());
|
Log.w("PartDatabase", "Reading non-encrypted part from: " + file.getAbsolutePath());
|
||||||
return new FileInputStream(file);
|
return new FileInputStream(file);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected FileOutputStream getPartOutputStream(File file, PduPart part) throws FileNotFoundException {
|
protected FileOutputStream getPartOutputStream(File file, PduPart part) throws FileNotFoundException {
|
||||||
Log.w("PartDatabase", "Writing non-encrypted part to: " + file.getAbsolutePath());
|
Log.w("PartDatabase", "Writing non-encrypted part to: " + file.getAbsolutePath());
|
||||||
return new FileOutputStream(file);
|
return new FileOutputStream(file);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void readPartData(PduPart part, String filename) {
|
private void readPartData(PduPart part, String filename) {
|
||||||
try {
|
try {
|
||||||
File dataFile = new File(filename);
|
File dataFile = new File(filename);
|
||||||
FileInputStream fin = getPartInputStream(dataFile, part);
|
FileInputStream fin = getPartInputStream(dataFile, part);
|
||||||
ByteArrayOutputStream baos = new ByteArrayOutputStream((int)dataFile.length());
|
ByteArrayOutputStream baos = new ByteArrayOutputStream((int)dataFile.length());
|
||||||
byte[] buffer = new byte[512];
|
byte[] buffer = new byte[512];
|
||||||
int read;
|
int read;
|
||||||
|
|
||||||
while ((read = fin.read(buffer)) != -1)
|
while ((read = fin.read(buffer)) != -1)
|
||||||
baos.write(buffer, 0, read);
|
baos.write(buffer, 0, read);
|
||||||
|
|
||||||
part.setData(baos.toByteArray());
|
part.setData(baos.toByteArray());
|
||||||
fin.close();
|
fin.close();
|
||||||
} catch (IOException ioe) {
|
} catch (IOException ioe) {
|
||||||
@ -181,7 +182,7 @@ public class PartDatabase extends Database {
|
|||||||
part.setData(null);
|
part.setData(null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private File writePartData(PduPart part) throws MmsException {
|
private File writePartData(PduPart part) throws MmsException {
|
||||||
try {
|
try {
|
||||||
File partsDirectory = context.getDir("parts", Context.MODE_PRIVATE);
|
File partsDirectory = context.getDir("parts", Context.MODE_PRIVATE);
|
||||||
@ -189,23 +190,23 @@ public class PartDatabase extends Database {
|
|||||||
FileOutputStream fout = getPartOutputStream(dataFile, part);
|
FileOutputStream fout = getPartOutputStream(dataFile, part);
|
||||||
|
|
||||||
if (part.getData() != null) {
|
if (part.getData() != null) {
|
||||||
Log.w("PartDatabase", "Writing part data from buffer");
|
Log.w("PartDatabase", "Writing part data from buffer");
|
||||||
fout.write(part.getData());
|
fout.write(part.getData());
|
||||||
fout.close();
|
fout.close();
|
||||||
return dataFile;
|
return dataFile;
|
||||||
} else if (part.getDataUri() != null) {
|
} else if (part.getDataUri() != null) {
|
||||||
Log.w("PartDatabase", "Writing part dat from URI");
|
Log.w("PartDatabase", "Writing part dat from URI");
|
||||||
byte[] buf = new byte[512];
|
byte[] buf = new byte[512];
|
||||||
InputStream in = context.getContentResolver().openInputStream(part.getDataUri());
|
InputStream in = context.getContentResolver().openInputStream(part.getDataUri());
|
||||||
int read;
|
int read;
|
||||||
while ((read = in.read(buf)) != -1)
|
while ((read = in.read(buf)) != -1)
|
||||||
fout.write(buf, 0, read);
|
fout.write(buf, 0, read);
|
||||||
|
|
||||||
fout.close();
|
fout.close();
|
||||||
in.close();
|
in.close();
|
||||||
return dataFile;
|
return dataFile;
|
||||||
} else {
|
} else {
|
||||||
throw new MmsException("Part is empty!");
|
throw new MmsException("Part is empty!");
|
||||||
}
|
}
|
||||||
} catch (FileNotFoundException e) {
|
} catch (FileNotFoundException e) {
|
||||||
throw new AssertionError(e);
|
throw new AssertionError(e);
|
||||||
@ -213,7 +214,7 @@ public class PartDatabase extends Database {
|
|||||||
throw new AssertionError(e);
|
throw new AssertionError(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private PduPart getPart(Cursor cursor, boolean includeData) {
|
private PduPart getPart(Cursor cursor, boolean includeData) {
|
||||||
PduPart part = new PduPart();
|
PduPart part = new PduPart();
|
||||||
String dataLocation = cursor.getString(cursor.getColumnIndexOrThrow(DATA));
|
String dataLocation = cursor.getString(cursor.getColumnIndexOrThrow(DATA));
|
||||||
@ -223,119 +224,119 @@ public class PartDatabase extends Database {
|
|||||||
if (includeData)
|
if (includeData)
|
||||||
readPartData(part, dataLocation);
|
readPartData(part, dataLocation);
|
||||||
part.setDataUri(ContentUris.withAppendedId(PartProvider.CONTENT_URI, partId));
|
part.setDataUri(ContentUris.withAppendedId(PartProvider.CONTENT_URI, partId));
|
||||||
|
|
||||||
return part;
|
return part;
|
||||||
}
|
}
|
||||||
|
|
||||||
private long insertPart(PduPart part, long mmsId) throws MmsException {
|
private long insertPart(PduPart part, long mmsId) throws MmsException {
|
||||||
SQLiteDatabase database = databaseHelper.getWritableDatabase();
|
SQLiteDatabase database = databaseHelper.getWritableDatabase();
|
||||||
File dataFile = writePartData(part);
|
File dataFile = writePartData(part);
|
||||||
|
|
||||||
Log.w("PartDatabase", "Wrote part to file: " + dataFile.getAbsolutePath());
|
Log.w("PartDatabase", "Wrote part to file: " + dataFile.getAbsolutePath());
|
||||||
ContentValues contentValues = getContentValuesForPart(part);
|
ContentValues contentValues = getContentValuesForPart(part);
|
||||||
|
|
||||||
contentValues.put(MMS_ID, mmsId);
|
contentValues.put(MMS_ID, mmsId);
|
||||||
contentValues.put(DATA, dataFile.getAbsolutePath());
|
contentValues.put(DATA, dataFile.getAbsolutePath());
|
||||||
|
|
||||||
return database.insert(TABLE_NAME, null, contentValues);
|
return database.insert(TABLE_NAME, null, contentValues);
|
||||||
}
|
}
|
||||||
|
|
||||||
public InputStream getPartStream(long partId) throws FileNotFoundException {
|
public InputStream getPartStream(long partId) throws FileNotFoundException {
|
||||||
SQLiteDatabase database = databaseHelper.getReadableDatabase();
|
SQLiteDatabase database = databaseHelper.getReadableDatabase();
|
||||||
Cursor cursor = null;
|
Cursor cursor = null;
|
||||||
|
|
||||||
Log.w("PartDatabase", "Getting part at ID: " + partId);
|
Log.w("PartDatabase", "Getting part at ID: " + partId);
|
||||||
try {
|
try {
|
||||||
cursor = database.query(TABLE_NAME, new String[]{DATA, ENCRYPTED}, ID_WHERE, new String[] {partId+""}, null, null, null);
|
cursor = database.query(TABLE_NAME, new String[]{DATA, ENCRYPTED}, ID_WHERE, new String[] {partId+""}, null, null, null);
|
||||||
|
|
||||||
if (cursor != null && cursor.moveToFirst()) {
|
if (cursor != null && cursor.moveToFirst()) {
|
||||||
PduPart part = new PduPart();
|
PduPart part = new PduPart();
|
||||||
part.setEncrypted(cursor.getInt(1) == 1);
|
part.setEncrypted(cursor.getInt(1) == 1);
|
||||||
|
|
||||||
return getPartInputStream(new File(cursor.getString(0)), part);
|
return getPartInputStream(new File(cursor.getString(0)), part);
|
||||||
} else {
|
} else {
|
||||||
throw new FileNotFoundException("No part for id: " + partId);
|
throw new FileNotFoundException("No part for id: " + partId);
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
if (cursor != null)
|
if (cursor != null)
|
||||||
cursor.close();
|
cursor.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void insertParts(long mmsId, PduBody body) throws MmsException {
|
public void insertParts(long mmsId, PduBody body) throws MmsException {
|
||||||
for (int i=0;i<body.getPartsNum();i++) {
|
for (int i=0;i<body.getPartsNum();i++) {
|
||||||
long partId = insertPart(body.getPart(i), mmsId);
|
long partId = insertPart(body.getPart(i), mmsId);
|
||||||
Log.w("PartDatabase", "Inserted part at ID: " + partId);
|
Log.w("PartDatabase", "Inserted part at ID: " + partId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public PduPart getPart(long partId, boolean includeData) {
|
public PduPart getPart(long partId, boolean includeData) {
|
||||||
SQLiteDatabase database = databaseHelper.getReadableDatabase();
|
SQLiteDatabase database = databaseHelper.getReadableDatabase();
|
||||||
Cursor cursor = null;
|
Cursor cursor = null;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
cursor = database.query(TABLE_NAME, null, ID_WHERE, new String[] {partId+""}, null, null, null);
|
cursor = database.query(TABLE_NAME, null, ID_WHERE, new String[] {partId+""}, null, null, null);
|
||||||
|
|
||||||
if (cursor != null && cursor.moveToFirst())
|
if (cursor != null && cursor.moveToFirst())
|
||||||
return getPart(cursor, includeData);
|
return getPart(cursor, includeData);
|
||||||
else
|
else
|
||||||
return null;
|
return null;
|
||||||
} finally {
|
} finally {
|
||||||
if (cursor != null)
|
if (cursor != null)
|
||||||
cursor.close();
|
cursor.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public PduBody getParts(long mmsId, boolean includeData) {
|
public PduBody getParts(long mmsId, boolean includeData) {
|
||||||
SQLiteDatabase database = databaseHelper.getReadableDatabase();
|
SQLiteDatabase database = databaseHelper.getReadableDatabase();
|
||||||
PduBody body = new PduBody();
|
PduBody body = new PduBody();
|
||||||
Cursor cursor = null;
|
Cursor cursor = null;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
cursor = database.query(TABLE_NAME, null, MMS_ID + " = ?", new String[] {mmsId+""}, null, null, null);
|
cursor = database.query(TABLE_NAME, null, MMS_ID + " = ?", new String[] {mmsId+""}, null, null, null);
|
||||||
|
|
||||||
while (cursor != null && cursor.moveToNext()) {
|
while (cursor != null && cursor.moveToNext()) {
|
||||||
PduPart part = getPart(cursor, includeData);
|
PduPart part = getPart(cursor, includeData);
|
||||||
body.addPart(part);
|
body.addPart(part);
|
||||||
}
|
}
|
||||||
|
|
||||||
return body;
|
return body;
|
||||||
} finally {
|
} finally {
|
||||||
if (cursor != null)
|
if (cursor != null)
|
||||||
cursor.close();
|
cursor.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void deleteParts(long mmsId) {
|
public void deleteParts(long mmsId) {
|
||||||
SQLiteDatabase database = databaseHelper.getWritableDatabase();
|
SQLiteDatabase database = databaseHelper.getWritableDatabase();
|
||||||
Cursor cursor = null;
|
Cursor cursor = null;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
cursor = database.query(TABLE_NAME, new String[] {DATA}, MMS_ID + " = ?", new String[] {mmsId+""}, null, null, null);
|
cursor = database.query(TABLE_NAME, new String[] {DATA}, MMS_ID + " = ?", new String[] {mmsId+""}, null, null, null);
|
||||||
|
|
||||||
while (cursor != null && cursor.moveToNext()) {
|
while (cursor != null && cursor.moveToNext()) {
|
||||||
new File(cursor.getString(0)).delete();
|
new File(cursor.getString(0)).delete();
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
if (cursor != null)
|
if (cursor != null)
|
||||||
cursor.close();
|
cursor.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
database.delete(TABLE_NAME, MMS_ID + " = ?", new String[] {mmsId+""});
|
database.delete(TABLE_NAME, MMS_ID + " = ?", new String[] {mmsId+""});
|
||||||
}
|
}
|
||||||
|
|
||||||
public void deleteAllParts() {
|
public void deleteAllParts() {
|
||||||
SQLiteDatabase database = databaseHelper.getWritableDatabase();
|
SQLiteDatabase database = databaseHelper.getWritableDatabase();
|
||||||
database.delete(TABLE_NAME, null, null);
|
database.delete(TABLE_NAME, null, null);
|
||||||
|
|
||||||
File partsDirectory = context.getDir("parts", Context.MODE_PRIVATE);
|
File partsDirectory = context.getDir("parts", Context.MODE_PRIVATE);
|
||||||
File[] parts = partsDirectory.listFiles();
|
File[] parts = partsDirectory.listFiles();
|
||||||
|
|
||||||
for (int i=0;i<parts.length;i++) {
|
for (int i=0;i<parts.length;i++) {
|
||||||
parts[i].delete();
|
parts[i].delete();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private byte[] getBytes(String data) {
|
private byte[] getBytes(String data) {
|
||||||
try {
|
try {
|
||||||
return data.getBytes(CharacterSets.MIMENAME_ISO_8859_1);
|
return data.getBytes(CharacterSets.MIMENAME_ISO_8859_1);
|
||||||
@ -344,7 +345,7 @@ public class PartDatabase extends Database {
|
|||||||
return new byte[0];
|
return new byte[0];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private String toIsoString(byte[] bytes) {
|
private String toIsoString(byte[] bytes) {
|
||||||
try {
|
try {
|
||||||
return new String(bytes, CharacterSets.MIMENAME_ISO_8859_1);
|
return new String(bytes, CharacterSets.MIMENAME_ISO_8859_1);
|
||||||
@ -354,7 +355,4 @@ public class PartDatabase extends Database {
|
|||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Copyright (C) 2011 Whisper Systems
|
* Copyright (C) 2011 Whisper Systems
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
@ -10,49 +10,50 @@
|
|||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
* GNU General Public License for more details.
|
* GNU General Public License for more details.
|
||||||
*
|
*
|
||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
package org.thoughtcrime.securesms.database;
|
package org.thoughtcrime.securesms.database;
|
||||||
|
|
||||||
import java.io.UnsupportedEncodingException;
|
import android.database.Cursor;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
import ws.com.google.android.mms.InvalidHeaderValueException;
|
import ws.com.google.android.mms.InvalidHeaderValueException;
|
||||||
import ws.com.google.android.mms.pdu.CharacterSets;
|
import ws.com.google.android.mms.pdu.CharacterSets;
|
||||||
import ws.com.google.android.mms.pdu.EncodedStringValue;
|
import ws.com.google.android.mms.pdu.EncodedStringValue;
|
||||||
import ws.com.google.android.mms.pdu.PduHeaders;
|
import ws.com.google.android.mms.pdu.PduHeaders;
|
||||||
import android.database.Cursor;
|
|
||||||
import android.util.Log;
|
import java.io.UnsupportedEncodingException;
|
||||||
|
|
||||||
public class PduHeadersBuilder {
|
public class PduHeadersBuilder {
|
||||||
|
|
||||||
private final PduHeaders headers;
|
private final PduHeaders headers;
|
||||||
private final Cursor cursor;
|
private final Cursor cursor;
|
||||||
|
|
||||||
public PduHeadersBuilder(PduHeaders headers, Cursor cursor) {
|
public PduHeadersBuilder(PduHeaders headers, Cursor cursor) {
|
||||||
this.headers = headers;
|
this.headers = headers;
|
||||||
this.cursor = cursor;
|
this.cursor = cursor;
|
||||||
}
|
}
|
||||||
|
|
||||||
public PduHeaders getHeaders() {
|
public PduHeaders getHeaders() {
|
||||||
return headers;
|
return headers;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addLong(String key, int headersKey) {
|
public void addLong(String key, int headersKey) {
|
||||||
int columnIndex = cursor.getColumnIndexOrThrow(key);
|
int columnIndex = cursor.getColumnIndexOrThrow(key);
|
||||||
|
|
||||||
if (!cursor.isNull(columnIndex))
|
if (!cursor.isNull(columnIndex))
|
||||||
headers.setLongInteger(cursor.getLong(columnIndex), headersKey);
|
headers.setLongInteger(cursor.getLong(columnIndex), headersKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addOctet(String key, int headersKey) throws InvalidHeaderValueException {
|
public void addOctet(String key, int headersKey) throws InvalidHeaderValueException {
|
||||||
int columnIndex = cursor.getColumnIndexOrThrow(key);
|
int columnIndex = cursor.getColumnIndexOrThrow(key);
|
||||||
|
|
||||||
if (!cursor.isNull(columnIndex))
|
if (!cursor.isNull(columnIndex))
|
||||||
headers.setOctet(cursor.getInt(columnIndex), headersKey);
|
headers.setOctet(cursor.getInt(columnIndex), headersKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addText(String key, int headersKey) {
|
public void addText(String key, int headersKey) {
|
||||||
String value = cursor.getString(cursor.getColumnIndexOrThrow(key));
|
String value = cursor.getString(cursor.getColumnIndexOrThrow(key));
|
||||||
if (value != null && value.trim().length() > 0)
|
if (value != null && value.trim().length() > 0)
|
||||||
@ -60,14 +61,14 @@ public class PduHeadersBuilder {
|
|||||||
}
|
}
|
||||||
public void add(String key, String charsetKey, int headersKey) {
|
public void add(String key, String charsetKey, int headersKey) {
|
||||||
String value = cursor.getString(cursor.getColumnIndexOrThrow(key));
|
String value = cursor.getString(cursor.getColumnIndexOrThrow(key));
|
||||||
|
|
||||||
if (value != null && value.trim().length() > 0) {
|
if (value != null && value.trim().length() > 0) {
|
||||||
int charsetValue = cursor.getInt(cursor.getColumnIndexOrThrow(charsetKey));
|
int charsetValue = cursor.getInt(cursor.getColumnIndexOrThrow(charsetKey));
|
||||||
EncodedStringValue encodedValue = new EncodedStringValue(charsetValue, getBytes(value));
|
EncodedStringValue encodedValue = new EncodedStringValue(charsetValue, getBytes(value));
|
||||||
headers.setEncodedStringValue(encodedValue, headersKey);
|
headers.setEncodedStringValue(encodedValue, headersKey);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private byte[] getBytes(String data) {
|
private byte[] getBytes(String data) {
|
||||||
try {
|
try {
|
||||||
return data.getBytes(CharacterSets.MIMENAME_ISO_8859_1);
|
return data.getBytes(CharacterSets.MIMENAME_ISO_8859_1);
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Copyright (C) 2011 Whisper Systems
|
* Copyright (C) 2011 Whisper Systems
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
@ -10,12 +10,16 @@
|
|||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
* GNU General Public License for more details.
|
* GNU General Public License for more details.
|
||||||
*
|
*
|
||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
package org.thoughtcrime.securesms.database;
|
package org.thoughtcrime.securesms.database;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
|
||||||
|
import org.thoughtcrime.securesms.util.Conversions;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
@ -24,24 +28,20 @@ import java.io.RandomAccessFile;
|
|||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.nio.channels.FileChannel;
|
import java.nio.channels.FileChannel;
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.util.Conversions;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
|
|
||||||
public abstract class Record {
|
public abstract class Record {
|
||||||
|
|
||||||
protected final String address;
|
protected final String address;
|
||||||
protected final Context context;
|
protected final Context context;
|
||||||
|
|
||||||
public Record(Context context, String address) {
|
public Record(Context context, String address) {
|
||||||
this.context = context;
|
this.context = context;
|
||||||
this.address = address;
|
this.address = address;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void delete() {
|
public void delete() {
|
||||||
delete(this.context, this.address);
|
delete(this.context, this.address);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected static void delete(Context context, String address) {
|
protected static void delete(Context context, String address) {
|
||||||
getAddressFile(context, address).delete();
|
getAddressFile(context, address).delete();
|
||||||
}
|
}
|
||||||
@ -52,44 +52,44 @@ public abstract class Record {
|
|||||||
|
|
||||||
protected RandomAccessFile openRandomAccessFile() throws FileNotFoundException {
|
protected RandomAccessFile openRandomAccessFile() throws FileNotFoundException {
|
||||||
return new RandomAccessFile(getAddressFile(), "rw");
|
return new RandomAccessFile(getAddressFile(), "rw");
|
||||||
}
|
}
|
||||||
|
|
||||||
protected FileInputStream openInputStream() throws FileNotFoundException {
|
protected FileInputStream openInputStream() throws FileNotFoundException {
|
||||||
return new FileInputStream(getAddressFile().getAbsolutePath());
|
return new FileInputStream(getAddressFile().getAbsolutePath());
|
||||||
}
|
}
|
||||||
|
|
||||||
private File getAddressFile() {
|
private File getAddressFile() {
|
||||||
return getAddressFile(context, address);
|
return getAddressFile(context, address);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static File getAddressFile(Context context, String address) {
|
private static File getAddressFile(Context context, String address) {
|
||||||
return new File(context.getFilesDir().getAbsolutePath() + File.separatorChar + "sessions", address);
|
return new File(context.getFilesDir().getAbsolutePath() + File.separatorChar + "sessions", address);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected byte[] readBlob(FileInputStream in) throws IOException {
|
protected byte[] readBlob(FileInputStream in) throws IOException {
|
||||||
int length = readInteger(in);
|
int length = readInteger(in);
|
||||||
byte[] blobBytes = new byte[length];
|
byte[] blobBytes = new byte[length];
|
||||||
|
|
||||||
in.read(blobBytes, 0, blobBytes.length);
|
in.read(blobBytes, 0, blobBytes.length);
|
||||||
return blobBytes;
|
return blobBytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void writeBlob(byte[] blobBytes, FileChannel out) throws IOException {
|
protected void writeBlob(byte[] blobBytes, FileChannel out) throws IOException {
|
||||||
writeInteger(blobBytes.length, out);
|
writeInteger(blobBytes.length, out);
|
||||||
ByteBuffer buffer = ByteBuffer.wrap(blobBytes);
|
ByteBuffer buffer = ByteBuffer.wrap(blobBytes);
|
||||||
out.write(buffer);
|
out.write(buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected int readInteger(FileInputStream in) throws IOException {
|
protected int readInteger(FileInputStream in) throws IOException {
|
||||||
byte[] integer = new byte[4];
|
byte[] integer = new byte[4];
|
||||||
in.read(integer, 0, integer.length);
|
in.read(integer, 0, integer.length);
|
||||||
return Conversions.byteArrayToInt(integer);
|
return Conversions.byteArrayToInt(integer);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void writeInteger(int value, FileChannel out) throws IOException {
|
protected void writeInteger(int value, FileChannel out) throws IOException {
|
||||||
byte[] valueBytes = Conversions.intToByteArray(value);
|
byte[] valueBytes = Conversions.intToByteArray(value);
|
||||||
ByteBuffer buffer = ByteBuffer.wrap(valueBytes);
|
ByteBuffer buffer = ByteBuffer.wrap(valueBytes);
|
||||||
out.write(buffer);
|
out.write(buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Copyright (C) 2011 Whisper Systems
|
* Copyright (C) 2011 Whisper Systems
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
@ -10,57 +10,57 @@
|
|||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
* GNU General Public License for more details.
|
* GNU General Public License for more details.
|
||||||
*
|
*
|
||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
package org.thoughtcrime.securesms.database;
|
package org.thoughtcrime.securesms.database;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import org.thoughtcrime.securesms.crypto.InvalidKeyException;
|
||||||
|
import org.thoughtcrime.securesms.crypto.PublicKey;
|
||||||
|
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||||
|
import org.thoughtcrime.securesms.util.Hex;
|
||||||
|
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.RandomAccessFile;
|
import java.io.RandomAccessFile;
|
||||||
import java.nio.channels.FileChannel;
|
import java.nio.channels.FileChannel;
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.crypto.InvalidKeyException;
|
|
||||||
import org.thoughtcrime.securesms.crypto.PublicKey;
|
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
|
||||||
import org.thoughtcrime.securesms.util.Hex;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents the current and last public key belonging to the "remote"
|
* Represents the current and last public key belonging to the "remote"
|
||||||
* endpoint in an encrypted session. These are stored on disk.
|
* endpoint in an encrypted session. These are stored on disk.
|
||||||
*
|
*
|
||||||
* @author Moxie Marlinspike
|
* @author Moxie Marlinspike
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public class RemoteKeyRecord extends Record {
|
public class RemoteKeyRecord extends Record {
|
||||||
private static final Object FILE_LOCK = new Object();
|
private static final Object FILE_LOCK = new Object();
|
||||||
|
|
||||||
private PublicKey remoteKeyCurrent;
|
private PublicKey remoteKeyCurrent;
|
||||||
private PublicKey remoteKeyLast;
|
private PublicKey remoteKeyLast;
|
||||||
|
|
||||||
public RemoteKeyRecord(Context context, Recipient recipient) {
|
public RemoteKeyRecord(Context context, Recipient recipient) {
|
||||||
super(context,getFileNameForRecipient(context, recipient));
|
super(context,getFileNameForRecipient(context, recipient));
|
||||||
loadData();
|
loadData();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void delete(Context context, Recipient recipient) {
|
public static void delete(Context context, Recipient recipient) {
|
||||||
Record.delete(context, getFileNameForRecipient(context, recipient));
|
Record.delete(context, getFileNameForRecipient(context, recipient));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean hasRecord(Context context, Recipient recipient) {
|
public static boolean hasRecord(Context context, Recipient recipient) {
|
||||||
Log.w("LocalKeyRecord", "Checking: " + getFileNameForRecipient(context, recipient));
|
Log.w("LocalKeyRecord", "Checking: " + getFileNameForRecipient(context, recipient));
|
||||||
return Record.hasRecord(context, getFileNameForRecipient(context, recipient));
|
return Record.hasRecord(context, getFileNameForRecipient(context, recipient));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String getFileNameForRecipient(Context context, Recipient recipient) {
|
private static String getFileNameForRecipient(Context context, Recipient recipient) {
|
||||||
return CanonicalAddressDatabase.getInstance(context).getCanonicalAddress(recipient.getNumber()) + "-remote";
|
return CanonicalAddressDatabase.getInstance(context).getCanonicalAddress(recipient.getNumber()) + "-remote";
|
||||||
}
|
}
|
||||||
|
|
||||||
public void updateCurrentRemoteKey(PublicKey remoteKey) {
|
public void updateCurrentRemoteKey(PublicKey remoteKey) {
|
||||||
Log.w("RemoteKeyRecord", "Updating current remote key: " + remoteKey.getId());
|
Log.w("RemoteKeyRecord", "Updating current remote key: " + remoteKey.getId());
|
||||||
if (remoteKey.getId() > remoteKeyCurrent.getId()) {
|
if (remoteKey.getId() > remoteKeyCurrent.getId()) {
|
||||||
@ -68,29 +68,29 @@ public class RemoteKeyRecord extends Record {
|
|||||||
this.remoteKeyCurrent = remoteKey;
|
this.remoteKeyCurrent = remoteKey;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setCurrentRemoteKey(PublicKey remoteKeyCurrent) {
|
public void setCurrentRemoteKey(PublicKey remoteKeyCurrent) {
|
||||||
this.remoteKeyCurrent = remoteKeyCurrent;
|
this.remoteKeyCurrent = remoteKeyCurrent;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setLastRemoteKey(PublicKey remoteKeyLast) {
|
public void setLastRemoteKey(PublicKey remoteKeyLast) {
|
||||||
this.remoteKeyLast = remoteKeyLast;
|
this.remoteKeyLast = remoteKeyLast;
|
||||||
}
|
}
|
||||||
|
|
||||||
public PublicKey getCurrentRemoteKey() {
|
public PublicKey getCurrentRemoteKey() {
|
||||||
return this.remoteKeyCurrent;
|
return this.remoteKeyCurrent;
|
||||||
}
|
}
|
||||||
|
|
||||||
public PublicKey getLastRemoteKey() {
|
public PublicKey getLastRemoteKey() {
|
||||||
return this.remoteKeyLast;
|
return this.remoteKeyLast;
|
||||||
}
|
}
|
||||||
|
|
||||||
public PublicKey getKeyForId(int id) throws InvalidKeyIdException {
|
public PublicKey getKeyForId(int id) throws InvalidKeyIdException {
|
||||||
if (this.remoteKeyCurrent.getId() == id) return this.remoteKeyCurrent;
|
if (this.remoteKeyCurrent.getId() == id) return this.remoteKeyCurrent;
|
||||||
else if (this.remoteKeyLast.getId() == id) return this.remoteKeyLast;
|
else if (this.remoteKeyLast.getId() == id) return this.remoteKeyLast;
|
||||||
else throw new InvalidKeyIdException("No remote key for ID: " + id);
|
else throw new InvalidKeyIdException("No remote key for ID: " + id);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void save() {
|
public void save() {
|
||||||
Log.w("RemoteKeyRecord", "Saving remote key record for recipient: " + this.address);
|
Log.w("RemoteKeyRecord", "Saving remote key record for recipient: " + this.address);
|
||||||
synchronized (FILE_LOCK) {
|
synchronized (FILE_LOCK) {
|
||||||
@ -99,10 +99,10 @@ public class RemoteKeyRecord extends Record {
|
|||||||
FileChannel out = file.getChannel();
|
FileChannel out = file.getChannel();
|
||||||
Log.w("RemoteKeyRecord", "Opened file of size: " + out.size());
|
Log.w("RemoteKeyRecord", "Opened file of size: " + out.size());
|
||||||
out.position(0);
|
out.position(0);
|
||||||
|
|
||||||
writeKey(remoteKeyCurrent, out);
|
writeKey(remoteKeyCurrent, out);
|
||||||
writeKey(remoteKeyLast, out);
|
writeKey(remoteKeyLast, out);
|
||||||
|
|
||||||
out.truncate(out.position());
|
out.truncate(out.position());
|
||||||
out.close();
|
out.close();
|
||||||
file.close();
|
file.close();
|
||||||
@ -112,12 +112,12 @@ public class RemoteKeyRecord extends Record {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void loadData() {
|
private void loadData() {
|
||||||
Log.w("RemoteKeyRecord", "Loading remote key record for recipient: " + this.address);
|
Log.w("RemoteKeyRecord", "Loading remote key record for recipient: " + this.address);
|
||||||
synchronized (FILE_LOCK) {
|
synchronized (FILE_LOCK) {
|
||||||
try {
|
try {
|
||||||
FileInputStream in = this.openInputStream();
|
FileInputStream in = this.openInputStream();
|
||||||
remoteKeyCurrent = readKey(in);
|
remoteKeyCurrent = readKey(in);
|
||||||
remoteKeyLast = readKey(in);
|
remoteKeyLast = readKey(in);
|
||||||
in.close();
|
in.close();
|
||||||
@ -130,13 +130,13 @@ public class RemoteKeyRecord extends Record {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void writeKey(PublicKey key, FileChannel out) throws IOException {
|
private void writeKey(PublicKey key, FileChannel out) throws IOException {
|
||||||
byte[] keyBytes = key.serialize();
|
byte[] keyBytes = key.serialize();
|
||||||
Log.w("RemoteKeyRecord", "Serializing remote key bytes: " + Hex.toString(keyBytes));
|
Log.w("RemoteKeyRecord", "Serializing remote key bytes: " + Hex.toString(keyBytes));
|
||||||
writeBlob(keyBytes, out);
|
writeBlob(keyBytes, out);
|
||||||
}
|
}
|
||||||
|
|
||||||
private PublicKey readKey(FileInputStream in) throws IOException {
|
private PublicKey readKey(FileInputStream in) throws IOException {
|
||||||
try {
|
try {
|
||||||
byte[] keyBytes = readBlob(in);
|
byte[] keyBytes = readBlob(in);
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Copyright (C) 2011 Whisper Systems
|
* Copyright (C) 2011 Whisper Systems
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
@ -10,25 +10,25 @@
|
|||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
* GNU General Public License for more details.
|
* GNU General Public License for more details.
|
||||||
*
|
*
|
||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
package org.thoughtcrime.securesms.database;
|
package org.thoughtcrime.securesms.database;
|
||||||
|
|
||||||
import javax.crypto.spec.SecretKeySpec;
|
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.crypto.MasterCipher;
|
import org.thoughtcrime.securesms.crypto.MasterCipher;
|
||||||
import org.thoughtcrime.securesms.crypto.MasterSecret;
|
import org.thoughtcrime.securesms.crypto.MasterSecret;
|
||||||
import org.thoughtcrime.securesms.crypto.SessionCipher;
|
import org.thoughtcrime.securesms.crypto.SessionCipher;
|
||||||
import org.thoughtcrime.securesms.util.Conversions;
|
import org.thoughtcrime.securesms.util.Conversions;
|
||||||
import org.thoughtcrime.securesms.util.Util;
|
import org.thoughtcrime.securesms.util.Util;
|
||||||
|
|
||||||
|
import javax.crypto.spec.SecretKeySpec;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents the currently negotiated session key for a given
|
* Represents the currently negotiated session key for a given
|
||||||
* local key id and remote key id. This is stored encrypted on
|
* local key id and remote key id. This is stored encrypted on
|
||||||
* disk.
|
* disk.
|
||||||
*
|
*
|
||||||
* @author Moxie Marlinspike
|
* @author Moxie Marlinspike
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ -39,7 +39,7 @@ public class SessionKey {
|
|||||||
private SecretKeySpec cipherKey;
|
private SecretKeySpec cipherKey;
|
||||||
private SecretKeySpec macKey;
|
private SecretKeySpec macKey;
|
||||||
private MasterCipher masterCipher;
|
private MasterCipher masterCipher;
|
||||||
|
|
||||||
public SessionKey(int localKeyId, int remoteKeyId, SecretKeySpec cipherKey, SecretKeySpec macKey, MasterSecret masterSecret) {
|
public SessionKey(int localKeyId, int remoteKeyId, SecretKeySpec cipherKey, SecretKeySpec macKey, MasterSecret masterSecret) {
|
||||||
this.localKeyId = localKeyId;
|
this.localKeyId = localKeyId;
|
||||||
this.remoteKeyId = remoteKeyId;
|
this.remoteKeyId = remoteKeyId;
|
||||||
@ -47,33 +47,33 @@ public class SessionKey {
|
|||||||
this.macKey = macKey;
|
this.macKey = macKey;
|
||||||
this.masterCipher = new MasterCipher(masterSecret);
|
this.masterCipher = new MasterCipher(masterSecret);
|
||||||
}
|
}
|
||||||
|
|
||||||
public SessionKey(byte[] bytes, MasterSecret masterSecret) {
|
public SessionKey(byte[] bytes, MasterSecret masterSecret) {
|
||||||
this.masterCipher = new MasterCipher(masterSecret);
|
this.masterCipher = new MasterCipher(masterSecret);
|
||||||
deserialize(bytes);
|
deserialize(bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
public byte[] serialize() {
|
public byte[] serialize() {
|
||||||
byte[] localKeyIdBytes = Conversions.mediumToByteArray(localKeyId);
|
byte[] localKeyIdBytes = Conversions.mediumToByteArray(localKeyId);
|
||||||
byte[] remoteKeyIdBytes = Conversions.mediumToByteArray(remoteKeyId);
|
byte[] remoteKeyIdBytes = Conversions.mediumToByteArray(remoteKeyId);
|
||||||
byte[] cipherKeyBytes = cipherKey.getEncoded();
|
byte[] cipherKeyBytes = cipherKey.getEncoded();
|
||||||
byte[] macKeyBytes = macKey.getEncoded();
|
byte[] macKeyBytes = macKey.getEncoded();
|
||||||
byte[] combined = Util.combine(localKeyIdBytes, remoteKeyIdBytes, cipherKeyBytes, macKeyBytes);
|
byte[] combined = Util.combine(localKeyIdBytes, remoteKeyIdBytes, cipherKeyBytes, macKeyBytes);
|
||||||
|
|
||||||
return masterCipher.encryptBytes(combined);
|
return masterCipher.encryptBytes(combined);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void deserialize(byte[] bytes) {
|
private void deserialize(byte[] bytes) {
|
||||||
byte[] decrypted = masterCipher.encryptBytes(bytes);
|
byte[] decrypted = masterCipher.encryptBytes(bytes);
|
||||||
this.localKeyId = Conversions.byteArrayToMedium(decrypted, 0);
|
this.localKeyId = Conversions.byteArrayToMedium(decrypted, 0);
|
||||||
this.remoteKeyId = Conversions.byteArrayToMedium(decrypted, 3);
|
this.remoteKeyId = Conversions.byteArrayToMedium(decrypted, 3);
|
||||||
|
|
||||||
byte[] keyBytes = new byte[SessionCipher.CIPHER_KEY_LENGTH];
|
byte[] keyBytes = new byte[SessionCipher.CIPHER_KEY_LENGTH];
|
||||||
System.arraycopy(decrypted, 6, keyBytes, 0, keyBytes.length);
|
System.arraycopy(decrypted, 6, keyBytes, 0, keyBytes.length);
|
||||||
|
|
||||||
byte[] macBytes = new byte[SessionCipher.MAC_KEY_LENGTH];
|
byte[] macBytes = new byte[SessionCipher.MAC_KEY_LENGTH];
|
||||||
System.arraycopy(decrypted, 6 + keyBytes.length, macBytes, 0, macBytes.length);
|
System.arraycopy(decrypted, 6 + keyBytes.length, macBytes, 0, macBytes.length);
|
||||||
|
|
||||||
this.cipherKey = new SecretKeySpec(keyBytes, "AES");
|
this.cipherKey = new SecretKeySpec(keyBytes, "AES");
|
||||||
this.macKey = new SecretKeySpec(macBytes, "HmacSHA1");
|
this.macKey = new SecretKeySpec(macBytes, "HmacSHA1");
|
||||||
}
|
}
|
||||||
@ -93,5 +93,5 @@ public class SessionKey {
|
|||||||
public SecretKeySpec getMacKey() {
|
public SecretKeySpec getMacKey() {
|
||||||
return this.macKey;
|
return this.macKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -208,8 +208,8 @@ public class SessionRecord extends Record {
|
|||||||
Log.w("SessionRecord", "No session information found.");
|
Log.w("SessionRecord", "No session information found.");
|
||||||
return;
|
return;
|
||||||
} catch (IOException ioe) {
|
} catch (IOException ioe) {
|
||||||
Log.w("keyrecord", ioe);
|
Log.w("keyrecord", ioe);
|
||||||
// XXX
|
// XXX
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -223,5 +223,4 @@ public class SessionRecord extends Record {
|
|||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -117,12 +117,12 @@ public class SmsDatabase extends Database {
|
|||||||
try {
|
try {
|
||||||
cursor = db.rawQuery(sql, sqlArgs);
|
cursor = db.rawQuery(sql, sqlArgs);
|
||||||
if (cursor != null && cursor.moveToFirst())
|
if (cursor != null && cursor.moveToFirst())
|
||||||
return cursor.getLong(0);
|
return cursor.getLong(0);
|
||||||
else
|
else
|
||||||
return -1;
|
return -1;
|
||||||
} finally {
|
} finally {
|
||||||
if (cursor != null)
|
if (cursor != null)
|
||||||
cursor.close();
|
cursor.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -134,10 +134,10 @@ public class SmsDatabase extends Database {
|
|||||||
cursor = db.query(TABLE_NAME, new String[] {"COUNT(*)"}, THREAD_ID + " = ?", new String[] {threadId+""}, null, null, null);
|
cursor = db.query(TABLE_NAME, new String[] {"COUNT(*)"}, THREAD_ID + " = ?", new String[] {threadId+""}, null, null, null);
|
||||||
|
|
||||||
if (cursor != null && cursor.moveToFirst())
|
if (cursor != null && cursor.moveToFirst())
|
||||||
return cursor.getInt(0);
|
return cursor.getInt(0);
|
||||||
} finally {
|
} finally {
|
||||||
if (cursor != null)
|
if (cursor != null)
|
||||||
cursor.close();
|
cursor.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@ -201,7 +201,7 @@ public class SmsDatabase extends Database {
|
|||||||
|
|
||||||
public long insertMessageSent(String address, long threadId, String body, long date, long type) {
|
public long insertMessageSent(String address, long threadId, String body, long date, long type) {
|
||||||
ContentValues contentValues = new ContentValues(6);
|
ContentValues contentValues = new ContentValues(6);
|
||||||
// contentValues.put(ADDRESS, NumberUtil.filterNumber(address));
|
// contentValues.put(ADDRESS, NumberUtil.filterNumber(address));
|
||||||
contentValues.put(ADDRESS, address);
|
contentValues.put(ADDRESS, address);
|
||||||
contentValues.put(THREAD_ID, threadId);
|
contentValues.put(THREAD_ID, threadId);
|
||||||
contentValues.put(BODY, body);
|
contentValues.put(BODY, body);
|
||||||
@ -292,7 +292,7 @@ public class SmsDatabase extends Database {
|
|||||||
|
|
||||||
/*package*/ SQLiteStatement createInsertStatement(SQLiteDatabase database) {
|
/*package*/ SQLiteStatement createInsertStatement(SQLiteDatabase database) {
|
||||||
return database.compileStatement("INSERT INTO " + TABLE_NAME + " (" + ADDRESS + ", " + PERSON + ", " + DATE + ", " + PROTOCOL + ", " + READ + ", " + STATUS + ", " + TYPE + ", " + REPLY_PATH_PRESENT + ", " + SUBJECT + ", " + BODY + ", " + SERVICE_CENTER + ", THREAD_ID) " +
|
return database.compileStatement("INSERT INTO " + TABLE_NAME + " (" + ADDRESS + ", " + PERSON + ", " + DATE + ", " + PROTOCOL + ", " + READ + ", " + STATUS + ", " + TYPE + ", " + REPLY_PATH_PRESENT + ", " + SUBJECT + ", " + BODY + ", " + SERVICE_CENTER + ", THREAD_ID) " +
|
||||||
" VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
|
" VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class Types {
|
public static class Types {
|
||||||
|
@ -54,13 +54,12 @@ public class ThreadDatabase extends Database {
|
|||||||
super(context, databaseHelper);
|
super(context, databaseHelper);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private long[] getRecipientIds(Recipients recipients) {
|
private long[] getRecipientIds(Recipients recipients) {
|
||||||
Set<Long> recipientSet = new HashSet<Long>();
|
Set<Long> recipientSet = new HashSet<Long>();
|
||||||
List<Recipient> recipientList = recipients.getRecipientsList();
|
List<Recipient> recipientList = recipients.getRecipientsList();
|
||||||
|
|
||||||
for (Recipient recipient : recipientList) {
|
for (Recipient recipient : recipientList) {
|
||||||
// String number = NumberUtil.filterNumber(recipient.getNumber());
|
// String number = NumberUtil.filterNumber(recipient.getNumber());
|
||||||
String number = recipient.getNumber();
|
String number = recipient.getNumber();
|
||||||
recipientSet.add(Long.valueOf(DatabaseFactory.getAddressDatabase(context).getCanonicalAddress(number)));
|
recipientSet.add(Long.valueOf(DatabaseFactory.getAddressDatabase(context).getCanonicalAddress(number)));
|
||||||
}
|
}
|
||||||
@ -229,12 +228,12 @@ public class ThreadDatabase extends Database {
|
|||||||
cursor = db.query(TABLE_NAME, new String[]{ID}, where, recipientsArg, null, null, null);
|
cursor = db.query(TABLE_NAME, new String[]{ID}, where, recipientsArg, null, null, null);
|
||||||
|
|
||||||
if (cursor != null && cursor.moveToFirst())
|
if (cursor != null && cursor.moveToFirst())
|
||||||
return cursor.getLong(cursor.getColumnIndexOrThrow(ID));
|
return cursor.getLong(cursor.getColumnIndexOrThrow(ID));
|
||||||
else
|
else
|
||||||
return -1L;
|
return -1L;
|
||||||
} finally {
|
} finally {
|
||||||
if (cursor != null)
|
if (cursor != null)
|
||||||
cursor.close();
|
cursor.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -250,12 +249,12 @@ public class ThreadDatabase extends Database {
|
|||||||
cursor = db.query(TABLE_NAME, new String[]{ID}, where, recipientsArg, null, null, null);
|
cursor = db.query(TABLE_NAME, new String[]{ID}, where, recipientsArg, null, null, null);
|
||||||
|
|
||||||
if (cursor != null && cursor.moveToFirst())
|
if (cursor != null && cursor.moveToFirst())
|
||||||
return cursor.getLong(cursor.getColumnIndexOrThrow(ID));
|
return cursor.getLong(cursor.getColumnIndexOrThrow(ID));
|
||||||
else
|
else
|
||||||
return createThreadForRecipients(recipientsList, recipientIds.length);
|
return createThreadForRecipients(recipientsList, recipientIds.length);
|
||||||
} finally {
|
} finally {
|
||||||
if (cursor != null)
|
if (cursor != null)
|
||||||
cursor.close();
|
cursor.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -273,22 +272,18 @@ public class ThreadDatabase extends Database {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
cursor = mmsSmsDatabase.getConversationSnippet(threadId);
|
cursor = mmsSmsDatabase.getConversationSnippet(threadId);
|
||||||
if (cursor != null && cursor.moveToFirst())
|
if (cursor != null && cursor.moveToFirst()) {
|
||||||
updateThread(threadId, count,
|
updateThread(threadId, count,
|
||||||
cursor.getString(cursor.getColumnIndexOrThrow(SmsDatabase.BODY)),
|
cursor.getString(cursor.getColumnIndexOrThrow(SmsDatabase.BODY)),
|
||||||
cursor.getLong(cursor.getColumnIndexOrThrow(SmsDatabase.DATE)));
|
cursor.getLong(cursor.getColumnIndexOrThrow(SmsDatabase.DATE)));
|
||||||
else
|
} else {
|
||||||
deleteThread(threadId);
|
deleteThread(threadId);
|
||||||
|
}
|
||||||
} finally {
|
} finally {
|
||||||
if (cursor != null)
|
if (cursor != null)
|
||||||
cursor.close();
|
cursor.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
notifyConversationListListeners();
|
notifyConversationListListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Copyright (C) 2011 Whisper Systems
|
* Copyright (C) 2011 Whisper Systems
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
@ -10,38 +10,38 @@
|
|||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
* GNU General Public License for more details.
|
* GNU General Public License for more details.
|
||||||
*
|
*
|
||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
package org.thoughtcrime.securesms.protocol;
|
package org.thoughtcrime.securesms.protocol;
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
import android.util.Log;
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.crypto.InvalidKeyException;
|
import org.thoughtcrime.securesms.crypto.InvalidKeyException;
|
||||||
import org.thoughtcrime.securesms.crypto.InvalidMessageException;
|
import org.thoughtcrime.securesms.crypto.InvalidMessageException;
|
||||||
import org.thoughtcrime.securesms.crypto.PublicKey;
|
import org.thoughtcrime.securesms.crypto.PublicKey;
|
||||||
import org.thoughtcrime.securesms.util.Conversions;
|
import org.thoughtcrime.securesms.util.Conversions;
|
||||||
|
|
||||||
import android.util.Log;
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parses and serializes the encrypted message format.
|
* Parses and serializes the encrypted message format.
|
||||||
*
|
*
|
||||||
* @author Moxie Marlinspike
|
* @author Moxie Marlinspike
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public class Message {
|
public class Message {
|
||||||
|
|
||||||
public static final int SUPPORTED_VERSION = 1;
|
public static final int SUPPORTED_VERSION = 1;
|
||||||
|
|
||||||
private static final int VERSION_LENGTH = 1;
|
private static final int VERSION_LENGTH = 1;
|
||||||
private static final int SENDER_KEY_ID_LENGTH = 3;
|
private static final int SENDER_KEY_ID_LENGTH = 3;
|
||||||
private static final int RECEIVER_KEY_ID_LENGTH = 3;
|
private static final int RECEIVER_KEY_ID_LENGTH = 3;
|
||||||
private static final int NEXT_KEY_LENGTH = PublicKey.KEY_SIZE;
|
private static final int NEXT_KEY_LENGTH = PublicKey.KEY_SIZE;
|
||||||
private static final int COUNTER_LENGTH = 3;
|
private static final int COUNTER_LENGTH = 3;
|
||||||
public static final int HEADER_LENGTH = VERSION_LENGTH + SENDER_KEY_ID_LENGTH + RECEIVER_KEY_ID_LENGTH + COUNTER_LENGTH + NEXT_KEY_LENGTH;
|
public static final int HEADER_LENGTH = VERSION_LENGTH + SENDER_KEY_ID_LENGTH + RECEIVER_KEY_ID_LENGTH + COUNTER_LENGTH + NEXT_KEY_LENGTH;
|
||||||
|
|
||||||
private static final int VERSION_OFFSET = 0;
|
private static final int VERSION_OFFSET = 0;
|
||||||
private static final int SENDER_KEY_ID_OFFSET = VERSION_OFFSET + VERSION_LENGTH;
|
private static final int SENDER_KEY_ID_OFFSET = VERSION_OFFSET + VERSION_LENGTH;
|
||||||
private static final int RECEIVER_KEY_ID_OFFSET = SENDER_KEY_ID_OFFSET + SENDER_KEY_ID_LENGTH;
|
private static final int RECEIVER_KEY_ID_OFFSET = SENDER_KEY_ID_OFFSET + SENDER_KEY_ID_LENGTH;
|
||||||
@ -55,9 +55,9 @@ public class Message {
|
|||||||
private int messageVersion;
|
private int messageVersion;
|
||||||
private int supportedVersion;
|
private int supportedVersion;
|
||||||
private byte[] message;
|
private byte[] message;
|
||||||
|
|
||||||
private PublicKey nextKey;
|
private PublicKey nextKey;
|
||||||
|
|
||||||
public Message(int senderKeyId, int receiverKeyId, PublicKey nextKey, int counter, byte[] message, int messageVersion, int supportedVersion) {
|
public Message(int senderKeyId, int receiverKeyId, PublicKey nextKey, int counter, byte[] message, int messageVersion, int supportedVersion) {
|
||||||
this.senderKeyId = senderKeyId;
|
this.senderKeyId = senderKeyId;
|
||||||
this.receiverKeyId = receiverKeyId;
|
this.receiverKeyId = receiverKeyId;
|
||||||
@ -67,33 +67,33 @@ public class Message {
|
|||||||
this.messageVersion = messageVersion;
|
this.messageVersion = messageVersion;
|
||||||
this.supportedVersion = supportedVersion;
|
this.supportedVersion = supportedVersion;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Message(byte[] messageBytes) throws InvalidMessageException {
|
public Message(byte[] messageBytes) throws InvalidMessageException {
|
||||||
try {
|
try {
|
||||||
if (messageBytes.length <= HEADER_LENGTH)
|
if (messageBytes.length <= HEADER_LENGTH)
|
||||||
throw new InvalidMessageException("Message is shorter than headers.");
|
throw new InvalidMessageException("Message is shorter than headers.");
|
||||||
|
|
||||||
this.messageVersion = Conversions.highBitsToInt(messageBytes[VERSION_OFFSET]);
|
this.messageVersion = Conversions.highBitsToInt(messageBytes[VERSION_OFFSET]);
|
||||||
this.supportedVersion = Conversions.lowBitsToInt(messageBytes[VERSION_OFFSET]);
|
this.supportedVersion = Conversions.lowBitsToInt(messageBytes[VERSION_OFFSET]);
|
||||||
|
|
||||||
Log.w("Message", "Message Version: " + messageVersion);
|
Log.w("Message", "Message Version: " + messageVersion);
|
||||||
Log.w("Message", "Supported Version: " + supportedVersion);
|
Log.w("Message", "Supported Version: " + supportedVersion);
|
||||||
|
|
||||||
if (messageVersion > SUPPORTED_VERSION)
|
if (messageVersion > SUPPORTED_VERSION)
|
||||||
throw new InvalidMessageException("Message protocol version not supported: " + messageVersion);
|
throw new InvalidMessageException("Message protocol version not supported: " + messageVersion);
|
||||||
|
|
||||||
this.senderKeyId = Conversions.byteArrayToMedium(messageBytes, SENDER_KEY_ID_OFFSET);
|
this.senderKeyId = Conversions.byteArrayToMedium(messageBytes, SENDER_KEY_ID_OFFSET);
|
||||||
this.receiverKeyId = Conversions.byteArrayToMedium(messageBytes, RECEIVER_KEY_ID_OFFSET);
|
this.receiverKeyId = Conversions.byteArrayToMedium(messageBytes, RECEIVER_KEY_ID_OFFSET);
|
||||||
this.counter = Conversions.byteArrayToMedium(messageBytes, COUNTER_OFFSET);
|
this.counter = Conversions.byteArrayToMedium(messageBytes, COUNTER_OFFSET);
|
||||||
|
|
||||||
Log.w("Message", "Parsed current version: " + messageVersion + " supported version: " + supportedVersion);
|
Log.w("Message", "Parsed current version: " + messageVersion + " supported version: " + supportedVersion);
|
||||||
|
|
||||||
byte[] nextKeyBytes = new byte[NEXT_KEY_LENGTH];
|
byte[] nextKeyBytes = new byte[NEXT_KEY_LENGTH];
|
||||||
byte[] textBytes = new byte[messageBytes.length - HEADER_LENGTH];
|
byte[] textBytes = new byte[messageBytes.length - HEADER_LENGTH];
|
||||||
|
|
||||||
System.arraycopy(messageBytes, NEXT_KEY_OFFSET, nextKeyBytes, 0, nextKeyBytes.length);
|
System.arraycopy(messageBytes, NEXT_KEY_OFFSET, nextKeyBytes, 0, nextKeyBytes.length);
|
||||||
System.arraycopy(messageBytes, TEXT_OFFSET, textBytes, 0, textBytes.length);
|
System.arraycopy(messageBytes, TEXT_OFFSET, textBytes, 0, textBytes.length);
|
||||||
|
|
||||||
Log.w("Message", "Pulling next key out of message...");
|
Log.w("Message", "Pulling next key out of message...");
|
||||||
this.nextKey = new PublicKey(nextKeyBytes);
|
this.nextKey = new PublicKey(nextKeyBytes);
|
||||||
this.message = textBytes;
|
this.message = textBytes;
|
||||||
@ -101,10 +101,10 @@ public class Message {
|
|||||||
throw new AssertionError(ike);
|
throw new AssertionError(ike);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public byte[] serialize() {
|
public byte[] serialize() {
|
||||||
ByteBuffer buffer = ByteBuffer.allocate(HEADER_LENGTH + message.length);
|
ByteBuffer buffer = ByteBuffer.allocate(HEADER_LENGTH + message.length);
|
||||||
|
|
||||||
Log.w("Message", "Constructing Message Version: (" + messageVersion + "," + supportedVersion + ")");
|
Log.w("Message", "Constructing Message Version: (" + messageVersion + "," + supportedVersion + ")");
|
||||||
|
|
||||||
byte versionByte = Conversions.intsToByteHighAndLow(messageVersion, supportedVersion);
|
byte versionByte = Conversions.intsToByteHighAndLow(messageVersion, supportedVersion);
|
||||||
@ -113,39 +113,39 @@ public class Message {
|
|||||||
Log.w("Message", "Serializing next key into message...");
|
Log.w("Message", "Serializing next key into message...");
|
||||||
byte[] nextKeyBytes = nextKey.serialize();
|
byte[] nextKeyBytes = nextKey.serialize();
|
||||||
byte[] counterBytes = Conversions.mediumToByteArray(counter);
|
byte[] counterBytes = Conversions.mediumToByteArray(counter);
|
||||||
|
|
||||||
buffer.put(versionByte);
|
buffer.put(versionByte);
|
||||||
buffer.put(senderKeyIdBytes);
|
buffer.put(senderKeyIdBytes);
|
||||||
buffer.put(receiverKeyIdBytes);
|
buffer.put(receiverKeyIdBytes);
|
||||||
buffer.put(nextKeyBytes);
|
buffer.put(nextKeyBytes);
|
||||||
buffer.put(counterBytes);
|
buffer.put(counterBytes);
|
||||||
buffer.put(message);
|
buffer.put(message);
|
||||||
|
|
||||||
return buffer.array();
|
return buffer.array();
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getHighestMutuallySupportedVersion() {
|
public int getHighestMutuallySupportedVersion() {
|
||||||
return Math.min(SUPPORTED_VERSION, this.supportedVersion);
|
return Math.min(SUPPORTED_VERSION, this.supportedVersion);
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getSenderKeyId() {
|
public int getSenderKeyId() {
|
||||||
return this.senderKeyId;
|
return this.senderKeyId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getReceiverKeyId() {
|
public int getReceiverKeyId() {
|
||||||
return this.receiverKeyId;
|
return this.receiverKeyId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public PublicKey getNextKey() {
|
public PublicKey getNextKey() {
|
||||||
return this.nextKey;
|
return this.nextKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getCounter() {
|
public int getCounter() {
|
||||||
return this.counter;
|
return this.counter;
|
||||||
}
|
}
|
||||||
|
|
||||||
public byte[] getMessageText() {
|
public byte[] getMessageText() {
|
||||||
return this.message;
|
return this.message;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -31,6 +31,6 @@ public class Prefix {
|
|||||||
public static final String ASYMMETRIC_ENCRYPT = "?TextSecureAsymmetricEncrypt";
|
public static final String ASYMMETRIC_ENCRYPT = "?TextSecureAsymmetricEncrypt";
|
||||||
public static final String ASYMMETRIC_LOCAL_ENCRYPT = "?TextSecureAsymmetricLocalEncrypt";
|
public static final String ASYMMETRIC_LOCAL_ENCRYPT = "?TextSecureAsymmetricLocalEncrypt";
|
||||||
public static final String PROCESSED_KEY_EXCHANGE = "?TextSecureKeyExchangd";
|
public static final String PROCESSED_KEY_EXCHANGE = "?TextSecureKeyExchangd";
|
||||||
public static final String STALE_KEY_EXCHANGE = "?TextSecureKeyExchangs";
|
public static final String STALE_KEY_EXCHANGE = "?TextSecureKeyExchangs";
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Copyright (C) 2011 Whisper Systems
|
* Copyright (C) 2011 Whisper Systems
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
@ -10,22 +10,12 @@
|
|||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
* GNU General Public License for more details.
|
* GNU General Public License for more details.
|
||||||
*
|
*
|
||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
package org.thoughtcrime.securesms.providers;
|
package org.thoughtcrime.securesms.providers;
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileNotFoundException;
|
|
||||||
import java.io.FileOutputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.crypto.MasterSecret;
|
|
||||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
|
||||||
import org.thoughtcrime.securesms.service.KeyCachingService;
|
|
||||||
|
|
||||||
import android.content.BroadcastReceiver;
|
import android.content.BroadcastReceiver;
|
||||||
import android.content.ComponentName;
|
import android.content.ComponentName;
|
||||||
import android.content.ContentProvider;
|
import android.content.ContentProvider;
|
||||||
@ -41,6 +31,16 @@ import android.os.IBinder;
|
|||||||
import android.os.ParcelFileDescriptor;
|
import android.os.ParcelFileDescriptor;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
|
import org.thoughtcrime.securesms.crypto.MasterSecret;
|
||||||
|
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||||
|
import org.thoughtcrime.securesms.service.KeyCachingService;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
|
||||||
public class PartProvider extends ContentProvider {
|
public class PartProvider extends ContentProvider {
|
||||||
|
|
||||||
private static final String CONTENT_URI_STRING = "content://org.thoughtcrime.provider.securesms/part";
|
private static final String CONTENT_URI_STRING = "content://org.thoughtcrime.provider.securesms/part";
|
||||||
@ -48,7 +48,7 @@ public class PartProvider extends ContentProvider {
|
|||||||
private static final int SINGLE_ROW = 1;
|
private static final int SINGLE_ROW = 1;
|
||||||
|
|
||||||
private static final UriMatcher uriMatcher;
|
private static final UriMatcher uriMatcher;
|
||||||
|
|
||||||
static {
|
static {
|
||||||
uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
|
uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
|
||||||
uriMatcher.addURI("org.thoughtcrime.provider.securesms", "part/#", SINGLE_ROW);
|
uriMatcher.addURI("org.thoughtcrime.provider.securesms", "part/#", SINGLE_ROW);
|
||||||
@ -58,85 +58,84 @@ public class PartProvider extends ContentProvider {
|
|||||||
private NewKeyReceiver receiver;
|
private NewKeyReceiver receiver;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onCreate() {
|
public boolean onCreate() {
|
||||||
initializeMasterSecret();
|
initializeMasterSecret();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isAuthority(Uri uri) {
|
public static boolean isAuthority(Uri uri) {
|
||||||
return uriMatcher.match(uri) != -1;
|
return uriMatcher.match(uri) != -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
private File copyPartToTemporaryFile(MasterSecret masterSecret, long partId) throws IOException {
|
private File copyPartToTemporaryFile(MasterSecret masterSecret, long partId) throws IOException {
|
||||||
InputStream in = DatabaseFactory.getEncryptingPartDatabase(getContext(), masterSecret).getPartStream(partId);
|
InputStream in = DatabaseFactory.getEncryptingPartDatabase(getContext(), masterSecret).getPartStream(partId);
|
||||||
File tmpDir = getContext().getDir("tmp", 0);
|
File tmpDir = getContext().getDir("tmp", 0);
|
||||||
File tmpFile = File.createTempFile("test", ".jpg", tmpDir);
|
File tmpFile = File.createTempFile("test", ".jpg", tmpDir);
|
||||||
FileOutputStream fout = new FileOutputStream(tmpFile);
|
FileOutputStream fout = new FileOutputStream(tmpFile);
|
||||||
|
|
||||||
byte[] buffer = new byte[512];
|
byte[] buffer = new byte[512];
|
||||||
int read;
|
int read;
|
||||||
|
|
||||||
while ((read = in.read(buffer)) != -1)
|
while ((read = in.read(buffer)) != -1)
|
||||||
fout.write(buffer, 0, read);
|
fout.write(buffer, 0, read);
|
||||||
|
|
||||||
in.close();
|
in.close();
|
||||||
|
|
||||||
return tmpFile;
|
return tmpFile;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {
|
public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {
|
||||||
Log.w("PartProvider", "openFile() called!");
|
Log.w("PartProvider", "openFile() called!");
|
||||||
|
|
||||||
if (this.masterSecret == null)
|
if (this.masterSecret == null)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
switch (uriMatcher.match(uri)) {
|
switch (uriMatcher.match(uri)) {
|
||||||
case SINGLE_ROW:
|
case SINGLE_ROW:
|
||||||
Log.w("PartProvider", "Parting out a single row...");
|
Log.w("PartProvider", "Parting out a single row...");
|
||||||
try {
|
try {
|
||||||
int partId = Integer.parseInt(uri.getPathSegments().get(1));
|
int partId = Integer.parseInt(uri.getPathSegments().get(1));
|
||||||
File tmpFile = copyPartToTemporaryFile(masterSecret, partId);
|
File tmpFile = copyPartToTemporaryFile(masterSecret, partId);
|
||||||
ParcelFileDescriptor pdf = ParcelFileDescriptor.open(tmpFile, ParcelFileDescriptor.MODE_READ_ONLY);
|
ParcelFileDescriptor pdf = ParcelFileDescriptor.open(tmpFile, ParcelFileDescriptor.MODE_READ_ONLY);
|
||||||
tmpFile.delete();
|
tmpFile.delete();
|
||||||
return pdf;
|
return pdf;
|
||||||
} catch (IOException ioe) {
|
} catch (IOException ioe) {
|
||||||
Log.w("PartProvider", ioe);
|
Log.w("PartProvider", ioe);
|
||||||
throw new FileNotFoundException("Error opening file");
|
throw new FileNotFoundException("Error opening file");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new FileNotFoundException("Request for bad part.");
|
throw new FileNotFoundException("Request for bad part.");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int delete(Uri arg0, String arg1, String[] arg2) {
|
public int delete(Uri arg0, String arg1, String[] arg2) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getType(Uri arg0) {
|
public String getType(Uri arg0) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Uri insert(Uri arg0, ContentValues arg1) {
|
public Uri insert(Uri arg0, ContentValues arg1) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Cursor query(Uri arg0, String[] arg1, String arg2, String[] arg3, String arg4) {
|
public Cursor query(Uri arg0, String[] arg1, String arg2, String[] arg3, String arg4) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int update(Uri arg0, ContentValues arg1, String arg2, String[] arg3) {
|
public int update(Uri arg0, ContentValues arg1, String arg2, String[] arg3) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private void initializeWithMasterSecret(MasterSecret masterSecret) {
|
private void initializeWithMasterSecret(MasterSecret masterSecret) {
|
||||||
Log.w("PartProvider", "Got master secret: " + masterSecret);
|
Log.w("PartProvider", "Got master secret: " + masterSecret);
|
||||||
this.masterSecret = masterSecret;
|
this.masterSecret = masterSecret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -148,15 +147,15 @@ public class PartProvider extends ContentProvider {
|
|||||||
Intent bindIntent = new Intent(getContext(), KeyCachingService.class);
|
Intent bindIntent = new Intent(getContext(), KeyCachingService.class);
|
||||||
getContext().bindService(bindIntent, serviceConnection, Context.BIND_AUTO_CREATE);
|
getContext().bindService(bindIntent, serviceConnection, Context.BIND_AUTO_CREATE);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ServiceConnection serviceConnection = new ServiceConnection() {
|
private ServiceConnection serviceConnection = new ServiceConnection() {
|
||||||
public void onServiceConnected(ComponentName className, IBinder service) {
|
public void onServiceConnected(ComponentName className, IBinder service) {
|
||||||
KeyCachingService keyCachingService = ((KeyCachingService.KeyCachingBinder)service).getService();
|
KeyCachingService keyCachingService = ((KeyCachingService.KeyCachingBinder)service).getService();
|
||||||
MasterSecret masterSecret = keyCachingService.getMasterSecret();
|
MasterSecret masterSecret = keyCachingService.getMasterSecret();
|
||||||
|
|
||||||
initializeWithMasterSecret(masterSecret);
|
initializeWithMasterSecret(masterSecret);
|
||||||
|
|
||||||
PartProvider.this.getContext().unbindService(this);
|
PartProvider.this.getContext().unbindService(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onServiceDisconnected(ComponentName name) {}
|
public void onServiceDisconnected(ComponentName name) {}
|
||||||
@ -164,7 +163,7 @@ public class PartProvider extends ContentProvider {
|
|||||||
|
|
||||||
private class NewKeyReceiver extends BroadcastReceiver {
|
private class NewKeyReceiver extends BroadcastReceiver {
|
||||||
@Override
|
@Override
|
||||||
public void onReceive(Context context, Intent intent) {
|
public void onReceive(Context context, Intent intent) {
|
||||||
Log.w("SendReceiveService", "Got a MasterSecret broadcast...");
|
Log.w("SendReceiveService", "Got a MasterSecret broadcast...");
|
||||||
initializeWithMasterSecret((MasterSecret)intent.getParcelableExtra("master_secret"));
|
initializeWithMasterSecret((MasterSecret)intent.getParcelableExtra("master_secret"));
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Copyright (C) 2011 Whisper Systems
|
* Copyright (C) 2011 Whisper Systems
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
@ -10,7 +10,7 @@
|
|||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
* GNU General Public License for more details.
|
* GNU General Public License for more details.
|
||||||
*
|
*
|
||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
@ -20,15 +20,15 @@ public class RecipientFormattingException extends Exception {
|
|||||||
public RecipientFormattingException() {
|
public RecipientFormattingException() {
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
public RecipientFormattingException(String message) {
|
public RecipientFormattingException(String message) {
|
||||||
super(message);
|
super(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
public RecipientFormattingException(String message, Throwable nested) {
|
public RecipientFormattingException(String message, Throwable nested) {
|
||||||
super(message, nested);
|
super(message, nested);
|
||||||
}
|
}
|
||||||
|
|
||||||
public RecipientFormattingException(Throwable nested) {
|
public RecipientFormattingException(Throwable nested) {
|
||||||
super(nested);
|
super(nested);
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Copyright (C) 2011 Whisper Systems
|
* Copyright (C) 2011 Whisper Systems
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
@ -10,32 +10,32 @@
|
|||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
* GNU General Public License for more details.
|
* GNU General Public License for more details.
|
||||||
*
|
*
|
||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
package org.thoughtcrime.securesms.recipients;
|
package org.thoughtcrime.securesms.recipients;
|
||||||
|
|
||||||
|
import android.telephony.PhoneNumberUtils;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.StringTokenizer;
|
import java.util.StringTokenizer;
|
||||||
|
|
||||||
import android.telephony.PhoneNumberUtils;
|
|
||||||
import android.text.TextUtils;
|
|
||||||
|
|
||||||
public class RecipientsFormatter {
|
public class RecipientsFormatter {
|
||||||
|
|
||||||
private static String parseBracketedNumber(String recipient) throws RecipientFormattingException {
|
private static String parseBracketedNumber(String recipient) throws RecipientFormattingException {
|
||||||
int begin = recipient.indexOf('<');
|
int begin = recipient.indexOf('<');
|
||||||
int end = recipient.indexOf('>');
|
int end = recipient.indexOf('>');
|
||||||
String value = recipient.substring(begin + 1, end);
|
String value = recipient.substring(begin + 1, end);
|
||||||
|
|
||||||
if (PhoneNumberUtils.isWellFormedSmsAddress(value))
|
if (PhoneNumberUtils.isWellFormedSmsAddress(value))
|
||||||
return value;
|
return value;
|
||||||
else
|
else
|
||||||
throw new RecipientFormattingException("Bracketed value: " + value + " is not valid.");
|
throw new RecipientFormattingException("Bracketed value: " + value + " is not valid.");
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String parseRecipient(String recipient) throws RecipientFormattingException {
|
private static String parseRecipient(String recipient) throws RecipientFormattingException {
|
||||||
recipient = recipient.trim();
|
recipient = recipient.trim();
|
||||||
|
|
||||||
@ -44,21 +44,21 @@ public class RecipientsFormatter {
|
|||||||
|
|
||||||
if (PhoneNumberUtils.isWellFormedSmsAddress(recipient))
|
if (PhoneNumberUtils.isWellFormedSmsAddress(recipient))
|
||||||
return recipient;
|
return recipient;
|
||||||
|
|
||||||
throw new RecipientFormattingException("Recipient: " + recipient + " is badly formatted.");
|
throw new RecipientFormattingException("Recipient: " + recipient + " is badly formatted.");
|
||||||
}
|
}
|
||||||
|
|
||||||
public static List<String> getRecipients(String rawText) throws RecipientFormattingException {
|
public static List<String> getRecipients(String rawText) throws RecipientFormattingException {
|
||||||
ArrayList<String> results = new ArrayList<String>();
|
ArrayList<String> results = new ArrayList<String>();
|
||||||
StringTokenizer tokenizer = new StringTokenizer(rawText, ",");
|
StringTokenizer tokenizer = new StringTokenizer(rawText, ",");
|
||||||
|
|
||||||
while (tokenizer.hasMoreTokens()) {
|
while (tokenizer.hasMoreTokens()) {
|
||||||
results.add(parseRecipient(tokenizer.nextToken()));
|
results.add(parseRecipient(tokenizer.nextToken()));
|
||||||
}
|
}
|
||||||
|
|
||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String formatNameAndNumber(String name, String number) {
|
public static String formatNameAndNumber(String name, String number) {
|
||||||
// Format like this: Mike Cleron <(650) 555-1234>
|
// Format like this: Mike Cleron <(650) 555-1234>
|
||||||
// Erick Tseng <(650) 555-1212>
|
// Erick Tseng <(650) 555-1212>
|
||||||
@ -72,5 +72,5 @@ public class RecipientsFormatter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Copyright (C) 2011 Whisper Systems
|
* Copyright (C) 2011 Whisper Systems
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
@ -10,7 +10,7 @@
|
|||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
* GNU General Public License for more details.
|
* GNU General Public License for more details.
|
||||||
*
|
*
|
||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
@ -22,7 +22,7 @@ public class CharacterCalculator {
|
|||||||
|
|
||||||
public CharacterState calculateCharacters(int charactersSpent) {
|
public CharacterState calculateCharacters(int charactersSpent) {
|
||||||
int maxMessageSize;
|
int maxMessageSize;
|
||||||
|
|
||||||
if (charactersSpent <= SmsTransportDetails.SMS_SIZE) {
|
if (charactersSpent <= SmsTransportDetails.SMS_SIZE) {
|
||||||
maxMessageSize = SmsTransportDetails.SMS_SIZE;
|
maxMessageSize = SmsTransportDetails.SMS_SIZE;
|
||||||
} else {
|
} else {
|
||||||
@ -30,21 +30,21 @@ public class CharacterCalculator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int messagesSpent = charactersSpent / maxMessageSize;
|
int messagesSpent = charactersSpent / maxMessageSize;
|
||||||
|
|
||||||
if (((charactersSpent % maxMessageSize) > 0) || (messagesSpent == 0))
|
if (((charactersSpent % maxMessageSize) > 0) || (messagesSpent == 0))
|
||||||
messagesSpent++;
|
messagesSpent++;
|
||||||
|
|
||||||
int charactersRemaining = (maxMessageSize * messagesSpent) - charactersSpent;
|
int charactersRemaining = (maxMessageSize * messagesSpent) - charactersSpent;
|
||||||
|
|
||||||
return new CharacterState(messagesSpent, charactersRemaining, maxMessageSize);
|
return new CharacterState(messagesSpent, charactersRemaining, maxMessageSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public class CharacterState {
|
public class CharacterState {
|
||||||
public int charactersRemaining;
|
public int charactersRemaining;
|
||||||
public int messagesSpent;
|
public int messagesSpent;
|
||||||
public int maxMessageSize;
|
public int maxMessageSize;
|
||||||
|
|
||||||
public CharacterState(int messagesSpent, int charactersRemaining, int maxMessageSize) {
|
public CharacterState(int messagesSpent, int charactersRemaining, int maxMessageSize) {
|
||||||
this.messagesSpent = messagesSpent;
|
this.messagesSpent = messagesSpent;
|
||||||
this.charactersRemaining = charactersRemaining;
|
this.charactersRemaining = charactersRemaining;
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Copyright (C) 2011 Whisper Systems
|
* Copyright (C) 2011 Whisper Systems
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
@ -10,7 +10,7 @@
|
|||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
* GNU General Public License for more details.
|
* GNU General Public License for more details.
|
||||||
*
|
*
|
||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
@ -32,7 +32,7 @@ public class Combiner {
|
|||||||
System.arraycopy(c, 0, combined, a.length + b.length, c.length);
|
System.arraycopy(c, 0, combined, a.length + b.length, c.length);
|
||||||
return combined;
|
return combined;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static byte[] combine(byte[] a, byte[] b, byte[] c, byte[] d) {
|
public static byte[] combine(byte[] a, byte[] b, byte[] c, byte[] d) {
|
||||||
byte[] combined = new byte[a.length + b.length + c.length + d.length];
|
byte[] combined = new byte[a.length + b.length + c.length + d.length];
|
||||||
System.arraycopy(a, 0, combined, 0, a.length);
|
System.arraycopy(a, 0, combined, 0, a.length);
|
||||||
@ -41,5 +41,5 @@ public class Combiner {
|
|||||||
System.arraycopy(d, 0, combined, a.length + b.length + c.length, d.length);
|
System.arraycopy(d, 0, combined, a.length + b.length + c.length, d.length);
|
||||||
return combined;
|
return combined;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Copyright (C) 2011 Whisper Systems
|
* Copyright (C) 2011 Whisper Systems
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
@ -10,7 +10,7 @@
|
|||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
* GNU General Public License for more details.
|
* GNU General Public License for more details.
|
||||||
*
|
*
|
||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
@ -18,26 +18,26 @@ package org.thoughtcrime.securesms.util;
|
|||||||
|
|
||||||
public class Conversions {
|
public class Conversions {
|
||||||
|
|
||||||
public static byte intsToByteHighAndLow(int highValue, int lowValue) {
|
public static byte intsToByteHighAndLow(int highValue, int lowValue) {
|
||||||
return (byte)((highValue << 4 | lowValue) & 0xFF);
|
return (byte)((highValue << 4 | lowValue) & 0xFF);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int highBitsToInt(byte value) {
|
public static int highBitsToInt(byte value) {
|
||||||
return (value & 0xFF) >> 4;
|
return (value & 0xFF) >> 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int lowBitsToInt(byte value) {
|
public static int lowBitsToInt(byte value) {
|
||||||
return (value & 0xF);
|
return (value & 0xF);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int highBitsToMedium(int value) {
|
public static int highBitsToMedium(int value) {
|
||||||
return (value >> 12);
|
return (value >> 12);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int lowBitsToMedium(int value) {
|
public static int lowBitsToMedium(int value) {
|
||||||
return (value & 0xFFF);
|
return (value & 0xFFF);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static byte[] shortToByteArray(int value) {
|
public static byte[] shortToByteArray(int value) {
|
||||||
byte[] bytes = new byte[2];
|
byte[] bytes = new byte[2];
|
||||||
shortToByteArray(bytes, 0, value);
|
shortToByteArray(bytes, 0, value);
|
||||||
@ -61,7 +61,7 @@ public class Conversions {
|
|||||||
mediumToByteArray(bytes, 0, value);
|
mediumToByteArray(bytes, 0, value);
|
||||||
return bytes;
|
return bytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int mediumToByteArray(byte[] bytes, int offset, int value) {
|
public static int mediumToByteArray(byte[] bytes, int offset, int value) {
|
||||||
bytes[offset + 2] = (byte)value;
|
bytes[offset + 2] = (byte)value;
|
||||||
bytes[offset + 1] = (byte)(value >> 8);
|
bytes[offset + 1] = (byte)(value >> 8);
|
||||||
@ -108,12 +108,12 @@ public class Conversions {
|
|||||||
bytes[offset] = (byte)(value >> 56);
|
bytes[offset] = (byte)(value >> 56);
|
||||||
return 8;
|
return 8;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int longTo4ByteArray(byte[] bytes, int offset, long value) {
|
public static int longTo4ByteArray(byte[] bytes, int offset, long value) {
|
||||||
bytes[offset + 3] = (byte)value;
|
bytes[offset + 3] = (byte)value;
|
||||||
bytes[offset + 2] = (byte)(value >> 8);
|
bytes[offset + 2] = (byte)(value >> 8);
|
||||||
bytes[offset + 1] = (byte)(value >> 16);
|
bytes[offset + 1] = (byte)(value >> 16);
|
||||||
bytes[offset + 0] = (byte)(value >> 24);
|
bytes[offset + 0] = (byte)(value >> 24);
|
||||||
return 4;
|
return 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -145,7 +145,7 @@ public class Conversions {
|
|||||||
(bytes[offset + 2] & 0xff) << 8 |
|
(bytes[offset + 2] & 0xff) << 8 |
|
||||||
(bytes[offset + 3] & 0xff);
|
(bytes[offset + 3] & 0xff);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int byteArrayToIntLittleEndian(byte[] bytes, int offset) {
|
public static int byteArrayToIntLittleEndian(byte[] bytes, int offset) {
|
||||||
return
|
return
|
||||||
(bytes[offset + 3] & 0xff) << 24 |
|
(bytes[offset + 3] & 0xff) << 24 |
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Copyright (C) 2011 Whisper Systems
|
* Copyright (C) 2011 Whisper Systems
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
@ -10,16 +10,16 @@
|
|||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
* GNU General Public License for more details.
|
* GNU General Public License for more details.
|
||||||
*
|
*
|
||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
package org.thoughtcrime.securesms.util;
|
package org.thoughtcrime.securesms.util;
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.sms.SmsTransportDetails;
|
|
||||||
|
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
|
import org.thoughtcrime.securesms.sms.SmsTransportDetails;
|
||||||
|
|
||||||
public class EncryptedCharacterCalculator extends CharacterCalculator {
|
public class EncryptedCharacterCalculator extends CharacterCalculator {
|
||||||
|
|
||||||
private CharacterState calculateSingleRecordCharacters(int charactersSpent) {
|
private CharacterState calculateSingleRecordCharacters(int charactersSpent) {
|
||||||
@ -27,37 +27,37 @@ public class EncryptedCharacterCalculator extends CharacterCalculator {
|
|||||||
|
|
||||||
return new CharacterState(1, charactersRemaining, SmsTransportDetails.ENCRYPTED_SINGLE_MESSAGE_BODY_MAX_SIZE);
|
return new CharacterState(1, charactersRemaining, SmsTransportDetails.ENCRYPTED_SINGLE_MESSAGE_BODY_MAX_SIZE);
|
||||||
}
|
}
|
||||||
|
|
||||||
private CharacterState calculateMultiRecordCharacters(int charactersSpent) {
|
private CharacterState calculateMultiRecordCharacters(int charactersSpent) {
|
||||||
int charactersInFirstRecord = SmsTransportDetails.ENCRYPTED_SINGLE_MESSAGE_BODY_MAX_SIZE;
|
int charactersInFirstRecord = SmsTransportDetails.ENCRYPTED_SINGLE_MESSAGE_BODY_MAX_SIZE;
|
||||||
int spillover = charactersSpent - charactersInFirstRecord;
|
int spillover = charactersSpent - charactersInFirstRecord;
|
||||||
Log.w("EncryptedCharacterCalculator", "Spillover: " + spillover);
|
Log.w("EncryptedCharacterCalculator", "Spillover: " + spillover);
|
||||||
// int maxMultiMessageSize = SessionCipher.getMaxBodySizePerMultiMessage(charactersSpent);
|
// int maxMultiMessageSize = SessionCipher.getMaxBodySizePerMultiMessage(charactersSpent);
|
||||||
// Log.w("EncryptedCharacterCalculator", "Maxmultimessagesize: " + maxMultiMessageSize);
|
// Log.w("EncryptedCharacterCalculator", "Maxmultimessagesize: " + maxMultiMessageSize);
|
||||||
// int spilloverMessagesSpent = spillover / maxMultiMessageSize;
|
// int spilloverMessagesSpent = spillover / maxMultiMessageSize;
|
||||||
int spilloverMessagesSpent = spillover / SmsTransportDetails.MULTI_MESSAGE_MAX_BYTES;
|
int spilloverMessagesSpent = spillover / SmsTransportDetails.MULTI_MESSAGE_MAX_BYTES;
|
||||||
Log.w("EncryptedCharacterCalculator", "Spillover messaegs spent: " + spilloverMessagesSpent);
|
Log.w("EncryptedCharacterCalculator", "Spillover messaegs spent: " + spilloverMessagesSpent);
|
||||||
|
|
||||||
// if ((spillover % maxMultiMessageSize) > 0)
|
// if ((spillover % maxMultiMessageSize) > 0)
|
||||||
if ((spillover % SmsTransportDetails.MULTI_MESSAGE_MAX_BYTES) > 0)
|
if ((spillover % SmsTransportDetails.MULTI_MESSAGE_MAX_BYTES) > 0)
|
||||||
spilloverMessagesSpent++;
|
spilloverMessagesSpent++;
|
||||||
|
|
||||||
Log.w("EncryptedCharacterCalculator", "Spillover messaegs spent: " + spilloverMessagesSpent);
|
Log.w("EncryptedCharacterCalculator", "Spillover messaegs spent: " + spilloverMessagesSpent);
|
||||||
|
|
||||||
// int charactersRemaining = (maxMultiMessageSize * spilloverMessagesSpent) - spillover;
|
// int charactersRemaining = (maxMultiMessageSize * spilloverMessagesSpent) - spillover;
|
||||||
int charactersRemaining = (SmsTransportDetails.MULTI_MESSAGE_MAX_BYTES * spilloverMessagesSpent) - spillover;
|
int charactersRemaining = (SmsTransportDetails.MULTI_MESSAGE_MAX_BYTES * spilloverMessagesSpent) - spillover;
|
||||||
Log.w("EncryptedCharacterCalculator", "charactersRemaining: " + charactersRemaining);
|
Log.w("EncryptedCharacterCalculator", "charactersRemaining: " + charactersRemaining);
|
||||||
|
|
||||||
// return new CharacterState(spilloverMessagesSpent+1, charactersRemaining, maxMultiMessageSize);
|
// return new CharacterState(spilloverMessagesSpent+1, charactersRemaining, maxMultiMessageSize);
|
||||||
return new CharacterState(spilloverMessagesSpent+1, charactersRemaining, SmsTransportDetails.MULTI_MESSAGE_MAX_BYTES);
|
return new CharacterState(spilloverMessagesSpent+1, charactersRemaining, SmsTransportDetails.MULTI_MESSAGE_MAX_BYTES);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CharacterState calculateCharacters(int charactersSpent) {
|
public CharacterState calculateCharacters(int charactersSpent) {
|
||||||
if (charactersSpent <= SmsTransportDetails.ENCRYPTED_SINGLE_MESSAGE_BODY_MAX_SIZE){
|
if (charactersSpent <= SmsTransportDetails.ENCRYPTED_SINGLE_MESSAGE_BODY_MAX_SIZE){
|
||||||
return calculateSingleRecordCharacters(charactersSpent);
|
return calculateSingleRecordCharacters(charactersSpent);
|
||||||
} else {
|
} else {
|
||||||
return calculateMultiRecordCharacters(charactersSpent);
|
return calculateMultiRecordCharacters(charactersSpent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Copyright (C) 2011 Whisper Systems
|
* Copyright (C) 2011 Whisper Systems
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
@ -10,7 +10,7 @@
|
|||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
* GNU General Public License for more details.
|
* GNU General Public License for more details.
|
||||||
*
|
*
|
||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
@ -46,7 +46,7 @@ public class Hex {
|
|||||||
public static String dump(byte[] bytes) {
|
public static String dump(byte[] bytes) {
|
||||||
return dump(bytes, 0, bytes.length);
|
return dump(bytes, 0, bytes.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String dump(byte[] bytes, int offset, int length) {
|
public static String dump(byte[] bytes, int offset, int length) {
|
||||||
StringBuffer buf = new StringBuffer();
|
StringBuffer buf = new StringBuffer();
|
||||||
int lines = ((length - 1) / 16) + 1;
|
int lines = ((length - 1) / 16) + 1;
|
||||||
@ -59,7 +59,7 @@ public class Hex {
|
|||||||
appendDumpLine(buf, i, bytes, lineOffset, lineLength);
|
appendDumpLine(buf, i, bytes, lineOffset, lineLength);
|
||||||
buf.append(EOL);
|
buf.append(EOL);
|
||||||
}
|
}
|
||||||
|
|
||||||
return buf.toString();
|
return buf.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -78,12 +78,12 @@ public class Hex {
|
|||||||
int idx = i + lineOffset;
|
int idx = i + lineOffset;
|
||||||
if (i < lineLength) {
|
if (i < lineLength) {
|
||||||
int b = bytes[idx];
|
int b = bytes[idx];
|
||||||
appendHexChar(buf, b);
|
appendHexChar(buf, b);
|
||||||
} else {
|
} else {
|
||||||
buf.append(" ");
|
buf.append(" ");
|
||||||
}
|
}
|
||||||
if ((i % 2) == 1) {
|
if ((i % 2) == 1) {
|
||||||
buf.append(' ');
|
buf.append(' ');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Copyright (C) 2012 Whisper Systems
|
* Copyright (C) 2012 Whisper Systems
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
@ -10,33 +10,33 @@
|
|||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
* GNU General Public License for more details.
|
* GNU General Public License for more details.
|
||||||
*
|
*
|
||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
package org.thoughtcrime.securesms.util;
|
package org.thoughtcrime.securesms.util;
|
||||||
|
|
||||||
|
import android.telephony.PhoneNumberUtils;
|
||||||
|
|
||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
import android.telephony.PhoneNumberUtils;
|
|
||||||
|
|
||||||
public class NumberUtil {
|
public class NumberUtil {
|
||||||
|
|
||||||
private static final Pattern emailPattern = android.util.Patterns.EMAIL_ADDRESS;
|
private static final Pattern emailPattern = android.util.Patterns.EMAIL_ADDRESS;
|
||||||
|
|
||||||
public static boolean isValidEmail(String number) {
|
public static boolean isValidEmail(String number) {
|
||||||
Matcher matcher = emailPattern.matcher(number);
|
Matcher matcher = emailPattern.matcher(number);
|
||||||
return matcher.matches();
|
return matcher.matches();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isValidSmsOrEmail(String number) {
|
public static boolean isValidSmsOrEmail(String number) {
|
||||||
return PhoneNumberUtils.isWellFormedSmsAddress(number) || isValidEmail(number);
|
return PhoneNumberUtils.isWellFormedSmsAddress(number) || isValidEmail(number);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String filterNumber(String number) {
|
public static String filterNumber(String number) {
|
||||||
if (number == null) return null;
|
if (number == null) return null;
|
||||||
|
|
||||||
int length = number.length();
|
int length = number.length();
|
||||||
StringBuilder builder = new StringBuilder(length);
|
StringBuilder builder = new StringBuilder(length);
|
||||||
|
|
||||||
@ -44,10 +44,9 @@ public class NumberUtil {
|
|||||||
char character = number.charAt(i);
|
char character = number.charAt(i);
|
||||||
|
|
||||||
if (Character.isDigit(character) || character == '+')
|
if (Character.isDigit(character) || character == '+')
|
||||||
builder.append(character);
|
builder.append(character);
|
||||||
}
|
}
|
||||||
|
|
||||||
return builder.toString();
|
return builder.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Copyright (C) 2011 Whisper Systems
|
* Copyright (C) 2011 Whisper Systems
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
@ -10,7 +10,7 @@
|
|||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
* GNU General Public License for more details.
|
* GNU General Public License for more details.
|
||||||
*
|
*
|
||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
@ -19,5 +19,5 @@ package org.thoughtcrime.securesms.util;
|
|||||||
public interface RedPhoneCallTypes {
|
public interface RedPhoneCallTypes {
|
||||||
public static final int INCOMING = 1023;
|
public static final int INCOMING = 1023;
|
||||||
public static final int OUTGOING = 1024;
|
public static final int OUTGOING = 1024;
|
||||||
public static final int MISSED = 1025;
|
public static final int MISSED = 1025;
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Copyright (C) 2011 Whisper Systems
|
* Copyright (C) 2011 Whisper Systems
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
@ -10,7 +10,7 @@
|
|||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
* GNU General Public License for more details.
|
* GNU General Public License for more details.
|
||||||
*
|
*
|
||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
@ -25,65 +25,65 @@ public class Util {
|
|||||||
|
|
||||||
return combined;
|
return combined;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static byte[] combine(byte[] one, byte[] two, byte[] three) {
|
public static byte[] combine(byte[] one, byte[] two, byte[] three) {
|
||||||
byte[] combined = new byte[one.length + two.length + three.length];
|
byte[] combined = new byte[one.length + two.length + three.length];
|
||||||
System.arraycopy(one, 0, combined, 0, one.length);
|
System.arraycopy(one, 0, combined, 0, one.length);
|
||||||
System.arraycopy(two, 0, combined, one.length, two.length);
|
System.arraycopy(two, 0, combined, one.length, two.length);
|
||||||
System.arraycopy(three, 0, combined, one.length + two.length, three.length);
|
System.arraycopy(three, 0, combined, one.length + two.length, three.length);
|
||||||
|
|
||||||
return combined;
|
return combined;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static byte[] combine(byte[] one, byte[] two, byte[] three, byte[] four) {
|
public static byte[] combine(byte[] one, byte[] two, byte[] three, byte[] four) {
|
||||||
byte[] combined = new byte[one.length + two.length + three.length + four.length];
|
byte[] combined = new byte[one.length + two.length + three.length + four.length];
|
||||||
System.arraycopy(one, 0, combined, 0, one.length);
|
System.arraycopy(one, 0, combined, 0, one.length);
|
||||||
System.arraycopy(two, 0, combined, one.length, two.length);
|
System.arraycopy(two, 0, combined, one.length, two.length);
|
||||||
System.arraycopy(three, 0, combined, one.length + two.length, three.length);
|
System.arraycopy(three, 0, combined, one.length + two.length, three.length);
|
||||||
System.arraycopy(four, 0, combined, one.length + two.length + three.length, four.length);
|
System.arraycopy(four, 0, combined, one.length + two.length + three.length, four.length);
|
||||||
|
|
||||||
return combined;
|
return combined;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String[] splitString(String string, int maxLength) {
|
public static String[] splitString(String string, int maxLength) {
|
||||||
int count = string.length() / maxLength;
|
int count = string.length() / maxLength;
|
||||||
|
|
||||||
if (string.length() % maxLength > 0)
|
if (string.length() % maxLength > 0)
|
||||||
count++;
|
count++;
|
||||||
|
|
||||||
String[] splitString = new String[count];
|
String[] splitString = new String[count];
|
||||||
|
|
||||||
for (int i=0;i<count-1;i++)
|
for (int i=0;i<count-1;i++)
|
||||||
splitString[i] = string.substring(i*maxLength, (i*maxLength) + maxLength);
|
splitString[i] = string.substring(i*maxLength, (i*maxLength) + maxLength);
|
||||||
|
|
||||||
splitString[count-1] = string.substring((count-1) * maxLength);
|
splitString[count-1] = string.substring((count-1) * maxLength);
|
||||||
|
|
||||||
return splitString;
|
return splitString;
|
||||||
}
|
}
|
||||||
|
|
||||||
// public static Bitmap loadScaledBitmap(InputStream src, int targetWidth, int targetHeight) {
|
// public static Bitmap loadScaledBitmap(InputStream src, int targetWidth, int targetHeight) {
|
||||||
// return BitmapFactory.decodeStream(src);
|
// return BitmapFactory.decodeStream(src);
|
||||||
//// BitmapFactory.Options options = new BitmapFactory.Options();
|
//// BitmapFactory.Options options = new BitmapFactory.Options();
|
||||||
//// options.inJustDecodeBounds = true;
|
//// options.inJustDecodeBounds = true;
|
||||||
//// BitmapFactory.decodeStream(src, null, options);
|
//// BitmapFactory.decodeStream(src, null, options);
|
||||||
////
|
////
|
||||||
//// Log.w("Util", "Bitmap Origin Width: " + options.outWidth);
|
//// Log.w("Util", "Bitmap Origin Width: " + options.outWidth);
|
||||||
//// Log.w("Util", "Bitmap Origin Height: " + options.outHeight);
|
//// Log.w("Util", "Bitmap Origin Height: " + options.outHeight);
|
||||||
////
|
|
||||||
//// boolean scaleByHeight =
|
|
||||||
//// Math.abs(options.outHeight - targetHeight) >=
|
|
||||||
//// Math.abs(options.outWidth - targetWidth);
|
|
||||||
////
|
////
|
||||||
//// if (options.outHeight * options.outWidth >= targetWidth * targetHeight * 2) {
|
//// boolean scaleByHeight =
|
||||||
//// double sampleSize = scaleByHeight ? (double)options.outHeight / (double)targetHeight : (double)options.outWidth / (double)targetWidth;
|
//// Math.abs(options.outHeight - targetHeight) >=
|
||||||
////// options.inSampleSize = (int)Math.pow(2d, Math.floor(Math.log(sampleSize) / Math.log(2d)));
|
//// Math.abs(options.outWidth - targetWidth);
|
||||||
//// Log.w("Util", "Sampling by: " + options.inSampleSize);
|
|
||||||
//// }
|
|
||||||
////
|
////
|
||||||
//// options.inJustDecodeBounds = false;
|
//// if (options.outHeight * options.outWidth >= targetWidth * targetHeight * 2) {
|
||||||
////
|
//// double sampleSize = scaleByHeight ? (double)options.outHeight / (double)targetHeight : (double)options.outWidth / (double)targetWidth;
|
||||||
//// return BitmapFactory.decodeStream(src, null, options);
|
////// options.inSampleSize = (int)Math.pow(2d, Math.floor(Math.log(sampleSize) / Math.log(2d)));
|
||||||
// }
|
//// Log.w("Util", "Sampling by: " + options.inSampleSize);
|
||||||
|
//// }
|
||||||
|
////
|
||||||
|
//// options.inJustDecodeBounds = false;
|
||||||
|
////
|
||||||
|
//// return BitmapFactory.decodeStream(src, null, options);
|
||||||
|
// }
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Copyright (C) 2011 Whisper Systems
|
* Copyright (C) 2011 Whisper Systems
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
@ -10,7 +10,7 @@
|
|||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
* GNU General Public License for more details.
|
* GNU General Public License for more details.
|
||||||
*
|
*
|
||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
@ -19,27 +19,28 @@ package org.thoughtcrime.securesms.util;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class WorkerThread extends Thread {
|
public class WorkerThread extends Thread {
|
||||||
|
|
||||||
private final List<Runnable> workQueue;
|
private final List<Runnable> workQueue;
|
||||||
|
|
||||||
public WorkerThread(List<Runnable> workQueue, String name) {
|
public WorkerThread(List<Runnable> workQueue, String name) {
|
||||||
super(name);
|
super(name);
|
||||||
this.workQueue = workQueue;
|
this.workQueue = workQueue;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Runnable getWork() {
|
private Runnable getWork() {
|
||||||
synchronized (workQueue) {
|
synchronized (workQueue) {
|
||||||
try {
|
try {
|
||||||
while (workQueue.isEmpty())
|
while (workQueue.isEmpty())
|
||||||
workQueue.wait();
|
workQueue.wait();
|
||||||
|
|
||||||
return workQueue.remove(0);
|
return workQueue.remove(0);
|
||||||
} catch (InterruptedException ie) {
|
} catch (InterruptedException ie) {
|
||||||
throw new AssertionError(ie);
|
throw new AssertionError(ie);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
for (;;)
|
for (;;)
|
||||||
getWork().run();
|
getWork().run();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user