Removed tabs

This commit is contained in:
Moxie Marlinspike 2012-09-30 19:56:29 -07:00
parent 030b39cd9c
commit 82f8ba3584
35 changed files with 635 additions and 649 deletions

View File

@ -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) {

View File

@ -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);

View File

@ -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) {
} }
} }

View File

@ -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);

View File

@ -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));
} }

View File

@ -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);
} }
} }
} }

View File

@ -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);
} }

View File

@ -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);
} }
} }

View File

@ -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);

View File

@ -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);
} }
} }

View File

@ -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 "";
} }
} }
} }

View File

@ -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;
} }
} }
} }

View File

@ -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;
} }
} }

View File

@ -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 "";
} }
} }
} }

View File

@ -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);

View File

@ -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);
} }
} }

View File

@ -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);

View File

@ -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;
} }
} }

View File

@ -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;
} }
} }

View File

@ -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 {

View File

@ -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();
} }
} }

View File

@ -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;
} }
} }

View File

@ -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";
} }

View File

@ -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"));
} }

View File

@ -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);
} }

View File

@ -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 {
} }
} }
} }

View File

@ -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;

View File

@ -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;
} }
} }

View File

@ -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 |

View File

@ -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);
} }
} }
} }

View File

@ -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(' ');
} }
} }

View File

@ -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();
} }
} }

View File

@ -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;
} }

View File

@ -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);
// }
} }

View File

@ -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();