Migrate from SQLite and ciphertext blobs to SQLCipher + KeyStore

This commit is contained in:
Moxie Marlinspike 2018-01-24 19:17:44 -08:00
parent d1819b6361
commit f36b296e2e
134 changed files with 3633 additions and 3544 deletions

View File

@ -117,6 +117,7 @@ dependencies {
} }
compile 'com.codewaves.stickyheadergrid:stickyheadergrid:0.9.4' compile 'com.codewaves.stickyheadergrid:stickyheadergrid:0.9.4'
compile 'com.github.dmytrodanylyk.circular-progress-button:library:1.1.3-S2' compile 'com.github.dmytrodanylyk.circular-progress-button:library:1.1.3-S2'
compile 'net.zetetic:android-database-sqlcipher:3.5.9'
testCompile 'junit:junit:4.12' testCompile 'junit:junit:4.12'
testCompile 'org.assertj:assertj-core:1.7.1' testCompile 'org.assertj:assertj-core:1.7.1'
@ -183,6 +184,7 @@ dependencyVerification {
'com.annimon:stream:5da6e2e3e0551d61a3ea7014f04312276549e3dd739cf637996e4cf43c5535b9', 'com.annimon:stream:5da6e2e3e0551d61a3ea7014f04312276549e3dd739cf637996e4cf43c5535b9',
'com.takisoft.fix:colorpicker:f5d0dbabe406a1800498ca9c1faf34db36e021d8488bf10360f29961fe3ab0d1', 'com.takisoft.fix:colorpicker:f5d0dbabe406a1800498ca9c1faf34db36e021d8488bf10360f29961fe3ab0d1',
'com.github.dmytrodanylyk.circular-progress-button:library:8dc6a29a5a8db7b2ad5a9a7fda1dc9ae0893f4c8f0545732b2c63854ea693e8e', 'com.github.dmytrodanylyk.circular-progress-button:library:8dc6a29a5a8db7b2ad5a9a7fda1dc9ae0893f4c8f0545732b2c63854ea693e8e',
'net.zetetic:android-database-sqlcipher:eff93b3222f4bdc349ffee2d2e3b2a2507241f17435fb998947bcce486618f1d',
'com.google.android.gms:play-services-iid:54e919f9957b8b7820da7ee9b83471d00d0cac1cf08ddea8b5b41aea80bb1a70', 'com.google.android.gms:play-services-iid:54e919f9957b8b7820da7ee9b83471d00d0cac1cf08ddea8b5b41aea80bb1a70',
'com.google.android.gms:play-services-base:0ca636a8fc9a5af45e607cdcd61783bf5d561cbbb0f862021ce69606eee5ad49', 'com.google.android.gms:play-services-base:0ca636a8fc9a5af45e607cdcd61783bf5d561cbbb0f862021ce69606eee5ad49',
'com.google.android.gms:play-services-tasks:69ec265168e601d0203d04cd42e34bb019b2f029aa1e16fabd38a5153eea2086', 'com.google.android.gms:play-services-tasks:69ec265168e601d0203d04cd42e34bb019b2f029aa1e16fabd38a5153eea2086',

View File

@ -1,2 +1,5 @@
-keep class org.sqlite.** { *; } -keep class org.sqlite.** { *; }
-keep class org.sqlite.database.** { *; } -keep class org.sqlite.database.** { *; }
-keep class net.sqlcipher.** { *; }
-dontwarn net.sqlcipher.**

View File

@ -11,8 +11,7 @@ import java.util.Locale;
import java.util.Set; import java.util.Set;
public interface BindableConversationItem extends Unbindable { public interface BindableConversationItem extends Unbindable {
void bind(@NonNull MasterSecret masterSecret, void bind(@NonNull MessageRecord messageRecord,
@NonNull MessageRecord messageRecord,
@NonNull GlideRequests glideRequests, @NonNull GlideRequests glideRequests,
@NonNull Locale locale, @NonNull Locale locale,
@NonNull Set<MessageRecord> batchSelected, @NonNull Set<MessageRecord> batchSelected,

View File

@ -11,7 +11,7 @@ import java.util.Set;
public interface BindableConversationListItem extends Unbindable { public interface BindableConversationListItem extends Unbindable {
public void bind(@NonNull MasterSecret masterSecret, @NonNull ThreadRecord thread, public void bind(@NonNull ThreadRecord thread,
@NonNull GlideRequests glideRequests, @NonNull Locale locale, @NonNull GlideRequests glideRequests, @NonNull Locale locale,
@NonNull Set<Long> selectedThreads, boolean batchMode); @NonNull Set<Long> selectedThreads, boolean batchMode);
} }

View File

@ -1,5 +1,6 @@
package org.thoughtcrime.securesms; package org.thoughtcrime.securesms;
import android.annotation.SuppressLint;
import android.content.Context; import android.content.Context;
import android.content.DialogInterface; import android.content.DialogInterface;
import android.database.Cursor; import android.database.Cursor;
@ -10,7 +11,6 @@ import android.text.Spanned;
import android.text.method.LinkMovementMethod; import android.text.method.LinkMovementMethod;
import android.widget.TextView; import android.widget.TextView;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.crypto.storage.TextSecureIdentityKeyStore; import org.thoughtcrime.securesms.crypto.storage.TextSecureIdentityKeyStore;
import org.thoughtcrime.securesms.database.Address; import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.DatabaseFactory;
@ -35,12 +35,12 @@ import static org.whispersystems.libsignal.SessionCipher.SESSION_LOCK;
public class ConfirmIdentityDialog extends AlertDialog { public class ConfirmIdentityDialog extends AlertDialog {
@SuppressWarnings("unused")
private static final String TAG = ConfirmIdentityDialog.class.getSimpleName(); private static final String TAG = ConfirmIdentityDialog.class.getSimpleName();
private OnClickListener callback; private OnClickListener callback;
public ConfirmIdentityDialog(Context context, public ConfirmIdentityDialog(Context context,
MasterSecret masterSecret,
MessageRecord messageRecord, MessageRecord messageRecord,
IdentityKeyMismatch mismatch) IdentityKeyMismatch mismatch)
{ {
@ -59,7 +59,7 @@ public class ConfirmIdentityDialog extends AlertDialog {
setTitle(name); setTitle(name);
setMessage(spannableString); setMessage(spannableString);
setButton(AlertDialog.BUTTON_POSITIVE, context.getString(R.string.ConfirmIdentityDialog_accept), new AcceptListener(masterSecret, messageRecord, mismatch, recipient.getAddress())); setButton(AlertDialog.BUTTON_POSITIVE, context.getString(R.string.ConfirmIdentityDialog_accept), new AcceptListener(messageRecord, mismatch, recipient.getAddress()));
setButton(AlertDialog.BUTTON_NEGATIVE, context.getString(android.R.string.cancel), new CancelListener()); setButton(AlertDialog.BUTTON_NEGATIVE, context.getString(android.R.string.cancel), new CancelListener());
} }
@ -76,18 +76,17 @@ public class ConfirmIdentityDialog extends AlertDialog {
private class AcceptListener implements OnClickListener { private class AcceptListener implements OnClickListener {
private final MasterSecret masterSecret;
private final MessageRecord messageRecord; private final MessageRecord messageRecord;
private final IdentityKeyMismatch mismatch; private final IdentityKeyMismatch mismatch;
private final Address address; private final Address address;
private AcceptListener(MasterSecret masterSecret, MessageRecord messageRecord, IdentityKeyMismatch mismatch, Address address) { private AcceptListener(MessageRecord messageRecord, IdentityKeyMismatch mismatch, Address address) {
this.masterSecret = masterSecret;
this.messageRecord = messageRecord; this.messageRecord = messageRecord;
this.mismatch = mismatch; this.mismatch = mismatch;
this.address = address; this.address = address;
} }
@SuppressLint("StaticFieldLeak")
@Override @Override
public void onClick(DialogInterface dialog, int which) { public void onClick(DialogInterface dialog, int which) {
new AsyncTask<Void, Void, Void>() new AsyncTask<Void, Void, Void>()
@ -115,7 +114,7 @@ public class ConfirmIdentityDialog extends AlertDialog {
private void processPendingMessageRecords(long threadId, IdentityKeyMismatch mismatch) { private void processPendingMessageRecords(long threadId, IdentityKeyMismatch mismatch) {
MmsSmsDatabase mmsSmsDatabase = DatabaseFactory.getMmsSmsDatabase(getContext()); MmsSmsDatabase mmsSmsDatabase = DatabaseFactory.getMmsSmsDatabase(getContext());
Cursor cursor = mmsSmsDatabase.getIdentityConflictMessagesForThread(threadId); Cursor cursor = mmsSmsDatabase.getIdentityConflictMessagesForThread(threadId);
MmsSmsDatabase.Reader reader = mmsSmsDatabase.readerFor(cursor, masterSecret); MmsSmsDatabase.Reader reader = mmsSmsDatabase.readerFor(cursor);
MessageRecord record; MessageRecord record;
try { try {
@ -144,14 +143,14 @@ public class ConfirmIdentityDialog extends AlertDialog {
if (messageRecord.getRecipient().isPushGroupRecipient()) { if (messageRecord.getRecipient().isPushGroupRecipient()) {
MessageSender.resendGroupMessage(getContext(), messageRecord, mismatch.getAddress()); MessageSender.resendGroupMessage(getContext(), messageRecord, mismatch.getAddress());
} else { } else {
MessageSender.resend(getContext(), masterSecret, messageRecord); MessageSender.resend(getContext(), messageRecord);
} }
} else { } else {
smsDatabase.removeMismatchedIdentity(messageRecord.getId(), smsDatabase.removeMismatchedIdentity(messageRecord.getId(),
mismatch.getAddress(), mismatch.getAddress(),
mismatch.getIdentityKey()); mismatch.getIdentityKey());
MessageSender.resend(getContext(), masterSecret, messageRecord); MessageSender.resend(getContext(), messageRecord);
} }
} }

View File

@ -1,4 +1,4 @@
/** /*
* Copyright (C) 2015 Open Whisper Systems * Copyright (C) 2015 Open 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
@ -24,7 +24,6 @@ import android.support.v4.widget.SwipeRefreshLayout;
import android.util.Log; import android.util.Log;
import org.thoughtcrime.securesms.components.ContactFilterToolbar; import org.thoughtcrime.securesms.components.ContactFilterToolbar;
import org.thoughtcrime.securesms.components.ContactFilterToolbar.OnFilterChangedListener;
import org.thoughtcrime.securesms.crypto.MasterSecret; import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.util.DirectoryHelper; import org.thoughtcrime.securesms.util.DirectoryHelper;
import org.thoughtcrime.securesms.util.DynamicLanguage; import org.thoughtcrime.securesms.util.DynamicLanguage;
@ -53,7 +52,6 @@ public abstract class ContactSelectionActivity extends PassphraseRequiredActionB
protected ContactSelectionListFragment contactsFragment; protected ContactSelectionListFragment contactsFragment;
private MasterSecret masterSecret;
private ContactFilterToolbar toolbar; private ContactFilterToolbar toolbar;
@Override @Override
@ -64,7 +62,6 @@ public abstract class ContactSelectionActivity extends PassphraseRequiredActionB
@Override @Override
protected void onCreate(Bundle icicle, @NonNull MasterSecret masterSecret) { protected void onCreate(Bundle icicle, @NonNull MasterSecret masterSecret) {
this.masterSecret = masterSecret;
if (!getIntent().hasExtra(ContactSelectionListFragment.DISPLAY_MODE)) { if (!getIntent().hasExtra(ContactSelectionListFragment.DISPLAY_MODE)) {
getIntent().putExtra(ContactSelectionListFragment.DISPLAY_MODE, getIntent().putExtra(ContactSelectionListFragment.DISPLAY_MODE,
TextSecurePreferences.isSmsEnabled(this) TextSecurePreferences.isSmsEnabled(this)
@ -94,6 +91,7 @@ public abstract class ContactSelectionActivity extends PassphraseRequiredActionB
this.toolbar = ViewUtil.findById(this, R.id.toolbar); this.toolbar = ViewUtil.findById(this, R.id.toolbar);
setSupportActionBar(toolbar); setSupportActionBar(toolbar);
assert getSupportActionBar() != null;
getSupportActionBar().setDisplayHomeAsUpEnabled(false); getSupportActionBar().setDisplayHomeAsUpEnabled(false);
getSupportActionBar().setDisplayShowTitleEnabled(false); getSupportActionBar().setDisplayShowTitleEnabled(false);
getSupportActionBar().setIcon(null); getSupportActionBar().setIcon(null);
@ -107,11 +105,7 @@ public abstract class ContactSelectionActivity extends PassphraseRequiredActionB
} }
private void initializeSearch() { private void initializeSearch() {
toolbar.setOnFilterChangedListener(new OnFilterChangedListener() { toolbar.setOnFilterChangedListener(filter -> contactsFragment.setQueryFilter(filter));
@Override public void onFilterChanged(String filter) {
contactsFragment.setQueryFilter(filter);
}
});
} }
@Override @Override
@ -128,19 +122,16 @@ public abstract class ContactSelectionActivity extends PassphraseRequiredActionB
private static class RefreshDirectoryTask extends AsyncTask<Context, Void, Void> { private static class RefreshDirectoryTask extends AsyncTask<Context, Void, Void> {
private final WeakReference<ContactSelectionActivity> activity; private final WeakReference<ContactSelectionActivity> activity;
private final MasterSecret masterSecret;
private RefreshDirectoryTask(ContactSelectionActivity activity) { private RefreshDirectoryTask(ContactSelectionActivity activity) {
this.activity = new WeakReference<>(activity); this.activity = new WeakReference<>(activity);
this.masterSecret = activity.masterSecret;
} }
@Override @Override
protected Void doInBackground(Context... params) { protected Void doInBackground(Context... params) {
try { try {
DirectoryHelper.refreshDirectory(params[0], masterSecret, true); DirectoryHelper.refreshDirectory(params[0], true);
} catch (IOException e) { } catch (IOException e) {
Log.w(TAG, e); Log.w(TAG, e);
} }

View File

@ -48,7 +48,6 @@ import org.thoughtcrime.securesms.contacts.ContactsCursorLoader;
import org.thoughtcrime.securesms.database.CursorRecyclerViewAdapter; import org.thoughtcrime.securesms.database.CursorRecyclerViewAdapter;
import org.thoughtcrime.securesms.mms.GlideApp; import org.thoughtcrime.securesms.mms.GlideApp;
import org.thoughtcrime.securesms.permissions.Permissions; import org.thoughtcrime.securesms.permissions.Permissions;
import org.thoughtcrime.securesms.service.KeyCachingService;
import org.thoughtcrime.securesms.util.DirectoryHelper; import org.thoughtcrime.securesms.util.DirectoryHelper;
import org.thoughtcrime.securesms.util.StickyHeaderDecoration; import org.thoughtcrime.securesms.util.StickyHeaderDecoration;
import org.thoughtcrime.securesms.util.TextSecurePreferences; import org.thoughtcrime.securesms.util.TextSecurePreferences;
@ -221,7 +220,7 @@ public class ContactSelectionListFragment extends Fragment
@Override @Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) { public Loader<Cursor> onCreateLoader(int id, Bundle args) {
return new ContactsCursorLoader(getActivity(), KeyCachingService.getMasterSecret(getContext()), return new ContactsCursorLoader(getActivity(),
getActivity().getIntent().getIntExtra(DISPLAY_MODE, DISPLAY_MODE_ALL), getActivity().getIntent().getIntExtra(DISPLAY_MODE, DISPLAY_MODE_ALL),
cursorFilter, getActivity().getIntent().getBooleanExtra(RECENTS, false)); cursorFilter, getActivity().getIntent().getBooleanExtra(RECENTS, false));
} }
@ -263,7 +262,7 @@ public class ContactSelectionListFragment extends Fragment
@Override @Override
protected Boolean doInBackground(Void... voids) { protected Boolean doInBackground(Void... voids) {
try { try {
DirectoryHelper.refreshDirectory(getContext(), null, false); DirectoryHelper.refreshDirectory(getContext(), false);
return true; return true;
} catch (IOException e) { } catch (IOException e) {
Log.w(TAG, e); Log.w(TAG, e);

View File

@ -97,7 +97,6 @@ import org.thoughtcrime.securesms.components.reminder.UnauthorizedReminder;
import org.thoughtcrime.securesms.contacts.ContactAccessor; import org.thoughtcrime.securesms.contacts.ContactAccessor;
import org.thoughtcrime.securesms.contacts.ContactAccessor.ContactData; import org.thoughtcrime.securesms.contacts.ContactAccessor.ContactData;
import org.thoughtcrime.securesms.crypto.IdentityKeyParcelable; import org.thoughtcrime.securesms.crypto.IdentityKeyParcelable;
import org.thoughtcrime.securesms.crypto.MasterCipher;
import org.thoughtcrime.securesms.crypto.MasterSecret; import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.crypto.SecurityEvent; import org.thoughtcrime.securesms.crypto.SecurityEvent;
import org.thoughtcrime.securesms.database.Address; import org.thoughtcrime.securesms.database.Address;
@ -214,7 +213,6 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
private static final int PICK_GIF = 9; private static final int PICK_GIF = 9;
private static final int SMS_DEFAULT = 10; private static final int SMS_DEFAULT = 10;
private MasterSecret masterSecret;
private GlideRequests glideRequests; private GlideRequests glideRequests;
protected ComposeText composeText; protected ComposeText composeText;
private AnimatingToggle buttonToggle; private AnimatingToggle buttonToggle;
@ -263,7 +261,6 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
@Override @Override
protected void onCreate(Bundle state, @NonNull MasterSecret masterSecret) { protected void onCreate(Bundle state, @NonNull MasterSecret masterSecret) {
Log.w(TAG, "onCreate()"); Log.w(TAG, "onCreate()");
this.masterSecret = masterSecret;
supportRequestWindowFeature(WindowCompat.FEATURE_ACTION_BAR_OVERLAY); supportRequestWindowFeature(WindowCompat.FEATURE_ACTION_BAR_OVERLAY);
setContentView(R.layout.conversation_activity); setContentView(R.layout.conversation_activity);
@ -440,7 +437,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
break; break;
case PICK_LOCATION: case PICK_LOCATION:
SignalPlace place = new SignalPlace(PlacePicker.getPlace(data, this)); SignalPlace place = new SignalPlace(PlacePicker.getPlace(data, this));
attachmentManager.setLocation(masterSecret, place, getCurrentMediaConstraints()); attachmentManager.setLocation(place, getCurrentMediaConstraints());
break; break;
case PICK_GIF: case PICK_GIF:
setMedia(data.getData(), MediaType.GIF); setMedia(data.getData(), MediaType.GIF);
@ -595,7 +592,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
protected Void doInBackground(Void... params) { protected Void doInBackground(Void... params) {
DatabaseFactory.getRecipientDatabase(ConversationActivity.this).setExpireMessages(recipient, expirationTime); DatabaseFactory.getRecipientDatabase(ConversationActivity.this).setExpireMessages(recipient, expirationTime);
OutgoingExpirationUpdateMessage outgoingMessage = new OutgoingExpirationUpdateMessage(getRecipient(), System.currentTimeMillis(), expirationTime * 1000); OutgoingExpirationUpdateMessage outgoingMessage = new OutgoingExpirationUpdateMessage(getRecipient(), System.currentTimeMillis(), expirationTime * 1000);
MessageSender.send(ConversationActivity.this, masterSecret, outgoingMessage, threadId, false, null); MessageSender.send(ConversationActivity.this, outgoingMessage, threadId, false, null);
return null; return null;
} }
@ -681,7 +678,6 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
private void handleRegisterForSignal() { private void handleRegisterForSignal() {
Intent intent = new Intent(this, RegistrationActivity.class); Intent intent = new Intent(this, RegistrationActivity.class);
intent.putExtra("cancel_button", true); intent.putExtra("cancel_button", true);
intent.putExtra("master_secret", masterSecret);
startActivity(intent); startActivity(intent);
} }
@ -723,7 +719,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
new AsyncTask<OutgoingEndSessionMessage, Void, Long>() { new AsyncTask<OutgoingEndSessionMessage, Void, Long>() {
@Override @Override
protected Long doInBackground(OutgoingEndSessionMessage... messages) { protected Long doInBackground(OutgoingEndSessionMessage... messages) {
return MessageSender.send(context, masterSecret, messages[0], threadId, false, null); return MessageSender.send(context, messages[0], threadId, false, null);
} }
@Override @Override
@ -768,7 +764,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
.build(); .build();
OutgoingGroupMediaMessage outgoingMessage = new OutgoingGroupMediaMessage(getRecipient(), context, null, System.currentTimeMillis(), 0); OutgoingGroupMediaMessage outgoingMessage = new OutgoingGroupMediaMessage(getRecipient(), context, null, System.currentTimeMillis(), 0);
MessageSender.send(self, masterSecret, outgoingMessage, threadId, false, null); MessageSender.send(self, outgoingMessage, threadId, false, null);
DatabaseFactory.getGroupDatabase(self).remove(groupId, Address.fromSerialized(TextSecurePreferences.getLocalNumber(self))); DatabaseFactory.getGroupDatabase(self).remove(groupId, Address.fromSerialized(TextSecurePreferences.getLocalNumber(self)));
initializeEnabledCheck(); initializeEnabledCheck();
} catch (IOException e) { } catch (IOException e) {
@ -1006,9 +1002,8 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
new AsyncTask<Void, Void, List<Draft>>() { new AsyncTask<Void, Void, List<Draft>>() {
@Override @Override
protected List<Draft> doInBackground(Void... params) { protected List<Draft> doInBackground(Void... params) {
MasterCipher masterCipher = new MasterCipher(masterSecret);
DraftDatabase draftDatabase = DatabaseFactory.getDraftDatabase(ConversationActivity.this); DraftDatabase draftDatabase = DatabaseFactory.getDraftDatabase(ConversationActivity.this);
List<Draft> results = draftDatabase.getDrafts(masterCipher, threadId); List<Draft> results = draftDatabase.getDrafts(threadId);
draftDatabase.clearDrafts(threadId); draftDatabase.clearDrafts(threadId);
@ -1024,7 +1019,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
composeText.setText(draft.getValue()); composeText.setText(draft.getValue());
break; break;
case Draft.LOCATION: case Draft.LOCATION:
attachmentManager.setLocation(masterSecret, SignalPlace.deserialize(draft.getValue()), getCurrentMediaConstraints()); attachmentManager.setLocation(SignalPlace.deserialize(draft.getValue()), getCurrentMediaConstraints());
break; break;
case Draft.IMAGE: case Draft.IMAGE:
setMedia(Uri.parse(draft.getValue()), MediaType.IMAGE); setMedia(Uri.parse(draft.getValue()), MediaType.IMAGE);
@ -1078,7 +1073,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
if (registeredState == RegisteredState.UNKNOWN) { if (registeredState == RegisteredState.UNKNOWN) {
try { try {
Log.w(TAG, "Refreshing directory for user: " + recipient.getAddress().serialize()); Log.w(TAG, "Refreshing directory for user: " + recipient.getAddress().serialize());
registeredState = DirectoryHelper.refreshDirectoryFor(context, masterSecret, recipient); registeredState = DirectoryHelper.refreshDirectoryFor(context, recipient);
} catch (IOException e) { } catch (IOException e) {
Log.w(TAG, e); Log.w(TAG, e);
} }
@ -1246,7 +1241,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
attachmentTypeSelector = null; attachmentTypeSelector = null;
attachmentManager = new AttachmentManager(this, this); attachmentManager = new AttachmentManager(this, this);
audioRecorder = new AudioRecorder(this, masterSecret); audioRecorder = new AudioRecorder(this);
SendButtonListener sendButtonListener = new SendButtonListener(); SendButtonListener sendButtonListener = new SendButtonListener();
ComposeKeyPressedListener composeKeyPressedListener = new ComposeKeyPressedListener(); ComposeKeyPressedListener composeKeyPressedListener = new ComposeKeyPressedListener();
@ -1385,7 +1380,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
private void setMedia(@Nullable Uri uri, @NonNull MediaType mediaType) { private void setMedia(@Nullable Uri uri, @NonNull MediaType mediaType) {
if (uri == null) return; if (uri == null) return;
attachmentManager.setMedia(masterSecret, glideRequests, uri, mediaType, getCurrentMediaConstraints()); attachmentManager.setMedia(glideRequests, uri, mediaType, getCurrentMediaConstraints());
} }
private void addAttachmentContactInfo(Uri contactUri) { private void addAttachmentContactInfo(Uri contactUri) {
@ -1440,7 +1435,6 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
final Drafts drafts = getDraftsForCurrentState(); final Drafts drafts = getDraftsForCurrentState();
final long thisThreadId = this.threadId; final long thisThreadId = this.threadId;
final MasterSecret thisMasterSecret = this.masterSecret.parcelClone();
final int thisDistributionType = this.distributionType; final int thisDistributionType = this.distributionType;
new AsyncTask<Long, Void, Long>() { new AsyncTask<Long, Void, Long>() {
@ -1453,9 +1447,9 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
if (drafts.size() > 0) { if (drafts.size() > 0) {
if (threadId == -1) threadId = threadDatabase.getThreadIdFor(getRecipient(), thisDistributionType); if (threadId == -1) threadId = threadDatabase.getThreadIdFor(getRecipient(), thisDistributionType);
draftDatabase.insertDrafts(new MasterCipher(thisMasterSecret), threadId, drafts); draftDatabase.insertDrafts(threadId, drafts);
threadDatabase.updateSnippet(threadId, drafts.getSnippet(ConversationActivity.this), threadDatabase.updateSnippet(threadId, drafts.getSnippet(ConversationActivity.this),
drafts.getUriSnippet(ConversationActivity.this), drafts.getUriSnippet(),
System.currentTimeMillis(), Types.BASE_DRAFT_TYPE, true); System.currentTimeMillis(), Types.BASE_DRAFT_TYPE, true);
} else if (threadId > 0) { } else if (threadId > 0) {
threadDatabase.update(threadId, false); threadDatabase.update(threadId, false);
@ -1588,7 +1582,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
Context context = ConversationActivity.this; Context context = ConversationActivity.this;
List<MarkedMessageInfo> messageIds = DatabaseFactory.getThreadDatabase(context).setRead(params[0], false); List<MarkedMessageInfo> messageIds = DatabaseFactory.getThreadDatabase(context).setRead(params[0], false);
MessageNotifier.updateNotification(context, masterSecret); MessageNotifier.updateNotification(context);
MarkReadReceiver.process(context, messageIds); MarkReadReceiver.process(context, messageIds);
return null; return null;
@ -1701,7 +1695,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
DatabaseFactory.getRecipientDatabase(context).setProfileSharing(recipient, true); DatabaseFactory.getRecipientDatabase(context).setProfileSharing(recipient, true);
} }
return MessageSender.send(context, masterSecret, outgoingMessage, threadId, forceSms, () -> fragment.releaseOutgoingMessage(id)); return MessageSender.send(context, outgoingMessage, threadId, forceSms, () -> fragment.releaseOutgoingMessage(id));
} }
@Override @Override
@ -1746,7 +1740,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
DatabaseFactory.getRecipientDatabase(context).setProfileSharing(recipient, true); DatabaseFactory.getRecipientDatabase(context).setProfileSharing(recipient, true);
} }
return MessageSender.send(context, masterSecret, messages[0], threadId, forceSms, () -> fragment.releaseOutgoingMessage(id)); return MessageSender.send(context, messages[0], threadId, forceSms, () -> fragment.releaseOutgoingMessage(id));
} }
@Override @Override
@ -1799,7 +1793,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
@Override @Override
public void onImageCapture(@NonNull final byte[] imageBytes) { public void onImageCapture(@NonNull final byte[] imageBytes) {
setMedia(PersistentBlobProvider.getInstance(this) setMedia(PersistentBlobProvider.getInstance(this)
.create(masterSecret, imageBytes, MediaUtil.IMAGE_JPEG, null), .create(this, imageBytes, MediaUtil.IMAGE_JPEG, null),
MediaType.IMAGE); MediaType.IMAGE);
quickAttachmentDrawer.hide(false); quickAttachmentDrawer.hide(false);
} }
@ -1862,7 +1856,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
new AsyncTask<Void, Void, Void>() { new AsyncTask<Void, Void, Void>() {
@Override @Override
protected Void doInBackground(Void... params) { protected Void doInBackground(Void... params) {
PersistentBlobProvider.getInstance(ConversationActivity.this).delete(result.first); PersistentBlobProvider.getInstance(ConversationActivity.this).delete(ConversationActivity.this, result.first);
return null; return null;
} }
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
@ -1891,7 +1885,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
new AsyncTask<Void, Void, Void>() { new AsyncTask<Void, Void, Void>() {
@Override @Override
protected Void doInBackground(Void... params) { protected Void doInBackground(Void... params) {
PersistentBlobProvider.getInstance(ConversationActivity.this).delete(result.first); PersistentBlobProvider.getInstance(ConversationActivity.this).delete(ConversationActivity.this, result.first);
return null; return null;
} }
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);

View File

@ -26,13 +26,10 @@ import android.support.v7.widget.RecyclerView;
import android.util.Log; import android.util.Log;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnLongClickListener;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.TextView; import android.widget.TextView;
import org.thoughtcrime.securesms.ConversationAdapter.HeaderViewHolder; import org.thoughtcrime.securesms.ConversationAdapter.HeaderViewHolder;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.database.AttachmentDatabase; import org.thoughtcrime.securesms.database.AttachmentDatabase;
import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.FastCursorRecyclerViewAdapter; import org.thoughtcrime.securesms.database.FastCursorRecyclerViewAdapter;
@ -92,7 +89,6 @@ public class ConversationAdapter <V extends View & BindableConversationItem>
private final Set<MessageRecord> batchSelected = Collections.synchronizedSet(new HashSet<MessageRecord>()); private final Set<MessageRecord> batchSelected = Collections.synchronizedSet(new HashSet<MessageRecord>());
private final @Nullable ItemClickListener clickListener; private final @Nullable ItemClickListener clickListener;
private final @NonNull MasterSecret masterSecret;
private final @NonNull GlideRequests glideRequests; private final @NonNull GlideRequests glideRequests;
private final @NonNull Locale locale; private final @NonNull Locale locale;
private final @NonNull Recipient recipient; private final @NonNull Recipient recipient;
@ -142,7 +138,6 @@ public class ConversationAdapter <V extends View & BindableConversationItem>
ConversationAdapter(Context context, Cursor cursor) { ConversationAdapter(Context context, Cursor cursor) {
super(context, cursor); super(context, cursor);
try { try {
this.masterSecret = null;
this.glideRequests = null; this.glideRequests = null;
this.locale = null; this.locale = null;
this.clickListener = null; this.clickListener = null;
@ -157,7 +152,6 @@ public class ConversationAdapter <V extends View & BindableConversationItem>
} }
public ConversationAdapter(@NonNull Context context, public ConversationAdapter(@NonNull Context context,
@NonNull MasterSecret masterSecret,
@NonNull GlideRequests glideRequests, @NonNull GlideRequests glideRequests,
@NonNull Locale locale, @NonNull Locale locale,
@Nullable ItemClickListener clickListener, @Nullable ItemClickListener clickListener,
@ -167,7 +161,6 @@ public class ConversationAdapter <V extends View & BindableConversationItem>
super(context, cursor); super(context, cursor);
try { try {
this.masterSecret = masterSecret;
this.glideRequests = glideRequests; this.glideRequests = glideRequests;
this.locale = locale; this.locale = locale;
this.clickListener = clickListener; this.clickListener = clickListener;
@ -193,7 +186,7 @@ public class ConversationAdapter <V extends View & BindableConversationItem>
@Override @Override
protected void onBindItemViewHolder(ViewHolder viewHolder, @NonNull MessageRecord messageRecord) { protected void onBindItemViewHolder(ViewHolder viewHolder, @NonNull MessageRecord messageRecord) {
long start = System.currentTimeMillis(); long start = System.currentTimeMillis();
viewHolder.getView().bind(masterSecret, messageRecord, glideRequests, locale, batchSelected, recipient); viewHolder.getView().bind(messageRecord, glideRequests, locale, batchSelected, recipient);
Log.w(TAG, "Bind time: " + (System.currentTimeMillis() - start)); Log.w(TAG, "Bind time: " + (System.currentTimeMillis() - start));
} }
@ -302,7 +295,7 @@ public class ConversationAdapter <V extends View & BindableConversationItem>
if (record != null) return record; if (record != null) return record;
} }
final MessageRecord messageRecord = db.readerFor(cursor, masterSecret).getCurrent(); final MessageRecord messageRecord = db.readerFor(cursor).getCurrent();
messageRecordCache.put(type + messageId, new SoftReference<>(messageRecord)); messageRecordCache.put(type + messageId, new SoftReference<>(messageRecord));
return messageRecord; return messageRecord;

View File

@ -52,7 +52,6 @@ import android.widget.Toast;
import org.thoughtcrime.securesms.ConversationAdapter.HeaderViewHolder; import org.thoughtcrime.securesms.ConversationAdapter.HeaderViewHolder;
import org.thoughtcrime.securesms.ConversationAdapter.ItemClickListener; import org.thoughtcrime.securesms.ConversationAdapter.ItemClickListener;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.MmsSmsDatabase; import org.thoughtcrime.securesms.database.MmsSmsDatabase;
import org.thoughtcrime.securesms.database.RecipientDatabase; import org.thoughtcrime.securesms.database.RecipientDatabase;
@ -92,7 +91,6 @@ public class ConversationFragment extends Fragment
private ConversationFragmentListener listener; private ConversationFragmentListener listener;
private MasterSecret masterSecret;
private Recipient recipient; private Recipient recipient;
private long threadId; private long threadId;
private long lastSeen; private long lastSeen;
@ -110,12 +108,11 @@ public class ConversationFragment extends Fragment
@Override @Override
public void onCreate(Bundle icicle) { public void onCreate(Bundle icicle) {
super.onCreate(icicle); super.onCreate(icicle);
this.masterSecret = getArguments().getParcelable("master_secret");
this.locale = (Locale) getArguments().getSerializable(PassphraseRequiredActionBarActivity.LOCALE_EXTRA); this.locale = (Locale) getArguments().getSerializable(PassphraseRequiredActionBarActivity.LOCALE_EXTRA);
} }
@Override @Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle bundle) { public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle bundle) {
final View view = inflater.inflate(R.layout.conversation_fragment, container, false); final View view = inflater.inflate(R.layout.conversation_fragment, container, false);
list = ViewUtil.findById(view, android.R.id.list); list = ViewUtil.findById(view, android.R.id.list);
composeDivider = ViewUtil.findById(view, R.id.compose_divider); composeDivider = ViewUtil.findById(view, R.id.compose_divider);
@ -192,7 +189,7 @@ public class ConversationFragment extends Fragment
private void initializeListAdapter() { private void initializeListAdapter() {
if (this.recipient != null && this.threadId != -1) { if (this.recipient != null && this.threadId != -1) {
ConversationAdapter adapter = new ConversationAdapter(getActivity(), masterSecret, GlideApp.with(this), locale, selectionClickListener, null, this.recipient); ConversationAdapter adapter = new ConversationAdapter(getActivity(), GlideApp.with(this), locale, selectionClickListener, null, this.recipient);
list.setAdapter(adapter); list.setAdapter(adapter);
list.addItemDecoration(new StickyHeaderDecoration(adapter, false, false)); list.addItemDecoration(new StickyHeaderDecoration(adapter, false, false));
@ -352,7 +349,6 @@ public class ConversationFragment extends Fragment
private void handleDisplayDetails(MessageRecord message) { private void handleDisplayDetails(MessageRecord message) {
Intent intent = new Intent(getActivity(), MessageDetailsActivity.class); Intent intent = new Intent(getActivity(), MessageDetailsActivity.class);
intent.putExtra(MessageDetailsActivity.MASTER_SECRET_EXTRA, masterSecret);
intent.putExtra(MessageDetailsActivity.MESSAGE_ID_EXTRA, message.getId()); intent.putExtra(MessageDetailsActivity.MESSAGE_ID_EXTRA, message.getId());
intent.putExtra(MessageDetailsActivity.THREAD_ID_EXTRA, threadId); intent.putExtra(MessageDetailsActivity.THREAD_ID_EXTRA, threadId);
intent.putExtra(MessageDetailsActivity.TYPE_EXTRA, message.isMms() ? MmsSmsDatabase.MMS_TRANSPORT : MmsSmsDatabase.SMS_TRANSPORT); intent.putExtra(MessageDetailsActivity.TYPE_EXTRA, message.isMms() ? MmsSmsDatabase.MMS_TRANSPORT : MmsSmsDatabase.SMS_TRANSPORT);
@ -380,7 +376,7 @@ public class ConversationFragment extends Fragment
new AsyncTask<MessageRecord, Void, Void>() { new AsyncTask<MessageRecord, Void, Void>() {
@Override @Override
protected Void doInBackground(MessageRecord... messageRecords) { protected Void doInBackground(MessageRecord... messageRecords) {
MessageSender.resend(context, masterSecret, messageRecords[0]); MessageSender.resend(context, messageRecords[0]);
return null; return null;
} }
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, message); }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, message);
@ -391,7 +387,7 @@ public class ConversationFragment extends Fragment
public void onClick(DialogInterface dialog, int which) { public void onClick(DialogInterface dialog, int which) {
for (Slide slide : message.getSlideDeck().getSlides()) { for (Slide slide : message.getSlideDeck().getSlides()) {
if ((slide.hasImage() || slide.hasVideo() || slide.hasAudio() || slide.hasDocument()) && slide.getUri() != null) { if ((slide.hasImage() || slide.hasVideo() || slide.hasAudio() || slide.hasDocument()) && slide.getUri() != null) {
SaveAttachmentTask saveTask = new SaveAttachmentTask(getActivity(), masterSecret); SaveAttachmentTask saveTask = new SaveAttachmentTask(getActivity());
saveTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, new Attachment(slide.getUri(), slide.getContentType(), message.getDateReceived(), slide.getFileName().orNull())); saveTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, new Attachment(slide.getUri(), slide.getContentType(), message.getDateReceived(), slide.getFileName().orNull()));
return; return;
} }

View File

@ -184,8 +184,7 @@ public class ConversationItem extends LinearLayout
} }
@Override @Override
public void bind(@NonNull MasterSecret masterSecret, public void bind(@NonNull MessageRecord messageRecord,
@NonNull MessageRecord messageRecord,
@NonNull GlideRequests glideRequests, @NonNull GlideRequests glideRequests,
@NonNull Locale locale, @NonNull Locale locale,
@NonNull Set<MessageRecord> batchSelected, @NonNull Set<MessageRecord> batchSelected,
@ -366,7 +365,7 @@ public class ConversationItem extends LinearLayout
if (documentViewStub.resolved()) documentViewStub.get().setVisibility(View.GONE); if (documentViewStub.resolved()) documentViewStub.get().setVisibility(View.GONE);
//noinspection ConstantConditions //noinspection ConstantConditions
audioViewStub.get().setAudio(masterSecret, ((MediaMmsMessageRecord) messageRecord).getSlideDeck().getAudioSlide(), showControls); audioViewStub.get().setAudio(((MediaMmsMessageRecord) messageRecord).getSlideDeck().getAudioSlide(), showControls);
audioViewStub.get().setDownloadClickListener(downloadClickListener); audioViewStub.get().setDownloadClickListener(downloadClickListener);
audioViewStub.get().setOnLongClickListener(passthroughClickListener); audioViewStub.get().setOnLongClickListener(passthroughClickListener);
@ -389,7 +388,7 @@ public class ConversationItem extends LinearLayout
if (documentViewStub.resolved()) documentViewStub.get().setVisibility(View.GONE); if (documentViewStub.resolved()) documentViewStub.get().setVisibility(View.GONE);
//noinspection ConstantConditions //noinspection ConstantConditions
mediaThumbnailStub.get().setImageResource(masterSecret, glideRequests, mediaThumbnailStub.get().setImageResource(glideRequests,
((MmsMessageRecord)messageRecord).getSlideDeck().getThumbnailSlide(), ((MmsMessageRecord)messageRecord).getSlideDeck().getThumbnailSlide(),
showControls, false); showControls, false);
mediaThumbnailStub.get().setThumbnailClickListener(new ThumbnailClickListener()); mediaThumbnailStub.get().setThumbnailClickListener(new ThumbnailClickListener());
@ -564,7 +563,7 @@ public class ConversationItem extends LinearLayout
throw new AssertionError("Identity mismatch count: " + mismatches.size()); throw new AssertionError("Identity mismatch count: " + mismatches.size());
} }
new ConfirmIdentityDialog(context, masterSecret, messageRecord, mismatches.get(0)).show(); new ConfirmIdentityDialog(context, messageRecord, mismatches.get(0)).show();
} }
@Override @Override
@ -660,7 +659,6 @@ public class ConversationItem extends LinearLayout
parent.onClick(v); parent.onClick(v);
} else if (messageRecord.isFailed()) { } else if (messageRecord.isFailed()) {
Intent intent = new Intent(context, MessageDetailsActivity.class); Intent intent = new Intent(context, MessageDetailsActivity.class);
intent.putExtra(MessageDetailsActivity.MASTER_SECRET_EXTRA, masterSecret);
intent.putExtra(MessageDetailsActivity.MESSAGE_ID_EXTRA, messageRecord.getId()); intent.putExtra(MessageDetailsActivity.MESSAGE_ID_EXTRA, messageRecord.getId());
intent.putExtra(MessageDetailsActivity.THREAD_ID_EXTRA, messageRecord.getThreadId()); intent.putExtra(MessageDetailsActivity.THREAD_ID_EXTRA, messageRecord.getThreadId());
intent.putExtra(MessageDetailsActivity.TYPE_EXTRA, messageRecord.isMms() ? MmsSmsDatabase.MMS_TRANSPORT : MmsSmsDatabase.SMS_TRANSPORT); intent.putExtra(MessageDetailsActivity.TYPE_EXTRA, messageRecord.isMms() ? MmsSmsDatabase.MMS_TRANSPORT : MmsSmsDatabase.SMS_TRANSPORT);

View File

@ -16,7 +16,6 @@
*/ */
package org.thoughtcrime.securesms; package org.thoughtcrime.securesms;
import android.*;
import android.Manifest; import android.Manifest;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import android.content.ActivityNotFoundException; import android.content.ActivityNotFoundException;
@ -60,7 +59,6 @@ public class ConversationListActivity extends PassphraseRequiredActionBarActivit
private final DynamicLanguage dynamicLanguage = new DynamicLanguage(); private final DynamicLanguage dynamicLanguage = new DynamicLanguage();
private ConversationListFragment fragment; private ConversationListFragment fragment;
private MasterSecret masterSecret;
private SearchToolbar searchToolbar; private SearchToolbar searchToolbar;
private ImageView searchAction; private ImageView searchAction;
@ -72,8 +70,6 @@ public class ConversationListActivity extends PassphraseRequiredActionBarActivit
@Override @Override
protected void onCreate(Bundle icicle, @NonNull MasterSecret masterSecret) { protected void onCreate(Bundle icicle, @NonNull MasterSecret masterSecret) {
this.masterSecret = masterSecret;
setContentView(R.layout.conversation_list_activity); setContentView(R.layout.conversation_list_activity);
Toolbar toolbar = findViewById(R.id.toolbar); Toolbar toolbar = findViewById(R.id.toolbar);
@ -211,7 +207,7 @@ public class ConversationListActivity extends PassphraseRequiredActionBarActivit
Context context = ConversationListActivity.this; Context context = ConversationListActivity.this;
List<MarkedMessageInfo> messageIds = DatabaseFactory.getThreadDatabase(context).setAllThreadsRead(); List<MarkedMessageInfo> messageIds = DatabaseFactory.getThreadDatabase(context).setAllThreadsRead();
MessageNotifier.updateNotification(context, masterSecret); MessageNotifier.updateNotification(context);
MarkReadReceiver.process(context, messageIds); MarkReadReceiver.process(context, messageIds);
return null; return null;

View File

@ -25,8 +25,6 @@ import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import org.thoughtcrime.securesms.crypto.MasterCipher;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.database.CursorRecyclerViewAdapter; import org.thoughtcrime.securesms.database.CursorRecyclerViewAdapter;
import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.ThreadDatabase; import org.thoughtcrime.securesms.database.ThreadDatabase;
@ -53,8 +51,6 @@ class ConversationListAdapter extends CursorRecyclerViewAdapter<ConversationList
private static final int MESSAGE_TYPE_INBOX_ZERO = 3; private static final int MESSAGE_TYPE_INBOX_ZERO = 3;
private final @NonNull ThreadDatabase threadDatabase; private final @NonNull ThreadDatabase threadDatabase;
private final @NonNull MasterSecret masterSecret;
private final @NonNull MasterCipher masterCipher;
private final @NonNull GlideRequests glideRequests; private final @NonNull GlideRequests glideRequests;
private final @NonNull Locale locale; private final @NonNull Locale locale;
private final @NonNull LayoutInflater inflater; private final @NonNull LayoutInflater inflater;
@ -83,7 +79,6 @@ class ConversationListAdapter extends CursorRecyclerViewAdapter<ConversationList
} }
ConversationListAdapter(@NonNull Context context, ConversationListAdapter(@NonNull Context context,
@NonNull MasterSecret masterSecret,
@NonNull GlideRequests glideRequests, @NonNull GlideRequests glideRequests,
@NonNull Locale locale, @NonNull Locale locale,
@Nullable Cursor cursor, @Nullable Cursor cursor,
@ -91,8 +86,6 @@ class ConversationListAdapter extends CursorRecyclerViewAdapter<ConversationList
{ {
super(context, cursor); super(context, cursor);
try { try {
this.masterSecret = masterSecret;
this.masterCipher = new MasterCipher(masterSecret);
this.glideRequests = glideRequests; this.glideRequests = glideRequests;
this.threadDatabase = DatabaseFactory.getThreadDatabase(context); this.threadDatabase = DatabaseFactory.getThreadDatabase(context);
this.locale = locale; this.locale = locale;
@ -142,7 +135,7 @@ class ConversationListAdapter extends CursorRecyclerViewAdapter<ConversationList
@Override @Override
public void onBindItemViewHolder(ViewHolder viewHolder, @NonNull Cursor cursor) { public void onBindItemViewHolder(ViewHolder viewHolder, @NonNull Cursor cursor) {
viewHolder.getItem().bind(masterSecret, getThreadRecord(cursor), glideRequests, locale, batchSet, batchMode); viewHolder.getItem().bind(getThreadRecord(cursor), glideRequests, locale, batchSet, batchMode);
} }
@Override @Override
@ -159,10 +152,10 @@ class ConversationListAdapter extends CursorRecyclerViewAdapter<ConversationList
} }
private ThreadRecord getThreadRecord(@NonNull Cursor cursor) { private ThreadRecord getThreadRecord(@NonNull Cursor cursor) {
return threadDatabase.readerFor(cursor, masterCipher).getCurrent(); return threadDatabase.readerFor(cursor).getCurrent();
} }
public void toggleThreadInBatchSet(long threadId) { void toggleThreadInBatchSet(long threadId) {
if (batchSet.contains(threadId)) { if (batchSet.contains(threadId)) {
batchSet.remove(threadId); batchSet.remove(threadId);
} else if (threadId != -1) { } else if (threadId != -1) {
@ -170,21 +163,21 @@ class ConversationListAdapter extends CursorRecyclerViewAdapter<ConversationList
} }
} }
public Set<Long> getBatchSelections() { Set<Long> getBatchSelections() {
return batchSet; return batchSet;
} }
public void initializeBatchMode(boolean toggle) { void initializeBatchMode(boolean toggle) {
this.batchMode = toggle; this.batchMode = toggle;
unselectAllThreads(); unselectAllThreads();
} }
public void unselectAllThreads() { private void unselectAllThreads() {
this.batchSet.clear(); this.batchSet.clear();
this.notifyDataSetChanged(); this.notifyDataSetChanged();
} }
public void selectAllThreads() { void selectAllThreads() {
for (int i = 0; i < getItemCount(); i++) { for (int i = 0; i < getItemCount(); i++) {
long threadId = getThreadRecord(getCursorAtPositionOrThrow(i)).getThreadId(); long threadId = getThreadRecord(getCursorAtPositionOrThrow(i)).getThreadId();
if (threadId != -1) batchSet.add(threadId); if (threadId != -1) batchSet.add(threadId);

View File

@ -221,7 +221,7 @@ public class ConversationListFragment extends Fragment
} }
private void initializeListAdapter() { private void initializeListAdapter() {
list.setAdapter(new ConversationListAdapter(getActivity(), masterSecret, GlideApp.with(this), locale, null, this)); list.setAdapter(new ConversationListAdapter(getActivity(), GlideApp.with(this), locale, null, this));
getLoaderManager().restartLoader(0, null, this); getLoaderManager().restartLoader(0, null, this);
} }
@ -302,7 +302,7 @@ public class ConversationListFragment extends Fragment
@Override @Override
protected Void doInBackground(Void... params) { protected Void doInBackground(Void... params) {
DatabaseFactory.getThreadDatabase(getActivity()).deleteConversations(selectedConversations); DatabaseFactory.getThreadDatabase(getActivity()).deleteConversations(selectedConversations);
MessageNotifier.updateNotification(getActivity(), masterSecret); MessageNotifier.updateNotification(getActivity());
return null; return null;
} }
@ -519,7 +519,7 @@ public class ConversationListFragment extends Fragment
if (unreadCount > 0) { if (unreadCount > 0) {
List<MarkedMessageInfo> messageIds = DatabaseFactory.getThreadDatabase(getActivity()).setRead(threadId, false); List<MarkedMessageInfo> messageIds = DatabaseFactory.getThreadDatabase(getActivity()).setRead(threadId, false);
MessageNotifier.updateNotification(getActivity(), masterSecret); MessageNotifier.updateNotification(getActivity());
MarkReadReceiver.process(getActivity(), messageIds); MarkReadReceiver.process(getActivity(), messageIds);
} }
} }
@ -530,7 +530,7 @@ public class ConversationListFragment extends Fragment
if (unreadCount > 0) { if (unreadCount > 0) {
DatabaseFactory.getThreadDatabase(getActivity()).incrementUnread(threadId, unreadCount); DatabaseFactory.getThreadDatabase(getActivity()).incrementUnread(threadId, unreadCount);
MessageNotifier.updateNotification(getActivity(), masterSecret); MessageNotifier.updateNotification(getActivity());
} }
} }
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, threadId); }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, threadId);

View File

@ -16,7 +16,6 @@
*/ */
package org.thoughtcrime.securesms; package org.thoughtcrime.securesms;
import android.annotation.TargetApi;
import android.content.Context; import android.content.Context;
import android.content.res.ColorStateList; import android.content.res.ColorStateList;
import android.graphics.Color; import android.graphics.Color;
@ -38,7 +37,6 @@ import org.thoughtcrime.securesms.components.AvatarImageView;
import org.thoughtcrime.securesms.components.DeliveryStatusView; import org.thoughtcrime.securesms.components.DeliveryStatusView;
import org.thoughtcrime.securesms.components.FromTextView; import org.thoughtcrime.securesms.components.FromTextView;
import org.thoughtcrime.securesms.components.ThumbnailView; import org.thoughtcrime.securesms.components.ThumbnailView;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.database.model.ThreadRecord; import org.thoughtcrime.securesms.database.model.ThreadRecord;
import org.thoughtcrime.securesms.mms.GlideRequests; import org.thoughtcrime.securesms.mms.GlideRequests;
import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.Recipient;
@ -108,7 +106,7 @@ public class ConversationListItem extends RelativeLayout
} }
@Override @Override
public void bind(@NonNull MasterSecret masterSecret, @NonNull ThreadRecord thread, public void bind(@NonNull ThreadRecord thread,
@NonNull GlideRequests glideRequests, @NonNull Locale locale, @NonNull GlideRequests glideRequests, @NonNull Locale locale,
@NonNull Set<Long> selectedThreads, boolean batchMode) @NonNull Set<Long> selectedThreads, boolean batchMode)
{ {
@ -139,7 +137,7 @@ public class ConversationListItem extends RelativeLayout
} }
setStatusIcons(thread); setStatusIcons(thread);
setThumbnailSnippet(masterSecret, thread); setThumbnailSnippet(thread);
setBatchState(batchMode); setBatchState(batchMode);
setRippleColor(recipient); setRippleColor(recipient);
setUnreadIndicator(thread); setUnreadIndicator(thread);
@ -175,10 +173,10 @@ public class ConversationListItem extends RelativeLayout
return lastSeen; return lastSeen;
} }
private void setThumbnailSnippet(MasterSecret masterSecret, ThreadRecord thread) { private void setThumbnailSnippet(ThreadRecord thread) {
if (thread.getSnippetUri() != null) { if (thread.getSnippetUri() != null) {
this.thumbnailView.setVisibility(View.VISIBLE); this.thumbnailView.setVisibility(View.VISIBLE);
this.thumbnailView.setImageResource(masterSecret, glideRequests, thread.getSnippetUri()); this.thumbnailView.setImageResource(glideRequests, thread.getSnippetUri());
LayoutParams subjectParams = (RelativeLayout.LayoutParams)this.subjectView.getLayoutParams(); LayoutParams subjectParams = (RelativeLayout.LayoutParams)this.subjectView.getLayoutParams();
subjectParams.addRule(RelativeLayout.LEFT_OF, R.id.thumbnail); subjectParams.addRule(RelativeLayout.LEFT_OF, R.id.thumbnail);

View File

@ -8,7 +8,6 @@ import android.util.AttributeSet;
import android.widget.LinearLayout; import android.widget.LinearLayout;
import android.widget.TextView; import android.widget.TextView;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.database.model.ThreadRecord; import org.thoughtcrime.securesms.database.model.ThreadRecord;
import org.thoughtcrime.securesms.mms.GlideRequests; import org.thoughtcrime.securesms.mms.GlideRequests;
import org.thoughtcrime.securesms.util.ViewUtil; import org.thoughtcrime.securesms.util.ViewUtil;
@ -40,7 +39,7 @@ public class ConversationListItemAction extends LinearLayout implements Bindable
} }
@Override @Override
public void bind(@NonNull MasterSecret masterSecret, @NonNull ThreadRecord thread, @NonNull GlideRequests glideRequests, @NonNull Locale locale, @NonNull Set<Long> selectedThreads, boolean batchMode) { public void bind(@NonNull ThreadRecord thread, @NonNull GlideRequests glideRequests, @NonNull Locale locale, @NonNull Set<Long> selectedThreads, boolean batchMode) {
this.description.setText(getContext().getString(R.string.ConversationListItemAction_archived_conversations_d, thread.getCount())); this.description.setText(getContext().getString(R.string.ConversationListItemAction_archived_conversations_d, thread.getCount()));
} }

View File

@ -9,7 +9,6 @@ import android.support.annotation.RequiresApi;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.widget.LinearLayout; import android.widget.LinearLayout;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.database.model.ThreadRecord; import org.thoughtcrime.securesms.database.model.ThreadRecord;
import org.thoughtcrime.securesms.mms.GlideRequests; import org.thoughtcrime.securesms.mms.GlideRequests;
@ -40,7 +39,7 @@ public class ConversationListItemInboxZero extends LinearLayout implements Binda
} }
@Override @Override
public void bind(@NonNull MasterSecret masterSecret, @NonNull ThreadRecord thread, @NonNull GlideRequests glideRequests, @NonNull Locale locale, @NonNull Set<Long> selectedThreads, boolean batchMode) { public void bind(@NonNull ThreadRecord thread, @NonNull GlideRequests glideRequests, @NonNull Locale locale, @NonNull Set<Long> selectedThreads, boolean batchMode) {
} }
} }

View File

@ -15,7 +15,6 @@ import android.widget.LinearLayout;
import android.widget.TextView; import android.widget.TextView;
import org.thoughtcrime.securesms.crypto.IdentityKeyParcelable; import org.thoughtcrime.securesms.crypto.IdentityKeyParcelable;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.database.IdentityDatabase; import org.thoughtcrime.securesms.database.IdentityDatabase;
import org.thoughtcrime.securesms.database.IdentityDatabase.IdentityRecord; import org.thoughtcrime.securesms.database.IdentityDatabase.IdentityRecord;
import org.thoughtcrime.securesms.database.model.MessageRecord; import org.thoughtcrime.securesms.database.model.MessageRecord;
@ -38,7 +37,6 @@ public class ConversationUpdateItem extends LinearLayout
{ {
private static final String TAG = ConversationUpdateItem.class.getSimpleName(); private static final String TAG = ConversationUpdateItem.class.getSimpleName();
private MasterSecret masterSecret;
private Set<MessageRecord> batchSelected; private Set<MessageRecord> batchSelected;
private ImageView icon; private ImageView icon;
@ -60,22 +58,20 @@ public class ConversationUpdateItem extends LinearLayout
public void onFinishInflate() { public void onFinishInflate() {
super.onFinishInflate(); super.onFinishInflate();
this.icon = (ImageView)findViewById(R.id.conversation_update_icon); this.icon = findViewById(R.id.conversation_update_icon);
this.body = (TextView)findViewById(R.id.conversation_update_body); this.body = findViewById(R.id.conversation_update_body);
this.date = (TextView)findViewById(R.id.conversation_update_date); this.date = findViewById(R.id.conversation_update_date);
this.setOnClickListener(new InternalClickListener(null)); this.setOnClickListener(new InternalClickListener(null));
} }
@Override @Override
public void bind(@NonNull MasterSecret masterSecret, public void bind(@NonNull MessageRecord messageRecord,
@NonNull MessageRecord messageRecord,
@NonNull GlideRequests glideRequests, @NonNull GlideRequests glideRequests,
@NonNull Locale locale, @NonNull Locale locale,
@NonNull Set<MessageRecord> batchSelected, @NonNull Set<MessageRecord> batchSelected,
@NonNull Recipient conversationRecipient) @NonNull Recipient conversationRecipient)
{ {
this.masterSecret = masterSecret;
this.batchSelected = batchSelected; this.batchSelected = batchSelected;
bind(messageRecord, locale); bind(messageRecord, locale);
@ -172,12 +168,7 @@ public class ConversationUpdateItem extends LinearLayout
@Override @Override
public void onModified(Recipient recipient) { public void onModified(Recipient recipient) {
Util.runOnMain(new Runnable() { Util.runOnMain(() -> bind(messageRecord, locale));
@Override
public void run() {
bind(messageRecord, locale);
}
});
} }
@Override @Override
@ -196,7 +187,7 @@ public class ConversationUpdateItem extends LinearLayout
@Nullable private final View.OnClickListener parent; @Nullable private final View.OnClickListener parent;
public InternalClickListener(@Nullable View.OnClickListener parent) { InternalClickListener(@Nullable View.OnClickListener parent) {
this.parent = parent; this.parent = parent;
} }

View File

@ -76,6 +76,7 @@ public class DatabaseUpgradeActivity extends BaseActivity {
public static final int SCREENSHOTS = 300; public static final int SCREENSHOTS = 300;
public static final int PERSISTENT_BLOBS = 317; public static final int PERSISTENT_BLOBS = 317;
public static final int INTERNALIZE_CONTACTS = 317; public static final int INTERNALIZE_CONTACTS = 317;
public static final int SQLCIPHER = 334;
private static final SortedSet<Integer> UPGRADE_VERSIONS = new TreeSet<Integer>() {{ private static final SortedSet<Integer> UPGRADE_VERSIONS = new TreeSet<Integer>() {{
add(NO_MORE_KEY_EXCHANGE_PREFIX_VERSION); add(NO_MORE_KEY_EXCHANGE_PREFIX_VERSION);
@ -93,6 +94,7 @@ public class DatabaseUpgradeActivity extends BaseActivity {
add(SCREENSHOTS); add(SCREENSHOTS);
add(INTERNALIZE_CONTACTS); add(INTERNALIZE_CONTACTS);
add(PERSISTENT_BLOBS); add(PERSISTENT_BLOBS);
add(SQLCIPHER);
}}; }};
private MasterSecret masterSecret; private MasterSecret masterSecret;
@ -106,14 +108,14 @@ public class DatabaseUpgradeActivity extends BaseActivity {
Log.w("DatabaseUpgradeActivity", "Upgrading..."); Log.w("DatabaseUpgradeActivity", "Upgrading...");
setContentView(R.layout.database_upgrade_activity); setContentView(R.layout.database_upgrade_activity);
ProgressBar indeterminateProgress = (ProgressBar)findViewById(R.id.indeterminate_progress); ProgressBar indeterminateProgress = findViewById(R.id.indeterminate_progress);
ProgressBar determinateProgress = (ProgressBar)findViewById(R.id.determinate_progress); ProgressBar determinateProgress = findViewById(R.id.determinate_progress);
new DatabaseUpgradeTask(indeterminateProgress, determinateProgress) new DatabaseUpgradeTask(indeterminateProgress, determinateProgress)
.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, VersionTracker.getLastSeenVersion(this)); .executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, VersionTracker.getLastSeenVersion(this));
} else { } else {
VersionTracker.updateLastSeenVersion(this); VersionTracker.updateLastSeenVersion(this);
updateNotifications(this, masterSecret); updateNotifications(this);
startActivity((Intent)getIntent().getParcelableExtra("next_intent")); startActivity((Intent)getIntent().getParcelableExtra("next_intent"));
finish(); finish();
} }
@ -149,11 +151,11 @@ public class DatabaseUpgradeActivity extends BaseActivity {
} }
@SuppressLint("StaticFieldLeak") @SuppressLint("StaticFieldLeak")
private void updateNotifications(final Context context, final MasterSecret masterSecret) { private void updateNotifications(final Context context) {
new AsyncTask<Void, Void, Void>() { new AsyncTask<Void, Void, Void>() {
@Override @Override
protected Void doInBackground(Void... params) { protected Void doInBackground(Void... params) {
MessageNotifier.updateNotification(context, masterSecret); MessageNotifier.updateNotification(context);
return null; return null;
} }
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
@ -171,7 +173,7 @@ public class DatabaseUpgradeActivity extends BaseActivity {
private final ProgressBar indeterminateProgress; private final ProgressBar indeterminateProgress;
private final ProgressBar determinateProgress; private final ProgressBar determinateProgress;
public DatabaseUpgradeTask(ProgressBar indeterminateProgress, ProgressBar determinateProgress) { DatabaseUpgradeTask(ProgressBar indeterminateProgress, ProgressBar determinateProgress) {
this.indeterminateProgress = indeterminateProgress; this.indeterminateProgress = indeterminateProgress;
this.determinateProgress = determinateProgress; this.determinateProgress = determinateProgress;
} }
@ -278,11 +280,11 @@ public class DatabaseUpgradeActivity extends BaseActivity {
private void schedulePendingIncomingParts(Context context) { private void schedulePendingIncomingParts(Context context) {
final AttachmentDatabase attachmentDb = DatabaseFactory.getAttachmentDatabase(context); final AttachmentDatabase attachmentDb = DatabaseFactory.getAttachmentDatabase(context);
final MmsDatabase mmsDb = DatabaseFactory.getMmsDatabase(context); final MmsDatabase mmsDb = DatabaseFactory.getMmsDatabase(context);
final List<DatabaseAttachment> pendingAttachments = DatabaseFactory.getAttachmentDatabase(context).getPendingAttachments(masterSecret); final List<DatabaseAttachment> pendingAttachments = DatabaseFactory.getAttachmentDatabase(context).getPendingAttachments();
Log.w(TAG, pendingAttachments.size() + " pending parts."); Log.w(TAG, pendingAttachments.size() + " pending parts.");
for (DatabaseAttachment attachment : pendingAttachments) { for (DatabaseAttachment attachment : pendingAttachments) {
final Reader reader = mmsDb.readerFor(masterSecret, mmsDb.getMessage(attachment.getMmsId())); final Reader reader = mmsDb.readerFor(mmsDb.getMessage(attachment.getMmsId()));
final MessageRecord record = reader.getNext(); final MessageRecord record = reader.getNext();
if (attachment.hasData()) { if (attachment.hasData()) {
@ -329,7 +331,7 @@ public class DatabaseUpgradeActivity extends BaseActivity {
@Override @Override
protected void onPostExecute(Void result) { protected void onPostExecute(Void result) {
VersionTracker.updateLastSeenVersion(DatabaseUpgradeActivity.this); VersionTracker.updateLastSeenVersion(DatabaseUpgradeActivity.this);
updateNotifications(DatabaseUpgradeActivity.this, masterSecret); updateNotifications(DatabaseUpgradeActivity.this);
startActivity((Intent)getIntent().getParcelableExtra("next_intent")); startActivity((Intent)getIntent().getParcelableExtra("next_intent"));
finish(); finish();

View File

@ -18,7 +18,6 @@
package org.thoughtcrime.securesms; package org.thoughtcrime.securesms;
import android.app.Activity; import android.app.Activity;
import android.content.AsyncTaskLoader;
import android.content.Intent; import android.content.Intent;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.net.Uri; import android.net.Uri;
@ -102,7 +101,6 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity
private ListView lv; private ListView lv;
private ImageView avatar; private ImageView avatar;
private TextView creatingText; private TextView creatingText;
private MasterSecret masterSecret;
private Bitmap avatarBmp; private Bitmap avatarBmp;
@NonNull private Optional<GroupData> groupToUpdate = Optional.absent(); @NonNull private Optional<GroupData> groupToUpdate = Optional.absent();
@ -115,8 +113,6 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity
@Override @Override
protected void onCreate(Bundle state, @NonNull MasterSecret masterSecret) { protected void onCreate(Bundle state, @NonNull MasterSecret masterSecret) {
this.masterSecret = masterSecret;
setContentView(R.layout.group_create_activity); setContentView(R.layout.group_create_activity);
//noinspection ConstantConditions //noinspection ConstantConditions
getSupportActionBar().setDisplayHomeAsUpEnabled(true); getSupportActionBar().setDisplayHomeAsUpEnabled(true);
@ -192,12 +188,7 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity
recipientsPanel.setPanelChangeListener(this); recipientsPanel.setPanelChangeListener(this);
findViewById(R.id.contacts_button).setOnClickListener(new AddRecipientButtonListener()); findViewById(R.id.contacts_button).setOnClickListener(new AddRecipientButtonListener());
avatar.setImageDrawable(new ResourceContactPhoto(R.drawable.ic_group_white_24dp).asDrawable(this, ContactColors.UNKNOWN_COLOR.toConversationColor(this))); avatar.setImageDrawable(new ResourceContactPhoto(R.drawable.ic_group_white_24dp).asDrawable(this, ContactColors.UNKNOWN_COLOR.toConversationColor(this)));
avatar.setOnClickListener(new View.OnClickListener() { avatar.setOnClickListener(view -> Crop.pickImage(GroupCreateActivity.this));
@Override
public void onClick(View view) {
Crop.pickImage(GroupCreateActivity.this);
}
});
} }
private void initializeExistingGroup() { private void initializeExistingGroup() {
@ -252,14 +243,14 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity
return; return;
} }
if (isSignalGroup()) { if (isSignalGroup()) {
new CreateSignalGroupTask(this, masterSecret, avatarBmp, getGroupName(), getAdapter().getRecipients()).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); new CreateSignalGroupTask(this, avatarBmp, getGroupName(), getAdapter().getRecipients()).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
} else { } else {
new CreateMmsGroupTask(this, masterSecret, getAdapter().getRecipients()).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); new CreateMmsGroupTask(this, getAdapter().getRecipients()).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
} }
} }
private void handleGroupUpdate() { private void handleGroupUpdate() {
new UpdateSignalGroupTask(this, masterSecret, groupToUpdate.get().id, avatarBmp, new UpdateSignalGroupTask(this, groupToUpdate.get().id, avatarBmp,
getGroupName(), getAdapter().getRecipients()).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); getGroupName(), getAdapter().getRecipients()).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
} }
@ -313,7 +304,7 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity
.override(AVATAR_SIZE, AVATAR_SIZE) .override(AVATAR_SIZE, AVATAR_SIZE)
.into(new SimpleTarget<Bitmap>() { .into(new SimpleTarget<Bitmap>() {
@Override @Override
public void onResourceReady(Bitmap resource, Transition<? super Bitmap> transition) { public void onResourceReady(@NonNull Bitmap resource, Transition<? super Bitmap> transition) {
setAvatar(Crop.getOutput(data), resource); setAvatar(Crop.getOutput(data), resource);
} }
}); });
@ -332,12 +323,10 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity
private static class CreateMmsGroupTask extends AsyncTask<Void,Void,GroupActionResult> { private static class CreateMmsGroupTask extends AsyncTask<Void,Void,GroupActionResult> {
private final GroupCreateActivity activity; private final GroupCreateActivity activity;
private final MasterSecret masterSecret;
private final Set<Recipient> members; private final Set<Recipient> members;
public CreateMmsGroupTask(GroupCreateActivity activity, MasterSecret masterSecret, Set<Recipient> members) { public CreateMmsGroupTask(GroupCreateActivity activity, Set<Recipient> members) {
this.activity = activity; this.activity = activity;
this.masterSecret = masterSecret;
this.members = members; this.members = members;
} }
@ -368,20 +357,18 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity
} }
private abstract static class SignalGroupTask extends AsyncTask<Void,Void,Optional<GroupActionResult>> { private abstract static class SignalGroupTask extends AsyncTask<Void,Void,Optional<GroupActionResult>> {
protected GroupCreateActivity activity; protected GroupCreateActivity activity;
protected MasterSecret masterSecret;
protected Bitmap avatar; protected Bitmap avatar;
protected Set<Recipient> members; protected Set<Recipient> members;
protected String name; protected String name;
public SignalGroupTask(GroupCreateActivity activity, public SignalGroupTask(GroupCreateActivity activity,
MasterSecret masterSecret,
Bitmap avatar, Bitmap avatar,
String name, String name,
Set<Recipient> members) Set<Recipient> members)
{ {
this.activity = activity; this.activity = activity;
this.masterSecret = masterSecret;
this.avatar = avatar; this.avatar = avatar;
this.name = name; this.name = name;
this.members = members; this.members = members;
@ -408,13 +395,13 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity
} }
private static class CreateSignalGroupTask extends SignalGroupTask { private static class CreateSignalGroupTask extends SignalGroupTask {
public CreateSignalGroupTask(GroupCreateActivity activity, MasterSecret masterSecret, Bitmap avatar, String name, Set<Recipient> members) { public CreateSignalGroupTask(GroupCreateActivity activity, Bitmap avatar, String name, Set<Recipient> members) {
super(activity, masterSecret, avatar, name, members); super(activity, avatar, name, members);
} }
@Override @Override
protected Optional<GroupActionResult> doInBackground(Void... aVoid) { protected Optional<GroupActionResult> doInBackground(Void... aVoid) {
return Optional.of(GroupManager.createGroup(activity, masterSecret, members, avatar, name, false)); return Optional.of(GroupManager.createGroup(activity, members, avatar, name, false));
} }
@Override @Override
@ -434,18 +421,17 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity
private static class UpdateSignalGroupTask extends SignalGroupTask { private static class UpdateSignalGroupTask extends SignalGroupTask {
private String groupId; private String groupId;
public UpdateSignalGroupTask(GroupCreateActivity activity, public UpdateSignalGroupTask(GroupCreateActivity activity, String groupId,
MasterSecret masterSecret, String groupId, Bitmap avatar, String name, Bitmap avatar, String name, Set<Recipient> members)
Set<Recipient> members)
{ {
super(activity, masterSecret, avatar, name, members); super(activity, avatar, name, members);
this.groupId = groupId; this.groupId = groupId;
} }
@Override @Override
protected Optional<GroupActionResult> doInBackground(Void... aVoid) { protected Optional<GroupActionResult> doInBackground(Void... aVoid) {
try { try {
return Optional.of(GroupManager.updateGroup(activity, masterSecret, groupId, members, avatar, name)); return Optional.of(GroupManager.updateGroup(activity, groupId, members, avatar, name));
} catch (InvalidNumberException e) { } catch (InvalidNumberException e) {
return Optional.absent(); return Optional.absent();
} }

View File

@ -16,7 +16,6 @@ import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.Toast; import android.widget.Toast;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.database.NoExternalStorageException; import org.thoughtcrime.securesms.database.NoExternalStorageException;
import org.thoughtcrime.securesms.database.PlaintextBackupExporter; import org.thoughtcrime.securesms.database.PlaintextBackupExporter;
import org.thoughtcrime.securesms.database.PlaintextBackupImporter; import org.thoughtcrime.securesms.database.PlaintextBackupImporter;
@ -35,13 +34,11 @@ public class ImportExportFragment extends Fragment {
private static final int NO_SD_CARD = 1; private static final int NO_SD_CARD = 1;
private static final int ERROR_IO = 2; private static final int ERROR_IO = 2;
private MasterSecret masterSecret;
private ProgressDialog progressDialog; private ProgressDialog progressDialog;
@Override @Override
public void onCreate(Bundle bundle) { public void onCreate(Bundle bundle) {
super.onCreate(bundle); super.onCreate(bundle);
this.masterSecret = getArguments().getParcelable("master_secret");
} }
@Override @Override
@ -87,7 +84,6 @@ public class ImportExportFragment extends Fragment {
.onAllGranted(() -> { .onAllGranted(() -> {
Intent intent = new Intent(getActivity(), ApplicationMigrationService.class); Intent intent = new Intent(getActivity(), ApplicationMigrationService.class);
intent.setAction(ApplicationMigrationService.MIGRATE_DATABASE); intent.setAction(ApplicationMigrationService.MIGRATE_DATABASE);
intent.putExtra("master_secret", masterSecret);
getActivity().startService(intent); getActivity().startService(intent);
Intent nextIntent = new Intent(getActivity(), ConversationListActivity.class); Intent nextIntent = new Intent(getActivity(), ConversationListActivity.class);
@ -185,7 +181,7 @@ public class ImportExportFragment extends Fragment {
@Override @Override
protected Integer doInBackground(Void... params) { protected Integer doInBackground(Void... params) {
try { try {
PlaintextBackupImporter.importPlaintextFromSd(getActivity(), masterSecret); PlaintextBackupImporter.importPlaintextFromSd(getActivity());
return SUCCESS; return SUCCESS;
} catch (NoExternalStorageException e) { } catch (NoExternalStorageException e) {
Log.w("ImportFragment", e); Log.w("ImportFragment", e);
@ -212,7 +208,7 @@ public class ImportExportFragment extends Fragment {
@Override @Override
protected Integer doInBackground(Void... params) { protected Integer doInBackground(Void... params) {
try { try {
PlaintextBackupExporter.exportPlaintextToSd(getActivity(), masterSecret); PlaintextBackupExporter.exportPlaintextToSd(getActivity());
return SUCCESS; return SUCCESS;
} catch (NoExternalStorageException e) { } catch (NoExternalStorageException e) {
Log.w("ExportFragment", e); Log.w("ExportFragment", e);

View File

@ -235,7 +235,7 @@ public class InviteActivity extends PassphraseRequiredActionBarActivity implemen
Recipient recipient = Recipient.from(context, Address.fromExternal(context, number), false); Recipient recipient = Recipient.from(context, Address.fromExternal(context, number), false);
int subscriptionId = recipient.getDefaultSubscriptionId().or(-1); int subscriptionId = recipient.getDefaultSubscriptionId().or(-1);
MessageSender.send(context, masterSecret, new OutgoingTextMessage(recipient, message, subscriptionId), -1L, true, null); MessageSender.send(context, new OutgoingTextMessage(recipient, message, subscriptionId), -1L, true, null);
if (recipient.getContactUri() != null) { if (recipient.getContactUri() != null) {
DatabaseFactory.getRecipientDatabase(context).setSeenInviteReminder(recipient, true); DatabaseFactory.getRecipientDatabase(context).setSeenInviteReminder(recipient, true);

View File

@ -17,7 +17,6 @@ import android.widget.Toast;
import org.thoughtcrime.securesms.MediaDocumentsAdapter.HeaderViewHolder; import org.thoughtcrime.securesms.MediaDocumentsAdapter.HeaderViewHolder;
import org.thoughtcrime.securesms.MediaDocumentsAdapter.ViewHolder; import org.thoughtcrime.securesms.MediaDocumentsAdapter.ViewHolder;
import org.thoughtcrime.securesms.components.DocumentView; import org.thoughtcrime.securesms.components.DocumentView;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.database.CursorRecyclerViewAdapter; import org.thoughtcrime.securesms.database.CursorRecyclerViewAdapter;
import org.thoughtcrime.securesms.database.MediaDatabase; import org.thoughtcrime.securesms.database.MediaDatabase;
import org.thoughtcrime.securesms.mms.DocumentSlide; import org.thoughtcrime.securesms.mms.DocumentSlide;
@ -36,14 +35,12 @@ import static com.codewaves.stickyheadergrid.StickyHeaderGridLayoutManager.TAG;
public class MediaDocumentsAdapter extends CursorRecyclerViewAdapter<ViewHolder> implements StickyHeaderDecoration.StickyHeaderAdapter<HeaderViewHolder> { public class MediaDocumentsAdapter extends CursorRecyclerViewAdapter<ViewHolder> implements StickyHeaderDecoration.StickyHeaderAdapter<HeaderViewHolder> {
private final MasterSecret masterSecret;
private final Calendar calendar; private final Calendar calendar;
private final Locale locale; private final Locale locale;
public MediaDocumentsAdapter(Context context, MasterSecret masterSecret, Cursor cursor, Locale locale) { MediaDocumentsAdapter(Context context, Cursor cursor, Locale locale) {
super(context, cursor); super(context, cursor);
this.masterSecret = masterSecret;
this.calendar = Calendar.getInstance(); this.calendar = Calendar.getInstance();
this.locale = locale; this.locale = locale;
} }
@ -55,7 +52,7 @@ public class MediaDocumentsAdapter extends CursorRecyclerViewAdapter<ViewHolder>
@Override @Override
public void onBindItemViewHolder(ViewHolder viewHolder, @NonNull Cursor cursor) { public void onBindItemViewHolder(ViewHolder viewHolder, @NonNull Cursor cursor) {
MediaDatabase.MediaRecord mediaRecord = MediaDatabase.MediaRecord.from(getContext(), masterSecret, cursor); MediaDatabase.MediaRecord mediaRecord = MediaDatabase.MediaRecord.from(getContext(), cursor);
Slide slide = MediaUtil.getSlideForAttachment(getContext(), mediaRecord.getAttachment()); Slide slide = MediaUtil.getSlideForAttachment(getContext(), mediaRecord.getAttachment());
if (slide != null && slide.hasDocument()) { if (slide != null && slide.hasDocument()) {
@ -89,7 +86,7 @@ public class MediaDocumentsAdapter extends CursorRecyclerViewAdapter<ViewHolder>
if (position < 0) return -1; if (position < 0) return -1;
Cursor cursor = getCursorAtPositionOrThrow(position); Cursor cursor = getCursorAtPositionOrThrow(position);
MediaDatabase.MediaRecord mediaRecord = MediaDatabase.MediaRecord.from(getContext(), masterSecret, cursor); MediaDatabase.MediaRecord mediaRecord = MediaDatabase.MediaRecord.from(getContext(), cursor);
calendar.setTime(new Date(mediaRecord.getDate())); calendar.setTime(new Date(mediaRecord.getDate()));
return Util.hashCode(calendar.get(Calendar.YEAR), calendar.get(Calendar.DAY_OF_YEAR)); return Util.hashCode(calendar.get(Calendar.YEAR), calendar.get(Calendar.DAY_OF_YEAR));
@ -103,7 +100,7 @@ public class MediaDocumentsAdapter extends CursorRecyclerViewAdapter<ViewHolder>
@Override @Override
public void onBindHeaderViewHolder(HeaderViewHolder viewHolder, int position) { public void onBindHeaderViewHolder(HeaderViewHolder viewHolder, int position) {
Cursor cursor = getCursorAtPositionOrThrow(position); Cursor cursor = getCursorAtPositionOrThrow(position);
MediaDatabase.MediaRecord mediaRecord = MediaDatabase.MediaRecord.from(getContext(), masterSecret, cursor); MediaDatabase.MediaRecord mediaRecord = MediaDatabase.MediaRecord.from(getContext(), cursor);
viewHolder.textView.setText(DateUtils.getRelativeDate(getContext(), locale, mediaRecord.getDate())); viewHolder.textView.setText(DateUtils.getRelativeDate(getContext(), locale, mediaRecord.getDate()));
} }
@ -114,8 +111,8 @@ public class MediaDocumentsAdapter extends CursorRecyclerViewAdapter<ViewHolder>
public ViewHolder(View itemView) { public ViewHolder(View itemView) {
super(itemView); super(itemView);
this.documentView = (DocumentView)itemView.findViewById(R.id.document_view); this.documentView = itemView.findViewById(R.id.document_view);
this.date = (TextView)itemView.findViewById(R.id.date); this.date = itemView.findViewById(R.id.date);
} }
} }
@ -125,7 +122,7 @@ public class MediaDocumentsAdapter extends CursorRecyclerViewAdapter<ViewHolder>
HeaderViewHolder(View itemView) { HeaderViewHolder(View itemView) {
super(itemView); super(itemView);
this.textView = (TextView)itemView.findViewById(R.id.text); this.textView = itemView.findViewById(R.id.text);
} }
} }

View File

@ -27,7 +27,6 @@ import android.widget.TextView;
import com.codewaves.stickyheadergrid.StickyHeaderGridAdapter; import com.codewaves.stickyheadergrid.StickyHeaderGridAdapter;
import org.thoughtcrime.securesms.components.ThumbnailView; import org.thoughtcrime.securesms.components.ThumbnailView;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.database.Address; import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.MediaDatabase.MediaRecord; import org.thoughtcrime.securesms.database.MediaDatabase.MediaRecord;
import org.thoughtcrime.securesms.database.loaders.BucketedThreadMediaLoader.BucketedThreadMedia; import org.thoughtcrime.securesms.database.loaders.BucketedThreadMediaLoader.BucketedThreadMedia;
@ -39,10 +38,10 @@ import java.util.Locale;
class MediaGalleryAdapter extends StickyHeaderGridAdapter { class MediaGalleryAdapter extends StickyHeaderGridAdapter {
@SuppressWarnings("unused")
private static final String TAG = MediaGalleryAdapter.class.getSimpleName(); private static final String TAG = MediaGalleryAdapter.class.getSimpleName();
private final Context context; private final Context context;
private final MasterSecret masterSecret;
private final GlideRequests glideRequests; private final GlideRequests glideRequests;
private final Locale locale; private final Locale locale;
private final Address address; private final Address address;
@ -67,11 +66,10 @@ class MediaGalleryAdapter extends StickyHeaderGridAdapter {
} }
} }
MediaGalleryAdapter(@NonNull Context context, @NonNull MasterSecret masterSecret, @NonNull GlideRequests glideRequests, MediaGalleryAdapter(@NonNull Context context, @NonNull GlideRequests glideRequests,
BucketedThreadMedia media, Locale locale, Address address) BucketedThreadMedia media, Locale locale, Address address)
{ {
this.context = context; this.context = context;
this.masterSecret = masterSecret;
this.glideRequests = glideRequests; this.glideRequests = glideRequests;
this.locale = locale; this.locale = locale;
this.media = media; this.media = media;
@ -105,7 +103,7 @@ class MediaGalleryAdapter extends StickyHeaderGridAdapter {
Slide slide = MediaUtil.getSlideForAttachment(context, mediaRecord.getAttachment()); Slide slide = MediaUtil.getSlideForAttachment(context, mediaRecord.getAttachment());
if (slide != null) { if (slide != null) {
thumbnailView.setImageResource(masterSecret, glideRequests, slide, false, false); thumbnailView.setImageResource(glideRequests, slide, false, false);
} }
thumbnailView.setOnClickListener(new OnMediaClickListener(mediaRecord)); thumbnailView.setOnClickListener(new OnMediaClickListener(mediaRecord));

View File

@ -207,7 +207,7 @@ public class MediaOverviewActivity extends PassphraseRequiredActionBarActivity
this.noMedia = ViewUtil.findById(view, R.id.no_images); this.noMedia = ViewUtil.findById(view, R.id.no_images);
this.gridManager = new StickyHeaderGridLayoutManager(getResources().getInteger(R.integer.media_overview_cols)); this.gridManager = new StickyHeaderGridLayoutManager(getResources().getInteger(R.integer.media_overview_cols));
this.recyclerView.setAdapter(new MediaGalleryAdapter(getContext(), masterSecret, GlideApp.with(this), new BucketedThreadMedia(getContext()), locale, recipient.getAddress())); this.recyclerView.setAdapter(new MediaGalleryAdapter(getContext(), GlideApp.with(this), new BucketedThreadMedia(getContext()), locale, recipient.getAddress()));
this.recyclerView.setLayoutManager(gridManager); this.recyclerView.setLayoutManager(gridManager);
this.recyclerView.setHasFixedSize(true); this.recyclerView.setHasFixedSize(true);
@ -225,7 +225,7 @@ public class MediaOverviewActivity extends PassphraseRequiredActionBarActivity
@Override @Override
public Loader<BucketedThreadMedia> onCreateLoader(int i, Bundle bundle) { public Loader<BucketedThreadMedia> onCreateLoader(int i, Bundle bundle) {
return new BucketedThreadMediaLoader(getContext(), masterSecret, recipient.getAddress()); return new BucketedThreadMediaLoader(getContext(), recipient.getAddress());
} }
@Override @Override
@ -248,7 +248,7 @@ public class MediaOverviewActivity extends PassphraseRequiredActionBarActivity
@Override @Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.media_overview_documents_fragment, container, false); View view = inflater.inflate(R.layout.media_overview_documents_fragment, container, false);
MediaDocumentsAdapter adapter = new MediaDocumentsAdapter(getContext(), masterSecret, null, locale); MediaDocumentsAdapter adapter = new MediaDocumentsAdapter(getContext(), null, locale);
this.recyclerView = ViewUtil.findById(view, R.id.recycler_view); this.recyclerView = ViewUtil.findById(view, R.id.recycler_view);
this.noMedia = ViewUtil.findById(view, R.id.no_documents); this.noMedia = ViewUtil.findById(view, R.id.no_documents);

View File

@ -81,8 +81,6 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity im
private final DynamicLanguage dynamicLanguage = new DynamicLanguage(); private final DynamicLanguage dynamicLanguage = new DynamicLanguage();
private MasterSecret masterSecret;
private ViewPager mediaPager; private ViewPager mediaPager;
private Uri initialMediaUri; private Uri initialMediaUri;
private String initialMediaType; private String initialMediaType;
@ -95,7 +93,6 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity im
@SuppressWarnings("ConstantConditions") @SuppressWarnings("ConstantConditions")
@Override @Override
protected void onCreate(Bundle bundle, @NonNull MasterSecret masterSecret) { protected void onCreate(Bundle bundle, @NonNull MasterSecret masterSecret) {
this.masterSecret = masterSecret;
this.setTheme(R.style.TextSecure_DarkTheme); this.setTheme(R.style.TextSecure_DarkTheme);
dynamicLanguage.onCreate(this); dynamicLanguage.onCreate(this);
@ -203,7 +200,7 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity im
if (conversationRecipient != null) { if (conversationRecipient != null) {
getSupportLoaderManager().restartLoader(0, null, this); getSupportLoaderManager().restartLoader(0, null, this);
} else { } else {
mediaPager.setAdapter(new SingleItemPagerAdapter(this, masterSecret, GlideApp.with(this), getWindow(), initialMediaUri, initialMediaType, initialMediaSize)); mediaPager.setAdapter(new SingleItemPagerAdapter(this, GlideApp.with(this), getWindow(), initialMediaUri, initialMediaType, initialMediaSize));
} }
} }
@ -246,7 +243,7 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity im
.withPermanentDenialDialog(getString(R.string.MediaPreviewActivity_signal_needs_the_storage_permission_in_order_to_write_to_external_storage_but_it_has_been_permanently_denied)) .withPermanentDenialDialog(getString(R.string.MediaPreviewActivity_signal_needs_the_storage_permission_in_order_to_write_to_external_storage_but_it_has_been_permanently_denied))
.onAnyDenied(() -> Toast.makeText(this, R.string.MediaPreviewActivity_unable_to_write_to_external_storage_without_permission, Toast.LENGTH_LONG).show()) .onAnyDenied(() -> Toast.makeText(this, R.string.MediaPreviewActivity_unable_to_write_to_external_storage_without_permission, Toast.LENGTH_LONG).show())
.onAllGranted(() -> { .onAllGranted(() -> {
SaveAttachmentTask saveTask = new SaveAttachmentTask(MediaPreviewActivity.this, masterSecret); SaveAttachmentTask saveTask = new SaveAttachmentTask(MediaPreviewActivity.this);
long saveDate = (mediaItem.date > 0) ? mediaItem.date : System.currentTimeMillis(); long saveDate = (mediaItem.date > 0) ? mediaItem.date : System.currentTimeMillis();
saveTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, new Attachment(mediaItem.uri, mediaItem.type, saveDate, null)); saveTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, new Attachment(mediaItem.uri, mediaItem.type, saveDate, null));
}) })
@ -304,7 +301,7 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity im
public void onLoadFinished(Loader<Pair<Cursor, Integer>> loader, @Nullable Pair<Cursor, Integer> data) { public void onLoadFinished(Loader<Pair<Cursor, Integer>> loader, @Nullable Pair<Cursor, Integer> data) {
if (data != null) { if (data != null) {
@SuppressWarnings("ConstantConditions") @SuppressWarnings("ConstantConditions")
CursorPagerAdapter adapter = new CursorPagerAdapter(this, masterSecret, GlideApp.with(this), getWindow(), data.first, data.second, leftIsRecent); CursorPagerAdapter adapter = new CursorPagerAdapter(this, GlideApp.with(this), getWindow(), data.first, data.second, leftIsRecent);
mediaPager.setAdapter(adapter); mediaPager.setAdapter(adapter);
adapter.setActive(true); adapter.setActive(true);
@ -350,7 +347,6 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity im
private static class SingleItemPagerAdapter extends PagerAdapter implements MediaItemAdapter { private static class SingleItemPagerAdapter extends PagerAdapter implements MediaItemAdapter {
private final MasterSecret masterSecret;
private final GlideRequests glideRequests; private final GlideRequests glideRequests;
private final Window window; private final Window window;
private final Uri uri; private final Uri uri;
@ -359,11 +355,10 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity im
private final LayoutInflater inflater; private final LayoutInflater inflater;
SingleItemPagerAdapter(@NonNull Context context, @NonNull MasterSecret masterSecret, SingleItemPagerAdapter(@NonNull Context context, @NonNull GlideRequests glideRequests,
@NonNull GlideRequests glideRequests, @NonNull Window window, @NonNull Window window, @NonNull Uri uri, @NonNull String mediaType,
@NonNull Uri uri, @NonNull String mediaType, long size) long size)
{ {
this.masterSecret = masterSecret;
this.glideRequests = glideRequests; this.glideRequests = glideRequests;
this.window = window; this.window = window;
this.uri = uri; this.uri = uri;
@ -388,7 +383,7 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity im
MediaView mediaView = itemView.findViewById(R.id.media_view); MediaView mediaView = itemView.findViewById(R.id.media_view);
try { try {
mediaView.set(masterSecret, glideRequests, window, uri, mediaType, size, true); mediaView.set(glideRequests, window, uri, mediaType, size, true);
} catch (IOException e) { } catch (IOException e) {
Log.w(TAG, e); Log.w(TAG, e);
} }
@ -422,7 +417,6 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity im
private final WeakHashMap<Integer, MediaView> mediaViews = new WeakHashMap<>(); private final WeakHashMap<Integer, MediaView> mediaViews = new WeakHashMap<>();
private final Context context; private final Context context;
private final MasterSecret masterSecret;
private final GlideRequests glideRequests; private final GlideRequests glideRequests;
private final Window window; private final Window window;
private final Cursor cursor; private final Cursor cursor;
@ -431,12 +425,11 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity im
private boolean active; private boolean active;
private int autoPlayPosition; private int autoPlayPosition;
CursorPagerAdapter(@NonNull Context context, @NonNull MasterSecret masterSecret, CursorPagerAdapter(@NonNull Context context, @NonNull GlideRequests glideRequests,
@NonNull GlideRequests glideRequests, @NonNull Window window, @NonNull Window window, @NonNull Cursor cursor, int autoPlayPosition,
@NonNull Cursor cursor, int autoPlayPosition, boolean leftIsRecent) boolean leftIsRecent)
{ {
this.context = context.getApplicationContext(); this.context = context.getApplicationContext();
this.masterSecret = masterSecret;
this.glideRequests = glideRequests; this.glideRequests = glideRequests;
this.window = window; this.window = window;
this.cursor = cursor; this.cursor = cursor;
@ -471,11 +464,11 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity im
cursor.moveToPosition(cursorPosition); cursor.moveToPosition(cursorPosition);
MediaRecord mediaRecord = MediaRecord.from(context, masterSecret, cursor); MediaRecord mediaRecord = MediaRecord.from(context, cursor);
try { try {
//noinspection ConstantConditions //noinspection ConstantConditions
mediaView.set(masterSecret, glideRequests, window, mediaRecord.getAttachment().getDataUri(), mediaRecord.getAttachment().getContentType(), mediaRecord.getAttachment().getSize(), autoplay); mediaView.set(glideRequests, window, mediaRecord.getAttachment().getDataUri(), mediaRecord.getAttachment().getContentType(), mediaRecord.getAttachment().getSize(), autoplay);
} catch (IOException e) { } catch (IOException e) {
Log.w(TAG, e); Log.w(TAG, e);
} }
@ -497,7 +490,7 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity im
public MediaItem getMediaItemFor(int position) { public MediaItem getMediaItemFor(int position) {
cursor.moveToPosition(getCursorPosition(position)); cursor.moveToPosition(getCursorPosition(position));
MediaRecord mediaRecord = MediaRecord.from(context, masterSecret, cursor); MediaRecord mediaRecord = MediaRecord.from(context, cursor);
Address address = mediaRecord.getAddress(); Address address = mediaRecord.getAddress();
if (mediaRecord.getAttachment().getDataUri() == null) throw new AssertionError(); if (mediaRecord.getAttachment().getDataUri() == null) throw new AssertionError();

View File

@ -39,7 +39,6 @@ import org.thoughtcrime.securesms.color.MaterialColor;
import org.thoughtcrime.securesms.crypto.MasterSecret; import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.database.Address; import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.EncryptingSmsDatabase;
import org.thoughtcrime.securesms.database.GroupReceiptDatabase; import org.thoughtcrime.securesms.database.GroupReceiptDatabase;
import org.thoughtcrime.securesms.database.GroupReceiptDatabase.GroupReceiptInfo; import org.thoughtcrime.securesms.database.GroupReceiptDatabase.GroupReceiptInfo;
import org.thoughtcrime.securesms.database.MmsDatabase; import org.thoughtcrime.securesms.database.MmsDatabase;
@ -72,14 +71,12 @@ import java.util.Locale;
public class MessageDetailsActivity extends PassphraseRequiredActionBarActivity implements LoaderCallbacks<Cursor>, RecipientModifiedListener { public class MessageDetailsActivity extends PassphraseRequiredActionBarActivity implements LoaderCallbacks<Cursor>, RecipientModifiedListener {
private final static String TAG = MessageDetailsActivity.class.getSimpleName(); private final static String TAG = MessageDetailsActivity.class.getSimpleName();
public final static String MASTER_SECRET_EXTRA = "master_secret";
public final static String MESSAGE_ID_EXTRA = "message_id"; public final static String MESSAGE_ID_EXTRA = "message_id";
public final static String THREAD_ID_EXTRA = "thread_id"; public final static String THREAD_ID_EXTRA = "thread_id";
public final static String IS_PUSH_GROUP_EXTRA = "is_push_group"; public final static String IS_PUSH_GROUP_EXTRA = "is_push_group";
public final static String TYPE_EXTRA = "type"; public final static String TYPE_EXTRA = "type";
public final static String ADDRESS_EXTRA = "address"; public final static String ADDRESS_EXTRA = "address";
private MasterSecret masterSecret;
private GlideRequests glideRequests; private GlideRequests glideRequests;
private long threadId; private long threadId;
private boolean isPushGroup; private boolean isPushGroup;
@ -166,21 +163,20 @@ public class MessageDetailsActivity extends PassphraseRequiredActionBarActivity
inflater = LayoutInflater.from(this); inflater = LayoutInflater.from(this);
View header = inflater.inflate(R.layout.message_details_header, recipientsList, false); View header = inflater.inflate(R.layout.message_details_header, recipientsList, false);
masterSecret = getIntent().getParcelableExtra(MASTER_SECRET_EXTRA);
threadId = getIntent().getLongExtra(THREAD_ID_EXTRA, -1); threadId = getIntent().getLongExtra(THREAD_ID_EXTRA, -1);
isPushGroup = getIntent().getBooleanExtra(IS_PUSH_GROUP_EXTRA, false); isPushGroup = getIntent().getBooleanExtra(IS_PUSH_GROUP_EXTRA, false);
glideRequests = GlideApp.with(this); glideRequests = GlideApp.with(this);
itemParent = (ViewGroup) header.findViewById(R.id.item_container); itemParent = header.findViewById(R.id.item_container);
recipientsList = (ListView ) findViewById(R.id.recipients_list); recipientsList = findViewById(R.id.recipients_list);
metadataContainer = header.findViewById(R.id.metadata_container); metadataContainer = header.findViewById(R.id.metadata_container);
errorText = (TextView ) header.findViewById(R.id.error_text); errorText = header.findViewById(R.id.error_text);
sentDate = (TextView ) header.findViewById(R.id.sent_time); sentDate = header.findViewById(R.id.sent_time);
receivedContainer = header.findViewById(R.id.received_container); receivedContainer = header.findViewById(R.id.received_container);
receivedDate = (TextView ) header.findViewById(R.id.received_time); receivedDate = header.findViewById(R.id.received_time);
transport = (TextView ) header.findViewById(R.id.transport); transport = header.findViewById(R.id.transport);
toFrom = (TextView ) header.findViewById(R.id.tofrom); toFrom = header.findViewById(R.id.tofrom);
expiresContainer = header.findViewById(R.id.expires_container); expiresContainer = header.findViewById(R.id.expires_container);
expiresInText = (TextView) header.findViewById(R.id.expires_in); expiresInText = header.findViewById(R.id.expires_in);
recipientsList.setHeaderDividersEnabled(false); recipientsList.setHeaderDividersEnabled(false);
recipientsList.addHeaderView(header, null, false); recipientsList.addHeaderView(header, null, false);
} }
@ -253,8 +249,8 @@ public class MessageDetailsActivity extends PassphraseRequiredActionBarActivity
toFromRes = R.string.message_details_header__from; toFromRes = R.string.message_details_header__from;
} }
toFrom.setText(toFromRes); toFrom.setText(toFromRes);
conversationItem.bind(masterSecret, messageRecord, glideRequests, dynamicLanguage.getCurrentLocale(), new HashSet<>(), recipient); conversationItem.bind(messageRecord, glideRequests, dynamicLanguage.getCurrentLocale(), new HashSet<>(), recipient);
recipientsList.setAdapter(new MessageDetailsRecipientAdapter(this, masterSecret, glideRequests, messageRecord, recipients, isPushGroup)); recipientsList.setAdapter(new MessageDetailsRecipientAdapter(this, glideRequests, messageRecord, recipients, isPushGroup));
} }
private void inflateMessageViewIfAbsent(MessageRecord messageRecord) { private void inflateMessageViewIfAbsent(MessageRecord messageRecord) {
@ -273,12 +269,12 @@ public class MessageDetailsActivity extends PassphraseRequiredActionBarActivity
private @Nullable MessageRecord getMessageRecord(Context context, Cursor cursor, String type) { private @Nullable MessageRecord getMessageRecord(Context context, Cursor cursor, String type) {
switch (type) { switch (type) {
case MmsSmsDatabase.SMS_TRANSPORT: case MmsSmsDatabase.SMS_TRANSPORT:
EncryptingSmsDatabase smsDatabase = DatabaseFactory.getEncryptingSmsDatabase(context); SmsDatabase smsDatabase = DatabaseFactory.getSmsDatabase(context);
SmsDatabase.Reader reader = smsDatabase.readerFor(masterSecret, cursor); SmsDatabase.Reader reader = smsDatabase.readerFor(cursor);
return reader.getNext(); return reader.getNext();
case MmsSmsDatabase.MMS_TRANSPORT: case MmsSmsDatabase.MMS_TRANSPORT:
MmsDatabase mmsDatabase = DatabaseFactory.getMmsDatabase(context); MmsDatabase mmsDatabase = DatabaseFactory.getMmsDatabase(context);
MmsDatabase.Reader mmsReader = mmsDatabase.readerFor(masterSecret, cursor); MmsDatabase.Reader mmsReader = mmsDatabase.readerFor(cursor);
return mmsReader.getNext(); return mmsReader.getNext();
default: default:
throw new AssertionError("no valid message type specified"); throw new AssertionError("no valid message type specified");

View File

@ -8,7 +8,6 @@ import android.view.ViewGroup;
import android.widget.AbsListView; import android.widget.AbsListView;
import android.widget.BaseAdapter; import android.widget.BaseAdapter;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.database.model.MessageRecord; import org.thoughtcrime.securesms.database.model.MessageRecord;
import org.thoughtcrime.securesms.mms.GlideRequests; import org.thoughtcrime.securesms.mms.GlideRequests;
import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.Recipient;
@ -21,18 +20,16 @@ import java.util.List;
class MessageDetailsRecipientAdapter extends BaseAdapter implements AbsListView.RecyclerListener { class MessageDetailsRecipientAdapter extends BaseAdapter implements AbsListView.RecyclerListener {
private final Context context; private final Context context;
private final MasterSecret masterSecret;
private final GlideRequests glideRequests; private final GlideRequests glideRequests;
private final MessageRecord record; private final MessageRecord record;
private final List<RecipientDeliveryStatus> members; private final List<RecipientDeliveryStatus> members;
private final boolean isPushGroup; private final boolean isPushGroup;
MessageDetailsRecipientAdapter(@NonNull Context context, @NonNull MasterSecret masterSecret, MessageDetailsRecipientAdapter(@NonNull Context context, @NonNull GlideRequests glideRequests,
@NonNull GlideRequests glideRequests, @NonNull MessageRecord record, @NonNull MessageRecord record, @NonNull List<RecipientDeliveryStatus> members,
@NonNull List<RecipientDeliveryStatus> members, boolean isPushGroup) boolean isPushGroup)
{ {
this.context = context; this.context = context;
this.masterSecret = masterSecret;
this.glideRequests = glideRequests; this.glideRequests = glideRequests;
this.record = record; this.record = record;
this.isPushGroup = isPushGroup; this.isPushGroup = isPushGroup;
@ -66,7 +63,7 @@ class MessageDetailsRecipientAdapter extends BaseAdapter implements AbsListView.
RecipientDeliveryStatus member = members.get(position); RecipientDeliveryStatus member = members.get(position);
((MessageRecipientListItem)convertView).set(masterSecret, glideRequests, record, member, isPushGroup); ((MessageRecipientListItem)convertView).set(glideRequests, record, member, isPushGroup);
return convertView; return convertView;
} }

View File

@ -16,6 +16,7 @@
*/ */
package org.thoughtcrime.securesms; package org.thoughtcrime.securesms;
import android.annotation.SuppressLint;
import android.content.Context; import android.content.Context;
import android.os.AsyncTask; import android.os.AsyncTask;
import android.text.TextUtils; import android.text.TextUtils;
@ -29,7 +30,6 @@ import org.thoughtcrime.securesms.MessageDetailsRecipientAdapter.RecipientDelive
import org.thoughtcrime.securesms.components.AvatarImageView; import org.thoughtcrime.securesms.components.AvatarImageView;
import org.thoughtcrime.securesms.components.DeliveryStatusView; import org.thoughtcrime.securesms.components.DeliveryStatusView;
import org.thoughtcrime.securesms.components.FromTextView; import org.thoughtcrime.securesms.components.FromTextView;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.MmsDatabase; import org.thoughtcrime.securesms.database.MmsDatabase;
import org.thoughtcrime.securesms.database.documents.IdentityKeyMismatch; import org.thoughtcrime.securesms.database.documents.IdentityKeyMismatch;
@ -49,6 +49,7 @@ import org.thoughtcrime.securesms.util.Util;
public class MessageRecipientListItem extends RelativeLayout public class MessageRecipientListItem extends RelativeLayout
implements RecipientModifiedListener implements RecipientModifiedListener
{ {
@SuppressWarnings("unused")
private final static String TAG = MessageRecipientListItem.class.getSimpleName(); private final static String TAG = MessageRecipientListItem.class.getSimpleName();
private RecipientDeliveryStatus member; private RecipientDeliveryStatus member;
@ -81,8 +82,7 @@ public class MessageRecipientListItem extends RelativeLayout
this.deliveryStatusView = findViewById(R.id.delivery_status); this.deliveryStatusView = findViewById(R.id.delivery_status);
} }
public void set(final MasterSecret masterSecret, public void set(final GlideRequests glideRequests,
final GlideRequests glideRequests,
final MessageRecord record, final MessageRecord record,
final RecipientDeliveryStatus member, final RecipientDeliveryStatus member,
final boolean isPushGroup) final boolean isPushGroup)
@ -93,11 +93,10 @@ public class MessageRecipientListItem extends RelativeLayout
member.getRecipient().addListener(this); member.getRecipient().addListener(this);
fromView.setText(member.getRecipient()); fromView.setText(member.getRecipient());
contactPhotoImage.setAvatar(glideRequests, member.getRecipient(), false); contactPhotoImage.setAvatar(glideRequests, member.getRecipient(), false);
setIssueIndicators(masterSecret, record, isPushGroup); setIssueIndicators(record, isPushGroup);
} }
private void setIssueIndicators(final MasterSecret masterSecret, private void setIssueIndicators(final MessageRecord record,
final MessageRecord record,
final boolean isPushGroup) final boolean isPushGroup)
{ {
final NetworkFailure networkFailure = getNetworkFailure(record); final NetworkFailure networkFailure = getNetworkFailure(record);
@ -110,7 +109,7 @@ public class MessageRecipientListItem extends RelativeLayout
conflictButton.setVisibility(View.VISIBLE); conflictButton.setVisibility(View.VISIBLE);
errorText = getContext().getString(R.string.MessageDetailsRecipient_new_safety_number); errorText = getContext().getString(R.string.MessageDetailsRecipient_new_safety_number);
conflictButton.setOnClickListener(v -> new ConfirmIdentityDialog(getContext(), masterSecret, record, keyMismatch).show()); conflictButton.setOnClickListener(v -> new ConfirmIdentityDialog(getContext(), record, keyMismatch).show());
} else if (networkFailure != null || (!isPushGroup && record.isFailed())) { } else if (networkFailure != null || (!isPushGroup && record.isFailed())) {
resendButton.setVisibility(View.VISIBLE); resendButton.setVisibility(View.VISIBLE);
resendButton.setEnabled(true); resendButton.setEnabled(true);
@ -123,7 +122,7 @@ public class MessageRecipientListItem extends RelativeLayout
errorDescription.setVisibility(View.GONE); errorDescription.setVisibility(View.GONE);
actionDescription.setVisibility(View.VISIBLE); actionDescription.setVisibility(View.VISIBLE);
actionDescription.setText(R.string.message_recipients_list_item__resending); actionDescription.setText(R.string.message_recipients_list_item__resending);
new ResendAsyncTask(masterSecret, record, networkFailure).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); new ResendAsyncTask(record, networkFailure).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}); });
} else { } else {
if (record.isOutgoing()) { if (record.isOutgoing()) {
@ -185,15 +184,14 @@ public class MessageRecipientListItem extends RelativeLayout
}); });
} }
@SuppressLint("StaticFieldLeak")
private class ResendAsyncTask extends AsyncTask<Void,Void,Void> { private class ResendAsyncTask extends AsyncTask<Void,Void,Void> {
private final Context context; private final Context context;
private final MasterSecret masterSecret;
private final MessageRecord record; private final MessageRecord record;
private final NetworkFailure failure; private final NetworkFailure failure;
ResendAsyncTask(MasterSecret masterSecret, MessageRecord record, NetworkFailure failure) { ResendAsyncTask(MessageRecord record, NetworkFailure failure) {
this.context = getContext().getApplicationContext(); this.context = getContext().getApplicationContext();
this.masterSecret = masterSecret;
this.record = record; this.record = record;
this.failure = failure; this.failure = failure;
} }
@ -206,7 +204,7 @@ public class MessageRecipientListItem extends RelativeLayout
if (record.getRecipient().isPushGroupRecipient()) { if (record.getRecipient().isPushGroupRecipient()) {
MessageSender.resendGroupMessage(context, record, failure.getAddress()); MessageSender.resendGroupMessage(context, record, failure.getAddress());
} else { } else {
MessageSender.resend(context, masterSecret, record); MessageSender.resend(context, record);
} }
return null; return null;
} }

View File

@ -18,7 +18,6 @@ package org.thoughtcrime.securesms;
import android.os.AsyncTask; import android.os.AsyncTask;
import android.os.Bundle; import android.os.Bundle;
import android.support.v7.app.ActionBar;
import org.thoughtcrime.securesms.crypto.IdentityKeyUtil; import org.thoughtcrime.securesms.crypto.IdentityKeyUtil;
import org.thoughtcrime.securesms.crypto.MasterSecret; import org.thoughtcrime.securesms.crypto.MasterSecret;
@ -66,6 +65,7 @@ public class PassphraseCreateActivity extends PassphraseActivity {
MasterSecretUtil.generateAsymmetricMasterSecret(PassphraseCreateActivity.this, masterSecret); MasterSecretUtil.generateAsymmetricMasterSecret(PassphraseCreateActivity.this, masterSecret);
IdentityKeyUtil.generateIdentityKeys(PassphraseCreateActivity.this); IdentityKeyUtil.generateIdentityKeys(PassphraseCreateActivity.this);
VersionTracker.updateLastSeenVersion(PassphraseCreateActivity.this); VersionTracker.updateLastSeenVersion(PassphraseCreateActivity.this);
TextSecurePreferences.setLastExperienceVersionCode(PassphraseCreateActivity.this, Util.getCurrentApkReleaseVersion(PassphraseCreateActivity.this)); TextSecurePreferences.setLastExperienceVersionCode(PassphraseCreateActivity.this, Util.getCurrentApkReleaseVersion(PassphraseCreateActivity.this));
TextSecurePreferences.setPasswordDisabled(PassphraseCreateActivity.this, true); TextSecurePreferences.setPasswordDisabled(PassphraseCreateActivity.this, true);
TextSecurePreferences.setReadReceiptsEnabled(PassphraseCreateActivity.this, true); TextSecurePreferences.setReadReceiptsEnabled(PassphraseCreateActivity.this, true);

View File

@ -131,7 +131,7 @@ public class ShareActivity extends PassphraseRequiredActionBarActivity
public void onPause() { public void onPause() {
super.onPause(); super.onPause();
if (!isPassingAlongMedia && resolvedExtra != null) { if (!isPassingAlongMedia && resolvedExtra != null) {
PersistentBlobProvider.getInstance(this).delete(resolvedExtra); PersistentBlobProvider.getInstance(this).delete(this, resolvedExtra);
} }
if (!isFinishing()) { if (!isFinishing()) {
finish(); finish();
@ -334,7 +334,7 @@ public class ShareActivity extends PassphraseRequiredActionBarActivity
if (cursor != null) cursor.close(); if (cursor != null) cursor.close();
} }
return PersistentBlobProvider.getInstance(context).create(masterSecret, inputStream, mimeType, fileName, fileSize); return PersistentBlobProvider.getInstance(context).create(context, inputStream, mimeType, fileName, fileSize);
} catch (IOException ioe) { } catch (IOException ioe) {
Log.w(TAG, ioe); Log.w(TAG, ioe);
return null; return null;

View File

@ -618,7 +618,7 @@ public class VerifyIdentityActivity extends PassphraseRequiredActionBarActivity
isChecked ? VerifiedStatus.VERIFIED : isChecked ? VerifiedStatus.VERIFIED :
VerifiedStatus.DEFAULT)); VerifiedStatus.DEFAULT));
IdentityUtil.markIdentityVerified(getActivity(), new MasterSecretUnion(masterSecret), recipient, isChecked, false); IdentityUtil.markIdentityVerified(getActivity(), recipient, isChecked, false);
} }
return null; return null;
} }

View File

@ -7,7 +7,6 @@ import android.support.annotation.NonNull;
import android.util.Log; import android.util.Log;
import org.spongycastle.util.encoders.Hex; import org.spongycastle.util.encoders.Hex;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.mms.PartAuthority; import org.thoughtcrime.securesms.mms.PartAuthority;
import org.thoughtcrime.securesms.util.Util; import org.thoughtcrime.securesms.util.Util;
@ -38,7 +37,6 @@ public class AttachmentServer implements Runnable {
private static final String TAG = AttachmentServer.class.getSimpleName(); private static final String TAG = AttachmentServer.class.getSimpleName();
private final Context context; private final Context context;
private final MasterSecret masterSecret;
private final Attachment attachment; private final Attachment attachment;
private final ServerSocket socket; private final ServerSocket socket;
private final int port; private final int port;
@ -46,12 +44,11 @@ public class AttachmentServer implements Runnable {
private volatile boolean isRunning; private volatile boolean isRunning;
public AttachmentServer(Context context, MasterSecret masterSecret, Attachment attachment) public AttachmentServer(Context context, Attachment attachment)
throws IOException throws IOException
{ {
try { try {
this.context = context; this.context = context;
this.masterSecret = masterSecret;
this.attachment = attachment; this.attachment = attachment;
this.socket = new ServerSocket(0, 0, InetAddress.getByAddress(new byte[]{127, 0, 0, 1})); this.socket = new ServerSocket(0, 0, InetAddress.getByAddress(new byte[]{127, 0, 0, 1}));
this.port = socket.getLocalPort(); this.port = socket.getLocalPort();
@ -189,7 +186,7 @@ public class AttachmentServer implements Runnable {
} }
protected void execute() throws IOException { protected void execute() throws IOException {
InputStream inputStream = PartAuthority.getAttachmentStream(context, masterSecret, attachment.getDataUri()); InputStream inputStream = PartAuthority.getAttachmentStream(context, attachment.getDataUri());
long fileSize = attachment.getSize(); long fileSize = attachment.getSize();
String headers = ""; String headers = "";

View File

@ -4,9 +4,8 @@ import android.net.Uri;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import org.thoughtcrime.securesms.crypto.MasterSecretUnion;
import org.thoughtcrime.securesms.crypto.MediaKey;
import org.thoughtcrime.securesms.database.AttachmentDatabase; import org.thoughtcrime.securesms.database.AttachmentDatabase;
import org.thoughtcrime.securesms.util.Base64;
import org.whispersystems.libsignal.util.guava.Optional; import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.api.messages.SignalServiceAttachment; import org.whispersystems.signalservice.api.messages.SignalServiceAttachment;
@ -15,7 +14,7 @@ import java.util.List;
public class PointerAttachment extends Attachment { public class PointerAttachment extends Attachment {
public PointerAttachment(@NonNull String contentType, int transferState, long size, private PointerAttachment(@NonNull String contentType, int transferState, long size,
@Nullable String fileName, @NonNull String location, @Nullable String fileName, @NonNull String location,
@NonNull String key, @NonNull String relay, @NonNull String key, @NonNull String relay,
@Nullable byte[] digest, boolean voiceNote) @Nullable byte[] digest, boolean voiceNote)
@ -36,19 +35,20 @@ public class PointerAttachment extends Attachment {
} }
public static List<Attachment> forPointers(@NonNull MasterSecretUnion masterSecret, Optional<List<SignalServiceAttachment>> pointers) { public static List<Attachment> forPointers(Optional<List<SignalServiceAttachment>> pointers) {
List<Attachment> results = new LinkedList<>(); List<Attachment> results = new LinkedList<>();
if (pointers.isPresent()) { if (pointers.isPresent()) {
for (SignalServiceAttachment pointer : pointers.get()) { for (SignalServiceAttachment pointer : pointers.get()) {
if (pointer.isPointer()) { if (pointer.isPointer()) {
String encryptedKey = MediaKey.getEncrypted(masterSecret, pointer.asPointer().getKey()); String encodedKey = Base64.encodeBytes(pointer.asPointer().getKey());
results.add(new PointerAttachment(pointer.getContentType(), results.add(new PointerAttachment(pointer.getContentType(),
AttachmentDatabase.TRANSFER_PROGRESS_PENDING, AttachmentDatabase.TRANSFER_PROGRESS_PENDING,
pointer.asPointer().getSize().or(0), pointer.asPointer().getSize().or(0),
pointer.asPointer().getFileName().orNull(), pointer.asPointer().getFileName().orNull(),
String.valueOf(pointer.asPointer().getId()), String.valueOf(pointer.asPointer().getId()),
encryptedKey, pointer.asPointer().getRelay().orNull(), encodedKey, pointer.asPointer().getRelay().orNull(),
pointer.asPointer().getDigest().orNull(), pointer.asPointer().getDigest().orNull(),
pointer.asPointer().getVoiceNote())); pointer.asPointer().getVoiceNote()));
} }

View File

@ -9,7 +9,6 @@ import android.support.annotation.NonNull;
import android.util.Log; import android.util.Log;
import android.util.Pair; import android.util.Pair;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.providers.PersistentBlobProvider; import org.thoughtcrime.securesms.providers.PersistentBlobProvider;
import org.thoughtcrime.securesms.util.MediaUtil; import org.thoughtcrime.securesms.util.MediaUtil;
import org.thoughtcrime.securesms.util.ThreadUtil; import org.thoughtcrime.securesms.util.ThreadUtil;
@ -28,24 +27,20 @@ public class AudioRecorder {
private static final ExecutorService executor = ThreadUtil.newDynamicSingleThreadedExecutor(); private static final ExecutorService executor = ThreadUtil.newDynamicSingleThreadedExecutor();
private final Context context; private final Context context;
private final MasterSecret masterSecret;
private final PersistentBlobProvider blobProvider; private final PersistentBlobProvider blobProvider;
private AudioCodec audioCodec; private AudioCodec audioCodec;
private Uri captureUri; private Uri captureUri;
public AudioRecorder(@NonNull Context context, @NonNull MasterSecret masterSecret) { public AudioRecorder(@NonNull Context context) {
this.context = context; this.context = context;
this.masterSecret = masterSecret;
this.blobProvider = PersistentBlobProvider.getInstance(context.getApplicationContext()); this.blobProvider = PersistentBlobProvider.getInstance(context.getApplicationContext());
} }
public void startRecording() { public void startRecording() {
Log.w(TAG, "startRecording()"); Log.w(TAG, "startRecording()");
executor.execute(new Runnable() { executor.execute(() -> {
@Override
public void run() {
Log.w(TAG, "Running startRecording() + " + Thread.currentThread().getId()); Log.w(TAG, "Running startRecording() + " + Thread.currentThread().getId());
try { try {
if (audioCodec != null) { if (audioCodec != null) {
@ -54,8 +49,7 @@ public class AudioRecorder {
ParcelFileDescriptor fds[] = ParcelFileDescriptor.createPipe(); ParcelFileDescriptor fds[] = ParcelFileDescriptor.createPipe();
captureUri = blobProvider.create(masterSecret, captureUri = blobProvider.create(context, new ParcelFileDescriptor.AutoCloseInputStream(fds[0]),
new ParcelFileDescriptor.AutoCloseInputStream(fds[0]),
MediaUtil.AUDIO_AAC, null, null); MediaUtil.AUDIO_AAC, null, null);
audioCodec = new AudioCodec(); audioCodec = new AudioCodec();
@ -63,7 +57,6 @@ public class AudioRecorder {
} catch (IOException e) { } catch (IOException e) {
Log.w(TAG, e); Log.w(TAG, e);
} }
}
}); });
} }
@ -72,9 +65,7 @@ public class AudioRecorder {
final SettableFuture<Pair<Uri, Long>> future = new SettableFuture<>(); final SettableFuture<Pair<Uri, Long>> future = new SettableFuture<>();
executor.execute(new Runnable() { executor.execute(() -> {
@Override
public void run() {
if (audioCodec == null) { if (audioCodec == null) {
sendToFuture(future, new IOException("MediaRecorder was never initialized successfully!")); sendToFuture(future, new IOException("MediaRecorder was never initialized successfully!"));
return; return;
@ -83,7 +74,7 @@ public class AudioRecorder {
audioCodec.stop(); audioCodec.stop();
try { try {
long size = MediaUtil.getMediaSize(context, masterSecret, captureUri); long size = MediaUtil.getMediaSize(context, captureUri);
sendToFuture(future, new Pair<>(captureUri, size)); sendToFuture(future, new Pair<>(captureUri, size));
} catch (IOException ioe) { } catch (IOException ioe) {
Log.w(TAG, ioe); Log.w(TAG, ioe);
@ -92,27 +83,16 @@ public class AudioRecorder {
audioCodec = null; audioCodec = null;
captureUri = null; captureUri = null;
}
}); });
return future; return future;
} }
private <T> void sendToFuture(final SettableFuture<T> future, final Exception exception) { private <T> void sendToFuture(final SettableFuture<T> future, final Exception exception) {
Util.runOnMain(new Runnable() { Util.runOnMain(() -> future.setException(exception));
@Override
public void run() {
future.setException(exception);
}
});
} }
private <T> void sendToFuture(final SettableFuture<T> future, final T result) { private <T> void sendToFuture(final SettableFuture<T> future, final T result) {
Util.runOnMain(new Runnable() { Util.runOnMain(() -> future.set(result));
@Override
public void run() {
future.set(result);
}
});
} }
} }

View File

@ -36,7 +36,6 @@ public class AudioSlidePlayer implements SensorEventListener {
private static @NonNull Optional<AudioSlidePlayer> playing = Optional.absent(); private static @NonNull Optional<AudioSlidePlayer> playing = Optional.absent();
private final @NonNull Context context; private final @NonNull Context context;
private final @NonNull MasterSecret masterSecret;
private final @NonNull AudioSlide slide; private final @NonNull AudioSlide slide;
private final @NonNull Handler progressEventHandler; private final @NonNull Handler progressEventHandler;
private final @NonNull AudioManager audioManager; private final @NonNull AudioManager audioManager;
@ -50,7 +49,6 @@ public class AudioSlidePlayer implements SensorEventListener {
private long startTime; private long startTime;
public synchronized static AudioSlidePlayer createFor(@NonNull Context context, public synchronized static AudioSlidePlayer createFor(@NonNull Context context,
@NonNull MasterSecret masterSecret,
@NonNull AudioSlide slide, @NonNull AudioSlide slide,
@NonNull Listener listener) @NonNull Listener listener)
{ {
@ -58,17 +56,15 @@ public class AudioSlidePlayer implements SensorEventListener {
playing.get().setListener(listener); playing.get().setListener(listener);
return playing.get(); return playing.get();
} else { } else {
return new AudioSlidePlayer(context, masterSecret, slide, listener); return new AudioSlidePlayer(context, slide, listener);
} }
} }
private AudioSlidePlayer(@NonNull Context context, private AudioSlidePlayer(@NonNull Context context,
@NonNull MasterSecret masterSecret,
@NonNull AudioSlide slide, @NonNull AudioSlide slide,
@NonNull Listener listener) @NonNull Listener listener)
{ {
this.context = context; this.context = context;
this.masterSecret = masterSecret;
this.slide = slide; this.slide = slide;
this.listener = new WeakReference<>(listener); this.listener = new WeakReference<>(listener);
this.progressEventHandler = new ProgressEventHandler(this); this.progressEventHandler = new ProgressEventHandler(this);
@ -91,7 +87,7 @@ public class AudioSlidePlayer implements SensorEventListener {
if (this.mediaPlayer != null) return; if (this.mediaPlayer != null) return;
this.mediaPlayer = new MediaPlayerWrapper(); this.mediaPlayer = new MediaPlayerWrapper();
this.audioAttachmentServer = new AttachmentServer(context, masterSecret, slide.asAttachment()); this.audioAttachmentServer = new AttachmentServer(context, slide.asAttachment());
this.startTime = System.currentTimeMillis(); this.startTime = System.currentTimeMillis();
audioAttachmentServer.start(); audioAttachmentServer.start();

View File

@ -27,7 +27,6 @@ import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode; import org.greenrobot.eventbus.ThreadMode;
import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.audio.AudioSlidePlayer; import org.thoughtcrime.securesms.audio.AudioSlidePlayer;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.database.AttachmentDatabase; import org.thoughtcrime.securesms.database.AttachmentDatabase;
import org.thoughtcrime.securesms.events.PartProgressEvent; import org.thoughtcrime.securesms.events.PartProgressEvent;
import org.thoughtcrime.securesms.mms.AudioSlide; import org.thoughtcrime.securesms.mms.AudioSlide;
@ -108,8 +107,7 @@ public class AudioView extends FrameLayout implements AudioSlidePlayer.Listener
EventBus.getDefault().unregister(this); EventBus.getDefault().unregister(this);
} }
public void setAudio(final @NonNull MasterSecret masterSecret, public void setAudio(final @NonNull AudioSlide audio,
final @NonNull AudioSlide audio,
final boolean showControls) final boolean showControls)
{ {
@ -128,7 +126,7 @@ public class AudioView extends FrameLayout implements AudioSlidePlayer.Listener
if (downloadProgress.isSpinning()) downloadProgress.stopSpinning(); if (downloadProgress.isSpinning()) downloadProgress.stopSpinning();
} }
this.audioSlidePlayer = AudioSlidePlayer.createFor(getContext(), masterSecret, audio, this); this.audioSlidePlayer = AudioSlidePlayer.createFor(getContext(), audio, this);
} }
public void cleanup() { public void cleanup() {

View File

@ -13,7 +13,6 @@ import android.view.Window;
import android.widget.FrameLayout; import android.widget.FrameLayout;
import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.mms.GlideRequests; import org.thoughtcrime.securesms.mms.GlideRequests;
import org.thoughtcrime.securesms.mms.VideoSlide; import org.thoughtcrime.securesms.mms.VideoSlide;
import org.thoughtcrime.securesms.util.views.Stub; import org.thoughtcrime.securesms.util.views.Stub;
@ -54,8 +53,7 @@ public class MediaView extends FrameLayout {
this.videoView = new Stub<>(findViewById(R.id.video_player_stub)); this.videoView = new Stub<>(findViewById(R.id.video_player_stub));
} }
public void set(@NonNull MasterSecret masterSecret, public void set(@NonNull GlideRequests glideRequests,
@NonNull GlideRequests glideRequests,
@NonNull Window window, @NonNull Window window,
@NonNull Uri source, @NonNull Uri source,
@NonNull String mediaType, @NonNull String mediaType,
@ -66,12 +64,12 @@ public class MediaView extends FrameLayout {
if (mediaType.startsWith("image/")) { if (mediaType.startsWith("image/")) {
imageView.setVisibility(View.VISIBLE); imageView.setVisibility(View.VISIBLE);
if (videoView.resolved()) videoView.get().setVisibility(View.GONE); if (videoView.resolved()) videoView.get().setVisibility(View.GONE);
imageView.setImageUri(masterSecret, glideRequests, source, mediaType); imageView.setImageUri(glideRequests, source, mediaType);
} else if (mediaType.startsWith("video/")) { } else if (mediaType.startsWith("video/")) {
imageView.setVisibility(View.GONE); imageView.setVisibility(View.GONE);
videoView.get().setVisibility(View.VISIBLE); videoView.get().setVisibility(View.VISIBLE);
videoView.get().setWindow(window); videoView.get().setWindow(window);
videoView.get().setVideoSource(masterSecret, new VideoSlide(getContext(), source, size), autoplay); videoView.get().setVideoSource(new VideoSlide(getContext(), source, size), autoplay);
} else { } else {
throw new IOException("Unsupported media type: " + mediaType); throw new IOException("Unsupported media type: " + mediaType);
} }

View File

@ -91,11 +91,11 @@ public class ThreadPhotoRailView extends FrameLayout {
@Override @Override
public void onBindItemViewHolder(ThreadPhotoViewHolder viewHolder, @NonNull Cursor cursor) { public void onBindItemViewHolder(ThreadPhotoViewHolder viewHolder, @NonNull Cursor cursor) {
ThumbnailView imageView = viewHolder.imageView; ThumbnailView imageView = viewHolder.imageView;
MediaDatabase.MediaRecord mediaRecord = MediaDatabase.MediaRecord.from(getContext(), masterSecret, cursor); MediaDatabase.MediaRecord mediaRecord = MediaDatabase.MediaRecord.from(getContext(), cursor);
Slide slide = MediaUtil.getSlideForAttachment(getContext(), mediaRecord.getAttachment()); Slide slide = MediaUtil.getSlideForAttachment(getContext(), mediaRecord.getAttachment());
if (slide != null) { if (slide != null) {
imageView.setImageResource(masterSecret, glideRequests, slide, false, false); imageView.setImageResource(glideRequests, slide, false, false);
} }
imageView.setOnClickListener(v -> { imageView.setOnClickListener(v -> {

View File

@ -17,7 +17,6 @@ import com.bumptech.glide.load.resource.bitmap.RoundedCorners;
import com.bumptech.glide.request.RequestOptions; import com.bumptech.glide.request.RequestOptions;
import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.database.AttachmentDatabase; import org.thoughtcrime.securesms.database.AttachmentDatabase;
import org.thoughtcrime.securesms.mms.DecryptableStreamUriLoader.DecryptableUri; import org.thoughtcrime.securesms.mms.DecryptableStreamUriLoader.DecryptableUri;
import org.thoughtcrime.securesms.mms.GlideRequests; import org.thoughtcrime.securesms.mms.GlideRequests;
@ -58,8 +57,8 @@ public class ThumbnailView extends FrameLayout {
inflate(context, R.layout.thumbnail_view, this); inflate(context, R.layout.thumbnail_view, this);
this.radius = getResources().getDimensionPixelSize(R.dimen.message_bubble_corner_radius); this.radius = getResources().getDimensionPixelSize(R.dimen.message_bubble_corner_radius);
this.image = (ImageView) findViewById(R.id.thumbnail_image); this.image = findViewById(R.id.thumbnail_image);
this.playOverlay = (ImageView) findViewById(R.id.play_overlay); this.playOverlay = findViewById(R.id.play_overlay);
super.setOnClickListener(new ThumbnailClickDispatcher()); super.setOnClickListener(new ThumbnailClickDispatcher());
if (attrs != null) { if (attrs != null) {
@ -88,7 +87,7 @@ public class ThumbnailView extends FrameLayout {
private TransferControlView getTransferControls() { private TransferControlView getTransferControls() {
if (!transferControls.isPresent()) { if (!transferControls.isPresent()) {
transferControls = Optional.of((TransferControlView)ViewUtil.inflateStub(this, R.id.transfer_controls_stub)); transferControls = Optional.of(ViewUtil.inflateStub(this, R.id.transfer_controls_stub));
} }
return transferControls.get(); return transferControls.get();
} }
@ -97,8 +96,8 @@ public class ThumbnailView extends FrameLayout {
this.backgroundColorHint = color; this.backgroundColorHint = color;
} }
public void setImageResource(@NonNull MasterSecret masterSecret, @NonNull GlideRequests glideRequests, public void setImageResource(@NonNull GlideRequests glideRequests, @NonNull Slide slide,
@NonNull Slide slide, boolean showControls, boolean isPreview) boolean showControls, boolean isPreview)
{ {
if (showControls) { if (showControls) {
getTransferControls().setSlide(slide); getTransferControls().setSlide(slide);
@ -134,15 +133,15 @@ public class ThumbnailView extends FrameLayout {
this.slide = slide; this.slide = slide;
if (slide.getThumbnailUri() != null) buildThumbnailGlideRequest(masterSecret, glideRequests, slide).into(image); if (slide.getThumbnailUri() != null) buildThumbnailGlideRequest(glideRequests, slide).into(image);
else if (slide.hasPlaceholder()) buildPlaceholderGlideRequest(glideRequests, slide).into(image); else if (slide.hasPlaceholder()) buildPlaceholderGlideRequest(glideRequests, slide).into(image);
else glideRequests.clear(image); else glideRequests.clear(image);
} }
public void setImageResource(@NonNull MasterSecret masterSecret, @NonNull GlideRequests glideRequests, @NonNull Uri uri) { public void setImageResource(@NonNull GlideRequests glideRequests, @NonNull Uri uri) {
if (transferControls.isPresent()) getTransferControls().setVisibility(View.GONE); if (transferControls.isPresent()) getTransferControls().setVisibility(View.GONE);
glideRequests.load(new DecryptableUri(masterSecret, uri)) glideRequests.load(new DecryptableUri(uri))
.diskCacheStrategy(DiskCacheStrategy.NONE) .diskCacheStrategy(DiskCacheStrategy.NONE)
.transform(new RoundedCorners(radius)) .transform(new RoundedCorners(radius))
.transition(withCrossFade()) .transition(withCrossFade())
@ -172,8 +171,8 @@ public class ThumbnailView extends FrameLayout {
getTransferControls().showProgressSpinner(); getTransferControls().showProgressSpinner();
} }
private RequestBuilder buildThumbnailGlideRequest(@NonNull MasterSecret masterSecret, @NonNull GlideRequests glideRequests, @NonNull Slide slide) { private RequestBuilder buildThumbnailGlideRequest(@NonNull GlideRequests glideRequests, @NonNull Slide slide) {
RequestBuilder builder = glideRequests.load(new DecryptableUri(masterSecret, slide.getThumbnailUri())) RequestBuilder builder = glideRequests.load(new DecryptableUri(slide.getThumbnailUri()))
.diskCacheStrategy(DiskCacheStrategy.NONE) .diskCacheStrategy(DiskCacheStrategy.NONE)
.transform(new RoundedCorners(radius)) .transform(new RoundedCorners(radius))
.centerCrop() .centerCrop()

View File

@ -22,7 +22,6 @@ import com.github.chrisbanes.photoview.PhotoView;
import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.components.subsampling.AttachmentBitmapDecoder; import org.thoughtcrime.securesms.components.subsampling.AttachmentBitmapDecoder;
import org.thoughtcrime.securesms.components.subsampling.AttachmentRegionDecoder; import org.thoughtcrime.securesms.components.subsampling.AttachmentRegionDecoder;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.mms.DecryptableStreamUriLoader.DecryptableUri; import org.thoughtcrime.securesms.mms.DecryptableStreamUriLoader.DecryptableUri;
import org.thoughtcrime.securesms.mms.GlideRequests; import org.thoughtcrime.securesms.mms.GlideRequests;
import org.thoughtcrime.securesms.mms.PartAuthority; import org.thoughtcrime.securesms.mms.PartAuthority;
@ -61,8 +60,7 @@ public class ZoomingImageView extends FrameLayout {
} }
@SuppressLint("StaticFieldLeak") @SuppressLint("StaticFieldLeak")
public void setImageUri(@NonNull MasterSecret masterSecret, @NonNull GlideRequests glideRequests, public void setImageUri(@NonNull GlideRequests glideRequests, @NonNull Uri uri, @NonNull String contentType)
@NonNull Uri uri, @NonNull String contentType)
{ {
final Context context = getContext(); final Context context = getContext();
final int maxTextureSize = BitmapUtil.getMaxTextureSize(); final int maxTextureSize = BitmapUtil.getMaxTextureSize();
@ -75,7 +73,7 @@ public class ZoomingImageView extends FrameLayout {
if (MediaUtil.isGif(contentType)) return null; if (MediaUtil.isGif(contentType)) return null;
try { try {
InputStream inputStream = PartAuthority.getAttachmentStream(context, masterSecret, uri); InputStream inputStream = PartAuthority.getAttachmentStream(context, uri);
return BitmapUtil.getDimensions(inputStream); return BitmapUtil.getDimensions(inputStream);
} catch (IOException | BitmapDecodingException e) { } catch (IOException | BitmapDecodingException e) {
Log.w(TAG, e); Log.w(TAG, e);
@ -88,29 +86,29 @@ public class ZoomingImageView extends FrameLayout {
if (dimensions == null || (dimensions.first <= maxTextureSize && dimensions.second <= maxTextureSize)) { if (dimensions == null || (dimensions.first <= maxTextureSize && dimensions.second <= maxTextureSize)) {
Log.w(TAG, "Loading in standard image view..."); Log.w(TAG, "Loading in standard image view...");
setImageViewUri(masterSecret, glideRequests, uri); setImageViewUri(glideRequests, uri);
} else { } else {
Log.w(TAG, "Loading in subsampling image view..."); Log.w(TAG, "Loading in subsampling image view...");
setSubsamplingImageViewUri(masterSecret, uri); setSubsamplingImageViewUri(uri);
} }
} }
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
} }
private void setImageViewUri(@NonNull MasterSecret masterSecret, @NonNull GlideRequests glideRequests, @NonNull Uri uri) { private void setImageViewUri(@NonNull GlideRequests glideRequests, @NonNull Uri uri) {
photoView.setVisibility(View.VISIBLE); photoView.setVisibility(View.VISIBLE);
subsamplingImageView.setVisibility(View.GONE); subsamplingImageView.setVisibility(View.GONE);
glideRequests.load(new DecryptableUri(masterSecret, uri)) glideRequests.load(new DecryptableUri(uri))
.diskCacheStrategy(DiskCacheStrategy.NONE) .diskCacheStrategy(DiskCacheStrategy.NONE)
.dontTransform() .dontTransform()
.override(Target.SIZE_ORIGINAL, Target.SIZE_ORIGINAL) .override(Target.SIZE_ORIGINAL, Target.SIZE_ORIGINAL)
.into(photoView); .into(photoView);
} }
private void setSubsamplingImageViewUri(@NonNull MasterSecret masterSecret, @NonNull Uri uri) { private void setSubsamplingImageViewUri(@NonNull Uri uri) {
subsamplingImageView.setBitmapDecoderFactory(new AttachmentBitmapDecoderFactory(masterSecret)); subsamplingImageView.setBitmapDecoderFactory(new AttachmentBitmapDecoderFactory());
subsamplingImageView.setRegionDecoderFactory(new AttachmentRegionDecoderFactory(masterSecret)); subsamplingImageView.setRegionDecoderFactory(new AttachmentRegionDecoderFactory());
subsamplingImageView.setVisibility(View.VISIBLE); subsamplingImageView.setVisibility(View.VISIBLE);
photoView.setVisibility(View.GONE); photoView.setVisibility(View.GONE);
@ -124,31 +122,16 @@ public class ZoomingImageView extends FrameLayout {
} }
private static class AttachmentBitmapDecoderFactory implements DecoderFactory<AttachmentBitmapDecoder> { private static class AttachmentBitmapDecoderFactory implements DecoderFactory<AttachmentBitmapDecoder> {
private final MasterSecret masterSecret;
private AttachmentBitmapDecoderFactory(MasterSecret masterSecret) {
this.masterSecret = masterSecret;
}
@Override @Override
public AttachmentBitmapDecoder make() throws IllegalAccessException, InstantiationException { public AttachmentBitmapDecoder make() throws IllegalAccessException, InstantiationException {
return new AttachmentBitmapDecoder(masterSecret); return new AttachmentBitmapDecoder();
} }
} }
private static class AttachmentRegionDecoderFactory implements DecoderFactory<AttachmentRegionDecoder> { private static class AttachmentRegionDecoderFactory implements DecoderFactory<AttachmentRegionDecoder> {
private final MasterSecret masterSecret;
private AttachmentRegionDecoderFactory(@NonNull MasterSecret masterSecret) {
this.masterSecret = masterSecret;
}
@Override @Override
public AttachmentRegionDecoder make() throws IllegalAccessException, InstantiationException { public AttachmentRegionDecoder make() throws IllegalAccessException, InstantiationException {
return new AttachmentRegionDecoder(masterSecret); return new AttachmentRegionDecoder();
} }
} }
} }

View File

@ -22,7 +22,6 @@ public class SystemSmsImportReminder extends Reminder {
public void onClick(View v) { public void onClick(View v) {
Intent intent = new Intent(context, ApplicationMigrationService.class); Intent intent = new Intent(context, ApplicationMigrationService.class);
intent.setAction(ApplicationMigrationService.MIGRATE_DATABASE); intent.setAction(ApplicationMigrationService.MIGRATE_DATABASE);
intent.putExtra("master_secret", masterSecret);
context.startService(intent); context.startService(intent);
Intent nextIntent = new Intent(context, ConversationListActivity.class); Intent nextIntent = new Intent(context, ConversationListActivity.class);

View File

@ -4,26 +4,18 @@ package org.thoughtcrime.securesms.components.subsampling;
import android.content.Context; import android.content.Context;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.graphics.BitmapFactory; import android.graphics.BitmapFactory;
import android.graphics.Rect;
import android.net.Uri; import android.net.Uri;
import android.support.annotation.NonNull;
import com.davemorrissey.labs.subscaleview.decoder.ImageDecoder; import com.davemorrissey.labs.subscaleview.decoder.ImageDecoder;
import com.davemorrissey.labs.subscaleview.decoder.SkiaImageDecoder; import com.davemorrissey.labs.subscaleview.decoder.SkiaImageDecoder;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.mms.PartAuthority; import org.thoughtcrime.securesms.mms.PartAuthority;
import org.thoughtcrime.securesms.service.KeyCachingService;
import java.io.InputStream; import java.io.InputStream;
public class AttachmentBitmapDecoder implements ImageDecoder{ public class AttachmentBitmapDecoder implements ImageDecoder{
private final MasterSecret masterSecret; public AttachmentBitmapDecoder() {}
public AttachmentBitmapDecoder(@NonNull MasterSecret masterSecret) {
this.masterSecret = masterSecret;
}
@Override @Override
public Bitmap decode(Context context, Uri uri) throws Exception { public Bitmap decode(Context context, Uri uri) throws Exception {
@ -31,7 +23,7 @@ public class AttachmentBitmapDecoder implements ImageDecoder{
return new SkiaImageDecoder().decode(context, uri); return new SkiaImageDecoder().decode(context, uri);
} }
InputStream inputStream = PartAuthority.getAttachmentStream(context, masterSecret, uri); InputStream inputStream = PartAuthority.getAttachmentStream(context, uri);
try { try {
BitmapFactory.Options options = new BitmapFactory.Options(); BitmapFactory.Options options = new BitmapFactory.Options();

View File

@ -8,13 +8,11 @@ import android.graphics.BitmapRegionDecoder;
import android.graphics.Point; import android.graphics.Point;
import android.graphics.Rect; import android.graphics.Rect;
import android.net.Uri; import android.net.Uri;
import android.support.annotation.NonNull;
import android.util.Log; import android.util.Log;
import com.davemorrissey.labs.subscaleview.decoder.ImageRegionDecoder; import com.davemorrissey.labs.subscaleview.decoder.ImageRegionDecoder;
import com.davemorrissey.labs.subscaleview.decoder.SkiaImageRegionDecoder; import com.davemorrissey.labs.subscaleview.decoder.SkiaImageRegionDecoder;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.mms.PartAuthority; import org.thoughtcrime.securesms.mms.PartAuthority;
import java.io.InputStream; import java.io.InputStream;
@ -23,16 +21,10 @@ public class AttachmentRegionDecoder implements ImageRegionDecoder {
private static final String TAG = AttachmentRegionDecoder.class.getName(); private static final String TAG = AttachmentRegionDecoder.class.getName();
private final MasterSecret masterSecret;
private SkiaImageRegionDecoder passthrough; private SkiaImageRegionDecoder passthrough;
private BitmapRegionDecoder bitmapRegionDecoder; private BitmapRegionDecoder bitmapRegionDecoder;
public AttachmentRegionDecoder(@NonNull MasterSecret masterSecret) {
this.masterSecret = masterSecret;
}
@Override @Override
public Point init(Context context, Uri uri) throws Exception { public Point init(Context context, Uri uri) throws Exception {
Log.w(TAG, "Init!"); Log.w(TAG, "Init!");
@ -41,7 +33,7 @@ public class AttachmentRegionDecoder implements ImageRegionDecoder {
return passthrough.init(context, uri); return passthrough.init(context, uri);
} }
InputStream inputStream = PartAuthority.getAttachmentStream(context, masterSecret, uri); InputStream inputStream = PartAuthority.getAttachmentStream(context, uri);
this.bitmapRegionDecoder = BitmapRegionDecoder.newInstance(inputStream, false); this.bitmapRegionDecoder = BitmapRegionDecoder.newInstance(inputStream, false);
inputStream.close(); inputStream.close();

View File

@ -28,16 +28,14 @@ import android.text.TextUtils;
import android.util.Log; import android.util.Log;
import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.crypto.MasterCipher;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.database.Address; import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.RecipientDatabase; import org.thoughtcrime.securesms.database.RecipientDatabase;
import org.thoughtcrime.securesms.database.ThreadDatabase; import org.thoughtcrime.securesms.database.ThreadDatabase;
import org.thoughtcrime.securesms.database.model.ThreadRecord; import org.thoughtcrime.securesms.database.model.ThreadRecord;
import org.thoughtcrime.securesms.permissions.Permissions;
import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.util.NumberUtil; import org.thoughtcrime.securesms.util.NumberUtil;
import org.thoughtcrime.securesms.permissions.Permissions;
import java.util.ArrayList; import java.util.ArrayList;
@ -61,17 +59,14 @@ public class ContactsCursorLoader extends CursorLoader {
ContactsDatabase.CONTACT_TYPE_COLUMN}; ContactsDatabase.CONTACT_TYPE_COLUMN};
private final MasterSecret masterSecret;
private final String filter; private final String filter;
private final int mode; private final int mode;
private final boolean recents; private final boolean recents;
public ContactsCursorLoader(@NonNull Context context, @NonNull MasterSecret masterSecret, public ContactsCursorLoader(@NonNull Context context, int mode, String filter, boolean recents)
int mode, String filter, boolean recents)
{ {
super(context); super(context);
this.masterSecret = masterSecret;
this.filter = filter; this.filter = filter;
this.mode = mode; this.mode = mode;
this.recents = recents; this.recents = recents;
@ -88,7 +83,7 @@ public class ContactsCursorLoader extends CursorLoader {
MatrixCursor synthesizedContacts = new MatrixCursor(CONTACT_PROJECTION); MatrixCursor synthesizedContacts = new MatrixCursor(CONTACT_PROJECTION);
synthesizedContacts.addRow(new Object[] {getContext().getString(R.string.ContactsCursorLoader_recent_chats), "", ContactsContract.CommonDataKinds.Phone.TYPE_MOBILE, "", ContactsDatabase.DIVIDER_TYPE}); synthesizedContacts.addRow(new Object[] {getContext().getString(R.string.ContactsCursorLoader_recent_chats), "", ContactsContract.CommonDataKinds.Phone.TYPE_MOBILE, "", ContactsDatabase.DIVIDER_TYPE});
ThreadDatabase.Reader reader = threadDatabase.readerFor(recentConversations, new MasterCipher(masterSecret)); ThreadDatabase.Reader reader = threadDatabase.readerFor(recentConversations);
ThreadRecord threadRecord; ThreadRecord threadRecord;

View File

@ -8,7 +8,6 @@ import android.content.SyncResult;
import android.os.Bundle; import android.os.Bundle;
import android.util.Log; import android.util.Log;
import org.thoughtcrime.securesms.service.KeyCachingService;
import org.thoughtcrime.securesms.util.DirectoryHelper; import org.thoughtcrime.securesms.util.DirectoryHelper;
import org.thoughtcrime.securesms.util.TextSecurePreferences; import org.thoughtcrime.securesms.util.TextSecurePreferences;
@ -30,7 +29,7 @@ public class ContactsSyncAdapter extends AbstractThreadedSyncAdapter {
if (TextSecurePreferences.isPushRegistered(getContext())) { if (TextSecurePreferences.isPushRegistered(getContext())) {
try { try {
DirectoryHelper.refreshDirectory(getContext(), KeyCachingService.getMasterSecret(getContext()), true); DirectoryHelper.refreshDirectory(getContext(), true);
} catch (IOException e) { } catch (IOException e) {
Log.w(TAG, e); Log.w(TAG, e);
} }

View File

@ -0,0 +1,115 @@
package org.thoughtcrime.securesms.crypto;
import android.support.annotation.NonNull;
import android.util.Base64;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import org.thoughtcrime.securesms.util.JsonUtils;
import java.io.IOException;
/**
* Encapsulates the key material used to encrypt attachments on disk.
*
* There are two logical pieces of material, a deprecated set of keys used to encrypt
* legacy attachments, and a key that is used to encrypt attachments going forward.
*/
public class AttachmentSecret {
@JsonProperty
@JsonSerialize(using = ByteArraySerializer.class)
@JsonDeserialize(using = ByteArrayDeserializer.class)
private byte[] classicCipherKey;
@JsonProperty
@JsonSerialize(using = ByteArraySerializer.class)
@JsonDeserialize(using = ByteArrayDeserializer.class)
private byte[] classicMacKey;
@JsonProperty
@JsonSerialize(using = ByteArraySerializer.class)
@JsonDeserialize(using = ByteArrayDeserializer.class)
private byte[] modernKey;
public AttachmentSecret(byte[] classicCipherKey, byte[] classicMacKey, byte[] modernKey)
{
this.classicCipherKey = classicCipherKey;
this.classicMacKey = classicMacKey;
this.modernKey = modernKey;
}
@SuppressWarnings("unused")
public AttachmentSecret() {
}
@JsonIgnore
byte[] getClassicCipherKey() {
return classicCipherKey;
}
@JsonIgnore
byte[] getClassicMacKey() {
return classicMacKey;
}
@JsonIgnore
byte[] getModernKey() {
return modernKey;
}
@JsonIgnore
void setClassicCipherKey(byte[] classicCipherKey) {
this.classicCipherKey = classicCipherKey;
}
@JsonIgnore
void setClassicMacKey(byte[] classicMacKey) {
this.classicMacKey = classicMacKey;
}
public String serialize() {
try {
return JsonUtils.toJson(this);
} catch (IOException e) {
throw new AssertionError(e);
}
}
static AttachmentSecret fromString(@NonNull String value) {
try {
return JsonUtils.fromJson(value, AttachmentSecret.class);
} catch (IOException e) {
throw new AssertionError(e);
}
}
private static class ByteArraySerializer extends JsonSerializer<byte[]> {
@Override
public void serialize(byte[] value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
gen.writeString(Base64.encodeToString(value, Base64.NO_WRAP | Base64.NO_PADDING));
}
}
private static class ByteArrayDeserializer extends JsonDeserializer<byte[]> {
@Override
public byte[] deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
return Base64.decode(p.getValueAsString(), Base64.NO_WRAP | Base64.NO_PADDING);
}
}
}

View File

@ -0,0 +1,103 @@
package org.thoughtcrime.securesms.crypto;
import android.content.Context;
import android.os.Build;
import android.support.annotation.NonNull;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import java.security.SecureRandom;
/**
* A provider that is responsible for creating or retrieving the AttachmentSecret model.
*
* On modern Android, the serialized secrets are themselves encrypted using a key that lives
* in the system KeyStore, for whatever that is worth.
*/
public class AttachmentSecretProvider {
private static AttachmentSecretProvider provider;
public static synchronized AttachmentSecretProvider getInstance(@NonNull Context context) {
if (provider == null) provider = new AttachmentSecretProvider(context.getApplicationContext());
return provider;
}
private final Context context;
private AttachmentSecret attachmentSecret;
private AttachmentSecretProvider(@NonNull Context context) {
this.context = context.getApplicationContext();
}
public synchronized AttachmentSecret getOrCreateAttachmentSecret() {
if (attachmentSecret != null) return attachmentSecret;
String unencryptedSecret = TextSecurePreferences.getAttachmentUnencryptedSecret(context);
String encryptedSecret = TextSecurePreferences.getAttachmentEncryptedSecret(context);
if (unencryptedSecret != null) attachmentSecret = getUnencryptedAttachmentSecret(context, unencryptedSecret);
else if (encryptedSecret != null) attachmentSecret = getEncryptedAttachmentSecret(encryptedSecret);
else attachmentSecret = createAndStoreAttachmentSecret(context);
return attachmentSecret;
}
public synchronized AttachmentSecret setClassicKey(@NonNull Context context, @NonNull byte[] classicCipherKey, @NonNull byte[] classicMacKey) {
AttachmentSecret currentSecret = getOrCreateAttachmentSecret();
currentSecret.setClassicCipherKey(classicCipherKey);
currentSecret.setClassicMacKey(classicMacKey);
storeAttachmentSecret(context, attachmentSecret);
return attachmentSecret;
}
private AttachmentSecret getUnencryptedAttachmentSecret(@NonNull Context context, @NonNull String unencryptedSecret)
{
AttachmentSecret attachmentSecret = AttachmentSecret.fromString(unencryptedSecret);
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
return attachmentSecret;
} else {
KeyStoreHelper.SealedData encryptedSecret = KeyStoreHelper.seal(attachmentSecret.serialize().getBytes());
TextSecurePreferences.setAttachmentEncryptedSecret(context, encryptedSecret.serialize());
TextSecurePreferences.setAttachmentUnencryptedSecret(context, null);
return attachmentSecret;
}
}
private AttachmentSecret getEncryptedAttachmentSecret(@NonNull String serializedEncryptedSecret) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
throw new AssertionError("OS downgrade not supported. KeyStore sealed data exists on platform < M!");
} else {
KeyStoreHelper.SealedData encryptedSecret = KeyStoreHelper.SealedData.fromString(serializedEncryptedSecret);
return AttachmentSecret.fromString(new String(KeyStoreHelper.unseal(encryptedSecret)));
}
}
private AttachmentSecret createAndStoreAttachmentSecret(@NonNull Context context) {
SecureRandom random = new SecureRandom();
byte[] secret = new byte[32];
random.nextBytes(secret);
AttachmentSecret attachmentSecret = new AttachmentSecret(null, null, secret);
storeAttachmentSecret(context, attachmentSecret);
return attachmentSecret;
}
private void storeAttachmentSecret(@NonNull Context context, @NonNull AttachmentSecret attachmentSecret) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
KeyStoreHelper.SealedData encryptedSecret = KeyStoreHelper.seal(attachmentSecret.serialize().getBytes());
TextSecurePreferences.setAttachmentEncryptedSecret(context, encryptedSecret.serialize());
} else {
TextSecurePreferences.setAttachmentUnencryptedSecret(context, attachmentSecret.serialize());
}
}
}

View File

@ -16,6 +16,7 @@
*/ */
package org.thoughtcrime.securesms.crypto; package org.thoughtcrime.securesms.crypto;
import android.support.annotation.NonNull;
import android.util.Log; import android.util.Log;
import org.thoughtcrime.securesms.util.LimitedInputStream; import org.thoughtcrime.securesms.util.LimitedInputStream;
@ -37,14 +38,14 @@ import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec; import javax.crypto.spec.SecretKeySpec;
public class DecryptingPartInputStream { public class ClassicDecryptingPartInputStream {
private static final String TAG = DecryptingPartInputStream.class.getSimpleName(); private static final String TAG = ClassicDecryptingPartInputStream.class.getSimpleName();
private static final int IV_LENGTH = 16; private static final int IV_LENGTH = 16;
private static final int MAC_LENGTH = 20; private static final int MAC_LENGTH = 20;
public static InputStream createFor(MasterSecret masterSecret, File file) public static InputStream createFor(@NonNull AttachmentSecret attachmentSecret, @NonNull File file)
throws IOException throws IOException
{ {
try { try {
@ -52,7 +53,7 @@ public class DecryptingPartInputStream {
throw new IOException("File too short"); throw new IOException("File too short");
} }
verifyMac(masterSecret, file); verifyMac(attachmentSecret, file);
FileInputStream fileStream = new FileInputStream(file); FileInputStream fileStream = new FileInputStream(file);
byte[] ivBytes = new byte[IV_LENGTH]; byte[] ivBytes = new byte[IV_LENGTH];
@ -60,7 +61,7 @@ public class DecryptingPartInputStream {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
IvParameterSpec iv = new IvParameterSpec(ivBytes); IvParameterSpec iv = new IvParameterSpec(ivBytes);
cipher.init(Cipher.DECRYPT_MODE, masterSecret.getEncryptionKey(), iv); cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(attachmentSecret.getClassicCipherKey(), "AES"), iv);
return new CipherInputStreamWrapper(new LimitedInputStream(fileStream, file.length() - MAC_LENGTH - IV_LENGTH), cipher); return new CipherInputStreamWrapper(new LimitedInputStream(fileStream, file.length() - MAC_LENGTH - IV_LENGTH), cipher);
} catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | InvalidAlgorithmParameterException e) { } catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | InvalidAlgorithmParameterException e) {
@ -68,8 +69,8 @@ public class DecryptingPartInputStream {
} }
} }
private static void verifyMac(MasterSecret masterSecret, File file) throws IOException { private static void verifyMac(AttachmentSecret attachmentSecret, File file) throws IOException {
Mac mac = initializeMac(masterSecret.getMacKey()); Mac mac = initializeMac(new SecretKeySpec(attachmentSecret.getClassicMacKey(), "HmacSHA1"));
FileInputStream macStream = new FileInputStream(file); FileInputStream macStream = new FileInputStream(file);
InputStream dataStream = new LimitedInputStream(new FileInputStream(file), file.length() - MAC_LENGTH); InputStream dataStream = new LimitedInputStream(new FileInputStream(file), file.length() - MAC_LENGTH);
byte[] theirMac = new byte[MAC_LENGTH]; byte[] theirMac = new byte[MAC_LENGTH];

View File

@ -0,0 +1,32 @@
package org.thoughtcrime.securesms.crypto;
import android.support.annotation.NonNull;
import org.thoughtcrime.securesms.util.Hex;
import java.io.IOException;
public class DatabaseSecret {
private final byte[] key;
private final String encoded;
public DatabaseSecret(@NonNull byte[] key) {
this.key = key;
this.encoded = Hex.toStringCondensed(key);
}
public DatabaseSecret(@NonNull String encoded) throws IOException {
this.key = Hex.fromStringCondensed(encoded);
this.encoded = encoded;
}
public String asString() {
return encoded;
}
public byte[] asBytes() {
return key;
}
}

View File

@ -0,0 +1,78 @@
package org.thoughtcrime.securesms.crypto;
import android.content.Context;
import android.os.Build;
import android.support.annotation.NonNull;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import java.io.IOException;
import java.security.SecureRandom;
public class DatabaseSecretProvider {
@SuppressWarnings("unused")
private static final String TAG = DatabaseSecretProvider.class.getSimpleName();
private final Context context;
public DatabaseSecretProvider(@NonNull Context context) {
this.context = context.getApplicationContext();
}
public DatabaseSecret getOrCreateDatabaseSecret() {
String unencryptedSecret = TextSecurePreferences.getDatabaseUnencryptedSecret(context);
String encryptedSecret = TextSecurePreferences.getDatabaseEncryptedSecret(context);
if (unencryptedSecret != null) return getUnencryptedDatabaseSecret(context, unencryptedSecret);
else if (encryptedSecret != null) return getEncryptedDatabaseSecret(encryptedSecret);
else return createAndStoreDatabaseSecret(context);
}
private DatabaseSecret getUnencryptedDatabaseSecret(@NonNull Context context, @NonNull String unencryptedSecret)
{
try {
DatabaseSecret databaseSecret = new DatabaseSecret(unencryptedSecret);
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
return databaseSecret;
} else {
KeyStoreHelper.SealedData encryptedSecret = KeyStoreHelper.seal(databaseSecret.asBytes());
TextSecurePreferences.setDatabaseEncryptedSecret(context, encryptedSecret.serialize());
TextSecurePreferences.setDatabaseUnencryptedSecret(context, null);
return databaseSecret;
}
} catch (IOException e) {
throw new AssertionError(e);
}
}
private DatabaseSecret getEncryptedDatabaseSecret(@NonNull String serializedEncryptedSecret) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
throw new AssertionError("OS downgrade not supported. KeyStore sealed data exists on platform < M!");
} else {
KeyStoreHelper.SealedData encryptedSecret = KeyStoreHelper.SealedData.fromString(serializedEncryptedSecret);
return new DatabaseSecret(KeyStoreHelper.unseal(encryptedSecret));
}
}
private DatabaseSecret createAndStoreDatabaseSecret(@NonNull Context context) {
SecureRandom random = new SecureRandom();
byte[] secret = new byte[32];
random.nextBytes(secret);
DatabaseSecret databaseSecret = new DatabaseSecret(secret);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
KeyStoreHelper.SealedData encryptedSecret = KeyStoreHelper.seal(databaseSecret.asBytes());
TextSecurePreferences.setDatabaseEncryptedSecret(context, encryptedSecret.serialize());
} else {
TextSecurePreferences.setDatabaseUnencryptedSecret(context, databaseSecret.asString());
}
return databaseSecret;
}
}

View File

@ -1,122 +0,0 @@
/**
* Copyright (C) 2011 Whisper Systems
*
* 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
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.thoughtcrime.securesms.crypto;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.Mac;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.SecretKeySpec;
import android.util.Log;
/**
* A class for streaming an encrypted MMS "part" to disk.
*
* @author Moxie Marlinspike
*/
public class EncryptingPartOutputStream extends FileOutputStream {
private Cipher cipher;
private Mac mac;
private boolean closed;
public EncryptingPartOutputStream(File file, MasterSecret masterSecret) throws FileNotFoundException {
super(file);
try {
mac = initializeMac(masterSecret.getMacKey());
cipher = initializeCipher(mac, masterSecret.getEncryptionKey());
closed = false;
} catch (IOException ioe) {
Log.w("EncryptingPartOutputStream", ioe);
throw new FileNotFoundException("Couldn't write IV");
} catch (InvalidKeyException e) {
throw new AssertionError(e);
} catch (NoSuchAlgorithmException e) {
throw new AssertionError(e);
} catch (NoSuchPaddingException e) {
throw new AssertionError(e);
}
}
@Override
public void write(byte[] buffer) throws IOException {
this.write(buffer, 0, buffer.length);
}
@Override
public void write(byte[] buffer, int offset, int length) throws IOException {
byte[] encryptedBuffer = cipher.update(buffer, offset, length);
if (encryptedBuffer != null) {
mac.update(encryptedBuffer);
super.write(encryptedBuffer, 0, encryptedBuffer.length);
}
}
@Override
public void close() throws IOException {
try {
if (!closed) {
byte[] encryptedRemainder = cipher.doFinal();
mac.update(encryptedRemainder);
byte[] macBytes = mac.doFinal();
super.write(encryptedRemainder, 0, encryptedRemainder.length);
super.write(macBytes, 0, macBytes.length);
closed = true;
}
super.close();
} catch (BadPaddingException bpe) {
throw new AssertionError(bpe);
} catch (IllegalBlockSizeException e) {
throw new AssertionError(e);
}
}
private Mac initializeMac(SecretKeySpec key) throws NoSuchAlgorithmException, InvalidKeyException {
Mac hmac = Mac.getInstance("HmacSHA1");
hmac.init(key);
return hmac;
}
private Cipher initializeCipher(Mac mac, SecretKeySpec key) throws IOException, InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] ivBytes = cipher.getIV();
mac.update(ivBytes);
super.write(ivBytes, 0, ivBytes.length);
return cipher;
}
}

View File

@ -0,0 +1,180 @@
package org.thoughtcrime.securesms.crypto;
import android.os.Build;
import android.security.keystore.KeyGenParameterSpec;
import android.security.keystore.KeyProperties;
import android.support.annotation.NonNull;
import android.support.annotation.RequiresApi;
import android.util.Base64;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import org.thoughtcrime.securesms.util.JsonUtils;
import java.io.IOException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.UnrecoverableEntryException;
import java.security.cert.CertificateException;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.KeyGenerator;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.spec.GCMParameterSpec;
public class KeyStoreHelper {
private static final String ANDROID_KEY_STORE = "AndroidKeyStore";
private static final String KEY_ALIAS = "SignalSecret";
@RequiresApi(Build.VERSION_CODES.M)
public static SealedData seal(@NonNull byte[] input) {
SecretKey secretKey = getOrCreateKeyStoreEntry();
try {
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
byte[] iv = cipher.getIV();
byte[] data = cipher.doFinal(input);
return new SealedData(iv, data);
} catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | IllegalBlockSizeException | BadPaddingException e) {
throw new AssertionError(e);
}
}
@RequiresApi(Build.VERSION_CODES.M)
public static byte[] unseal(@NonNull SealedData sealedData) {
SecretKey secretKey = getKeyStoreEntry();
try {
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
cipher.init(Cipher.DECRYPT_MODE, secretKey, new GCMParameterSpec(128, sealedData.iv));
return cipher.doFinal(sealedData.data);
} catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | InvalidAlgorithmParameterException | IllegalBlockSizeException | BadPaddingException e) {
throw new AssertionError(e);
}
}
@RequiresApi(Build.VERSION_CODES.M)
private static SecretKey getOrCreateKeyStoreEntry() {
if (hasKeyStoreEntry()) return getKeyStoreEntry();
else return createKeyStoreEntry();
}
@RequiresApi(Build.VERSION_CODES.M)
private static SecretKey createKeyStoreEntry() {
try {
KeyGenerator keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, ANDROID_KEY_STORE);
KeyGenParameterSpec keyGenParameterSpec = new KeyGenParameterSpec.Builder(KEY_ALIAS, KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
.setBlockModes(KeyProperties.BLOCK_MODE_GCM)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
.build();
keyGenerator.init(keyGenParameterSpec);
return keyGenerator.generateKey();
} catch (NoSuchAlgorithmException | NoSuchProviderException | InvalidAlgorithmParameterException e) {
throw new AssertionError(e);
}
}
@RequiresApi(Build.VERSION_CODES.M)
private static SecretKey getKeyStoreEntry() {
try {
KeyStore keyStore = KeyStore.getInstance(ANDROID_KEY_STORE);
keyStore.load(null);
return ((KeyStore.SecretKeyEntry) keyStore.getEntry(KEY_ALIAS, null)).getSecretKey();
} catch (KeyStoreException | IOException | NoSuchAlgorithmException | CertificateException | UnrecoverableEntryException e) {
throw new AssertionError(e);
}
}
@RequiresApi(Build.VERSION_CODES.M)
private static boolean hasKeyStoreEntry() {
try {
KeyStore ks = KeyStore.getInstance(ANDROID_KEY_STORE);
ks.load(null);
return ks.containsAlias(KEY_ALIAS) && ks.entryInstanceOf(KEY_ALIAS, KeyStore.SecretKeyEntry.class);
} catch (KeyStoreException | IOException | NoSuchAlgorithmException | CertificateException e) {
throw new AssertionError(e);
}
}
public static class SealedData {
@SuppressWarnings("unused")
private static final String TAG = SealedData.class.getSimpleName();
@JsonProperty
@JsonSerialize(using = ByteArraySerializer.class)
@JsonDeserialize(using = ByteArrayDeserializer.class)
private byte[] iv;
@JsonProperty
@JsonSerialize(using = ByteArraySerializer.class)
@JsonDeserialize(using = ByteArrayDeserializer.class)
private byte[] data;
SealedData(@NonNull byte[] iv, @NonNull byte[] data) {
this.iv = iv;
this.data = data;
}
@SuppressWarnings("unused")
public SealedData() {}
public String serialize() {
try {
return JsonUtils.toJson(this);
} catch (IOException e) {
throw new AssertionError(e);
}
}
static SealedData fromString(@NonNull String value) {
try {
return JsonUtils.fromJson(value, SealedData.class);
} catch (IOException e) {
throw new AssertionError(e);
}
}
private static class ByteArraySerializer extends JsonSerializer<byte[]> {
@Override
public void serialize(byte[] value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
gen.writeString(Base64.encodeToString(value, Base64.NO_WRAP | Base64.NO_PADDING));
}
}
private static class ByteArrayDeserializer extends JsonDeserializer<byte[]> {
@Override
public byte[] deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
return Base64.decode(p.getValueAsString(), Base64.NO_WRAP | Base64.NO_PADDING);
}
}
}
}

View File

@ -1,31 +0,0 @@
package org.thoughtcrime.securesms.crypto;
import android.support.annotation.NonNull;
import org.thoughtcrime.securesms.util.Base64;
import org.whispersystems.libsignal.InvalidMessageException;
import java.io.IOException;
public class MediaKey {
public static String getEncrypted(@NonNull MasterSecretUnion masterSecret, @NonNull byte[] key) {
if (masterSecret.getMasterSecret().isPresent()) {
return Base64.encodeBytes(new MasterCipher(masterSecret.getMasterSecret().get()).encryptBytes(key));
} else {
return "?ASYNC-" + Base64.encodeBytes(new AsymmetricMasterCipher(masterSecret.getAsymmetricMasterSecret().get()).encryptBytes(key));
}
}
public static byte[] getDecrypted(@NonNull MasterSecret masterSecret,
@NonNull AsymmetricMasterSecret asymmetricMasterSecret,
@NonNull String encodedKey)
throws IOException, InvalidMessageException
{
if (encodedKey.startsWith("?ASYNC-")) {
return new AsymmetricMasterCipher(asymmetricMasterSecret).decryptBytes(Base64.decode(encodedKey.substring("?ASYNC-".length())));
} else {
return new MasterCipher(masterSecret).decryptBytes(Base64.decode(encodedKey));
}
}
}

View File

@ -0,0 +1,43 @@
package org.thoughtcrime.securesms.crypto;
import android.support.annotation.NonNull;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import javax.crypto.Cipher;
import javax.crypto.CipherInputStream;
import javax.crypto.Mac;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
public class ModernDecryptingPartInputStream {
public static InputStream createFor(@NonNull AttachmentSecret attachmentSecret, @NonNull byte[] random, @NonNull File file)
throws IOException
{
try {
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(new SecretKeySpec(attachmentSecret.getModernKey(), "HmacSHA256"));
FileInputStream fileInputStream = new FileInputStream(file);
byte[] iv = new byte[16];
byte[] key = mac.doFinal(random);
Cipher cipher = Cipher.getInstance("AES/CTR/NoPadding");
cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(key, "AES"), new IvParameterSpec(iv));
return new CipherInputStream(fileInputStream, cipher);
} catch (NoSuchAlgorithmException | InvalidKeyException | InvalidAlgorithmParameterException | NoSuchPaddingException e) {
throw new AssertionError(e);
}
}
}

View File

@ -0,0 +1,48 @@
package org.thoughtcrime.securesms.crypto;
import android.support.annotation.NonNull;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import javax.crypto.Cipher;
import javax.crypto.CipherOutputStream;
import javax.crypto.Mac;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
/**
* Constructs an OutputStream that encrypts data written to it with the AttachmentSecret provided.
*
* The on-disk format is very simple, and intentionally no longer includes authentication.
*/
public class ModernEncryptingPartOutputStream {
public static OutputStream createFor(@NonNull AttachmentSecret attachmentSecret, byte[] random, File file)
throws IOException
{
try {
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(new SecretKeySpec(attachmentSecret.getModernKey(), "HmacSHA256"));
FileOutputStream fileOutputStream = new FileOutputStream(file);
byte[] iv = new byte[16];
byte[] key = mac.doFinal(random);
Cipher cipher = Cipher.getInstance("AES/CTR/NoPadding");
cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key, "AES"), new IvParameterSpec(iv));
return new CipherOutputStream(fileOutputStream, cipher);
} catch (NoSuchAlgorithmException | InvalidKeyException | InvalidAlgorithmParameterException | NoSuchPaddingException e) {
throw new AssertionError(e);
}
}
}

View File

@ -15,12 +15,12 @@ import java.util.List;
public class SessionUtil { public class SessionUtil {
public static boolean hasSession(Context context, MasterSecret masterSecret, Recipient recipient) { public static boolean hasSession(Context context, Recipient recipient) {
return hasSession(context, masterSecret, recipient.getAddress()); return hasSession(context, recipient.getAddress());
} }
public static boolean hasSession(Context context, MasterSecret masterSecret, @NonNull Address address) { public static boolean hasSession(Context context, @NonNull Address address) {
SessionStore sessionStore = new TextSecureSessionStore(context, masterSecret); SessionStore sessionStore = new TextSecureSessionStore(context, null);
SignalProtocolAddress axolotlAddress = new SignalProtocolAddress(address.serialize(), SignalServiceAddress.DEFAULT_DEVICE_ID); SignalProtocolAddress axolotlAddress = new SignalProtocolAddress(address.serialize(), SignalServiceAddress.DEFAULT_DEVICE_ID);
return sessionStore.containsSession(axolotlAddress); return sessionStore.containsSession(axolotlAddress);

View File

@ -1,4 +1,4 @@
/** /*
* 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
@ -16,11 +16,10 @@
*/ */
package org.thoughtcrime.securesms.database; package org.thoughtcrime.securesms.database;
import android.annotation.SuppressLint;
import android.content.ContentValues; import android.content.ContentValues;
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.SQLiteOpenHelper;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.media.MediaMetadataRetriever; import android.media.MediaMetadataRetriever;
import android.net.Uri; import android.net.Uri;
@ -30,17 +29,17 @@ import android.support.annotation.Nullable;
import android.support.annotation.VisibleForTesting; import android.support.annotation.VisibleForTesting;
import android.text.TextUtils; import android.text.TextUtils;
import android.util.Log; import android.util.Log;
import android.util.Pair;
import org.thoughtcrime.securesms.ApplicationContext; import net.sqlcipher.database.SQLiteDatabase;
import org.thoughtcrime.securesms.attachments.Attachment; import org.thoughtcrime.securesms.attachments.Attachment;
import org.thoughtcrime.securesms.attachments.AttachmentId; import org.thoughtcrime.securesms.attachments.AttachmentId;
import org.thoughtcrime.securesms.attachments.DatabaseAttachment; import org.thoughtcrime.securesms.attachments.DatabaseAttachment;
import org.thoughtcrime.securesms.crypto.DecryptingPartInputStream; import org.thoughtcrime.securesms.crypto.AttachmentSecret;
import org.thoughtcrime.securesms.crypto.EncryptingPartOutputStream; import org.thoughtcrime.securesms.crypto.ClassicDecryptingPartInputStream;
import org.thoughtcrime.securesms.crypto.MasterCipher; import org.thoughtcrime.securesms.crypto.ModernDecryptingPartInputStream;
import org.thoughtcrime.securesms.crypto.MasterSecret; import org.thoughtcrime.securesms.crypto.ModernEncryptingPartOutputStream;
import org.thoughtcrime.securesms.crypto.MasterSecretUnion; import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper;
import org.thoughtcrime.securesms.mms.MediaStream; import org.thoughtcrime.securesms.mms.MediaStream;
import org.thoughtcrime.securesms.mms.MmsException; import org.thoughtcrime.securesms.mms.MmsException;
import org.thoughtcrime.securesms.mms.PartAuthority; import org.thoughtcrime.securesms.mms.PartAuthority;
@ -48,13 +47,13 @@ import org.thoughtcrime.securesms.util.MediaUtil;
import org.thoughtcrime.securesms.util.MediaUtil.ThumbnailData; import org.thoughtcrime.securesms.util.MediaUtil.ThumbnailData;
import org.thoughtcrime.securesms.util.Util; import org.thoughtcrime.securesms.util.Util;
import org.thoughtcrime.securesms.video.EncryptedMediaDataSource; import org.thoughtcrime.securesms.video.EncryptedMediaDataSource;
import org.whispersystems.libsignal.InvalidMessageException;
import java.io.File; import java.io.File;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.security.SecureRandom;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.concurrent.Callable; import java.util.concurrent.Callable;
@ -83,6 +82,8 @@ public class AttachmentDatabase extends Database {
static final String DIGEST = "digest"; static final String DIGEST = "digest";
static final String VOICE_NOTE = "voice_note"; static final String VOICE_NOTE = "voice_note";
public static final String FAST_PREFLIGHT_ID = "fast_preflight_id"; public static final String FAST_PREFLIGHT_ID = "fast_preflight_id";
static final String DATA_RANDOM = "data_random";
static final String THUMBNAIL_RANDOM = "thumbnail_random";
public static final int TRANSFER_PROGRESS_DONE = 0; public static final int TRANSFER_PROGRESS_DONE = 0;
public static final int TRANSFER_PROGRESS_STARTED = 1; public static final int TRANSFER_PROGRESS_STARTED = 1;
@ -95,7 +96,8 @@ public class AttachmentDatabase extends Database {
MMS_ID, CONTENT_TYPE, NAME, CONTENT_DISPOSITION, MMS_ID, CONTENT_TYPE, NAME, CONTENT_DISPOSITION,
CONTENT_LOCATION, DATA, THUMBNAIL, TRANSFER_STATE, CONTENT_LOCATION, DATA, THUMBNAIL, TRANSFER_STATE,
SIZE, FILE_NAME, THUMBNAIL, THUMBNAIL_ASPECT_RATIO, SIZE, FILE_NAME, THUMBNAIL, THUMBNAIL_ASPECT_RATIO,
UNIQUE_ID, DIGEST, FAST_PREFLIGHT_ID, VOICE_NOTE}; UNIQUE_ID, DIGEST, FAST_PREFLIGHT_ID, VOICE_NOTE,
DATA_RANDOM, THUMBNAIL_RANDOM};
public static final String CREATE_TABLE = "CREATE TABLE " + TABLE_NAME + " (" + ROW_ID + " INTEGER PRIMARY KEY, " + public static final String CREATE_TABLE = "CREATE TABLE " + TABLE_NAME + " (" + ROW_ID + " INTEGER PRIMARY KEY, " +
MMS_ID + " INTEGER, " + "seq" + " INTEGER DEFAULT 0, " + MMS_ID + " INTEGER, " + "seq" + " INTEGER DEFAULT 0, " +
@ -106,7 +108,7 @@ public class AttachmentDatabase extends Database {
TRANSFER_STATE + " INTEGER, "+ DATA + " TEXT, " + SIZE + " INTEGER, " + TRANSFER_STATE + " INTEGER, "+ DATA + " TEXT, " + SIZE + " INTEGER, " +
FILE_NAME + " TEXT, " + THUMBNAIL + " TEXT, " + THUMBNAIL_ASPECT_RATIO + " REAL, " + FILE_NAME + " TEXT, " + THUMBNAIL + " TEXT, " + THUMBNAIL_ASPECT_RATIO + " REAL, " +
UNIQUE_ID + " INTEGER NOT NULL, " + DIGEST + " BLOB, " + FAST_PREFLIGHT_ID + " TEXT, " + UNIQUE_ID + " INTEGER NOT NULL, " + DIGEST + " BLOB, " + FAST_PREFLIGHT_ID + " TEXT, " +
VOICE_NOTE + " INTEGER DEFAULT 0);"; VOICE_NOTE + " INTEGER DEFAULT 0, " + DATA_RANDOM + " BLOB, " + THUMBNAIL_RANDOM + " BLOB);";
public static final String[] CREATE_INDEXS = { public static final String[] CREATE_INDEXS = {
"CREATE INDEX IF NOT EXISTS part_mms_id_index ON " + TABLE_NAME + " (" + MMS_ID + ");", "CREATE INDEX IF NOT EXISTS part_mms_id_index ON " + TABLE_NAME + " (" + MMS_ID + ");",
@ -115,31 +117,34 @@ public class AttachmentDatabase extends Database {
private final ExecutorService thumbnailExecutor = Util.newSingleThreadedLifoExecutor(); private final ExecutorService thumbnailExecutor = Util.newSingleThreadedLifoExecutor();
public AttachmentDatabase(Context context, SQLiteOpenHelper databaseHelper) { private final AttachmentSecret attachmentSecret;
public AttachmentDatabase(Context context, SQLCipherOpenHelper databaseHelper, AttachmentSecret attachmentSecret) {
super(context, databaseHelper); super(context, databaseHelper);
this.attachmentSecret = attachmentSecret;
} }
public @NonNull InputStream getAttachmentStream(MasterSecret masterSecret, AttachmentId attachmentId) public @NonNull InputStream getAttachmentStream(AttachmentId attachmentId)
throws IOException throws IOException
{ {
InputStream dataStream = getDataStream(masterSecret, attachmentId, DATA); InputStream dataStream = getDataStream(attachmentId, DATA);
if (dataStream == null) throw new IOException("No stream for: " + attachmentId); if (dataStream == null) throw new IOException("No stream for: " + attachmentId);
else return dataStream; else return dataStream;
} }
public @NonNull InputStream getThumbnailStream(@NonNull MasterSecret masterSecret, @NonNull AttachmentId attachmentId) public @NonNull InputStream getThumbnailStream(@NonNull AttachmentId attachmentId)
throws IOException throws IOException
{ {
Log.w(TAG, "getThumbnailStream(" + attachmentId + ")"); Log.w(TAG, "getThumbnailStream(" + attachmentId + ")");
InputStream dataStream = getDataStream(masterSecret, attachmentId, THUMBNAIL); InputStream dataStream = getDataStream(attachmentId, THUMBNAIL);
if (dataStream != null) { if (dataStream != null) {
return dataStream; return dataStream;
} }
try { try {
InputStream generatedStream = thumbnailExecutor.submit(new ThumbnailFetchCallable(masterSecret, attachmentId)).get(); InputStream generatedStream = thumbnailExecutor.submit(new ThumbnailFetchCallable(attachmentId)).get();
if (generatedStream == null) throw new FileNotFoundException("No thumbnail stream available: " + attachmentId); if (generatedStream == null) throw new FileNotFoundException("No thumbnail stream available: " + attachmentId);
else return generatedStream; else return generatedStream;
@ -162,7 +167,7 @@ public class AttachmentDatabase extends Database {
notifyConversationListeners(DatabaseFactory.getMmsDatabase(context).getThreadIdForMessage(mmsId)); notifyConversationListeners(DatabaseFactory.getMmsDatabase(context).getThreadIdForMessage(mmsId));
} }
public @Nullable DatabaseAttachment getAttachment(@Nullable MasterSecret masterSecret, AttachmentId attachmentId) public @Nullable DatabaseAttachment getAttachment(@NonNull AttachmentId attachmentId)
{ {
SQLiteDatabase database = databaseHelper.getReadableDatabase(); SQLiteDatabase database = databaseHelper.getReadableDatabase();
Cursor cursor = null; Cursor cursor = null;
@ -170,7 +175,7 @@ public class AttachmentDatabase extends Database {
try { try {
cursor = database.query(TABLE_NAME, PROJECTION, PART_ID_WHERE, attachmentId.toStrings(), null, null, null); cursor = database.query(TABLE_NAME, PROJECTION, PART_ID_WHERE, attachmentId.toStrings(), null, null, null);
if (cursor != null && cursor.moveToFirst()) return getAttachment(masterSecret, cursor); if (cursor != null && cursor.moveToFirst()) return getAttachment(cursor);
else return null; else return null;
} finally { } finally {
@ -179,7 +184,7 @@ public class AttachmentDatabase extends Database {
} }
} }
public @NonNull List<DatabaseAttachment> getAttachmentsForMessage(@Nullable MasterSecret masterSecret, long mmsId) { public @NonNull List<DatabaseAttachment> getAttachmentsForMessage(long mmsId) {
SQLiteDatabase database = databaseHelper.getReadableDatabase(); SQLiteDatabase database = databaseHelper.getReadableDatabase();
List<DatabaseAttachment> results = new LinkedList<>(); List<DatabaseAttachment> results = new LinkedList<>();
Cursor cursor = null; Cursor cursor = null;
@ -189,7 +194,7 @@ public class AttachmentDatabase extends Database {
null, null, null); null, null, null);
while (cursor != null && cursor.moveToNext()) { while (cursor != null && cursor.moveToNext()) {
results.add(getAttachment(masterSecret, cursor)); results.add(getAttachment(cursor));
} }
return results; return results;
@ -199,7 +204,7 @@ public class AttachmentDatabase extends Database {
} }
} }
public @NonNull List<DatabaseAttachment> getPendingAttachments(@NonNull MasterSecret masterSecret) { public @NonNull List<DatabaseAttachment> getPendingAttachments() {
final SQLiteDatabase database = databaseHelper.getReadableDatabase(); final SQLiteDatabase database = databaseHelper.getReadableDatabase();
final List<DatabaseAttachment> attachments = new LinkedList<>(); final List<DatabaseAttachment> attachments = new LinkedList<>();
@ -207,7 +212,7 @@ public class AttachmentDatabase extends Database {
try { try {
cursor = database.query(TABLE_NAME, PROJECTION, TRANSFER_STATE + " = ?", new String[] {String.valueOf(TRANSFER_PROGRESS_STARTED)}, null, null, null); cursor = database.query(TABLE_NAME, PROJECTION, TRANSFER_STATE + " = ?", new String[] {String.valueOf(TRANSFER_PROGRESS_STARTED)}, null, null, null);
while (cursor != null && cursor.moveToNext()) { while (cursor != null && cursor.moveToNext()) {
attachments.add(getAttachment(masterSecret, cursor)); attachments.add(getAttachment(cursor));
} }
} finally { } finally {
if (cursor != null) cursor.close(); if (cursor != null) cursor.close();
@ -217,7 +222,7 @@ public class AttachmentDatabase extends Database {
} }
@SuppressWarnings("ResultOfMethodCallIgnored") @SuppressWarnings("ResultOfMethodCallIgnored")
public void deleteAttachmentsForMessage(long mmsId) { void deleteAttachmentsForMessage(long mmsId) {
SQLiteDatabase database = databaseHelper.getWritableDatabase(); SQLiteDatabase database = databaseHelper.getWritableDatabase();
Cursor cursor = null; Cursor cursor = null;
@ -246,7 +251,7 @@ public class AttachmentDatabase extends Database {
} }
@SuppressWarnings("ResultOfMethodCallIgnored") @SuppressWarnings("ResultOfMethodCallIgnored")
public void deleteAllAttachments() { void deleteAllAttachments() {
SQLiteDatabase database = databaseHelper.getWritableDatabase(); SQLiteDatabase database = databaseHelper.getWritableDatabase();
database.delete(TABLE_NAME, null, null); database.delete(TABLE_NAME, null, null);
@ -258,17 +263,16 @@ public class AttachmentDatabase extends Database {
} }
} }
public long insertAttachmentsForPlaceholder(@NonNull MasterSecret masterSecret, long mmsId, public void insertAttachmentsForPlaceholder(long mmsId, @NonNull AttachmentId attachmentId, @NonNull InputStream inputStream)
@NonNull AttachmentId attachmentId,
@NonNull InputStream inputStream)
throws MmsException throws MmsException
{ {
SQLiteDatabase database = databaseHelper.getWritableDatabase(); SQLiteDatabase database = databaseHelper.getWritableDatabase();
Pair<File, Long> partData = setAttachmentData(masterSecret, inputStream); DataInfo dataInfo = setAttachmentData(inputStream);
ContentValues values = new ContentValues(); ContentValues values = new ContentValues();
values.put(DATA, partData.first.getAbsolutePath()); values.put(DATA, dataInfo.file.getAbsolutePath());
values.put(SIZE, partData.second); values.put(SIZE, dataInfo.length);
values.put(DATA_RANDOM, dataInfo.random);
values.put(TRANSFER_STATE, TRANSFER_PROGRESS_DONE); values.put(TRANSFER_STATE, TRANSFER_PROGRESS_DONE);
values.put(CONTENT_LOCATION, (String)null); values.put(CONTENT_LOCATION, (String)null);
values.put(CONTENT_DISPOSITION, (String)null); values.put(CONTENT_DISPOSITION, (String)null);
@ -278,47 +282,44 @@ public class AttachmentDatabase extends Database {
if (database.update(TABLE_NAME, values, PART_ID_WHERE, attachmentId.toStrings()) == 0) { if (database.update(TABLE_NAME, values, PART_ID_WHERE, attachmentId.toStrings()) == 0) {
//noinspection ResultOfMethodCallIgnored //noinspection ResultOfMethodCallIgnored
partData.first.delete(); dataInfo.file.delete();
} else { } else {
notifyConversationListeners(DatabaseFactory.getMmsDatabase(context).getThreadIdForMessage(mmsId)); notifyConversationListeners(DatabaseFactory.getMmsDatabase(context).getThreadIdForMessage(mmsId));
notifyConversationListListeners(); notifyConversationListListeners();
} }
thumbnailExecutor.submit(new ThumbnailFetchCallable(masterSecret, attachmentId)); thumbnailExecutor.submit(new ThumbnailFetchCallable(attachmentId));
return partData.second;
} }
void insertAttachmentsForMessage(@NonNull MasterSecretUnion masterSecret, void insertAttachmentsForMessage(long mmsId, @NonNull List<Attachment> attachments)
long mmsId,
@NonNull List<Attachment> attachments)
throws MmsException throws MmsException
{ {
Log.w(TAG, "insertParts(" + attachments.size() + ")"); Log.w(TAG, "insertParts(" + attachments.size() + ")");
for (Attachment attachment : attachments) { for (Attachment attachment : attachments) {
AttachmentId attachmentId = insertAttachment(masterSecret, mmsId, attachment); AttachmentId attachmentId = insertAttachment(mmsId, attachment);
Log.w(TAG, "Inserted attachment at ID: " + attachmentId); Log.w(TAG, "Inserted attachment at ID: " + attachmentId);
} }
} }
public @NonNull Attachment updateAttachmentData(@NonNull MasterSecret masterSecret, public @NonNull Attachment updateAttachmentData(@NonNull Attachment attachment,
@NonNull Attachment attachment,
@NonNull MediaStream mediaStream) @NonNull MediaStream mediaStream)
throws MmsException throws MmsException
{ {
SQLiteDatabase database = databaseHelper.getWritableDatabase(); SQLiteDatabase database = databaseHelper.getWritableDatabase();
DatabaseAttachment databaseAttachment = (DatabaseAttachment) attachment; DatabaseAttachment databaseAttachment = (DatabaseAttachment) attachment;
File dataFile = getAttachmentDataFile(databaseAttachment.getAttachmentId(), DATA); DataInfo dataInfo = getAttachmentDataFileInfo(databaseAttachment.getAttachmentId(), DATA);
if (dataFile == null) { if (dataInfo == null) {
throw new MmsException("No attachment data found!"); throw new MmsException("No attachment data found!");
} }
long dataSize = setAttachmentData(masterSecret, dataFile, mediaStream.getStream()); dataInfo = setAttachmentData(dataInfo.file, mediaStream.getStream());
ContentValues contentValues = new ContentValues(); ContentValues contentValues = new ContentValues();
contentValues.put(SIZE, dataSize); contentValues.put(SIZE, dataInfo.length);
contentValues.put(CONTENT_TYPE, mediaStream.getMimeType()); contentValues.put(CONTENT_TYPE, mediaStream.getMimeType());
contentValues.put(DATA_RANDOM, dataInfo.random);
database.update(TABLE_NAME, contentValues, PART_ID_WHERE, databaseAttachment.getAttachmentId().toStrings()); database.update(TABLE_NAME, contentValues, PART_ID_WHERE, databaseAttachment.getAttachmentId().toStrings());
@ -328,7 +329,7 @@ public class AttachmentDatabase extends Database {
databaseAttachment.hasThumbnail(), databaseAttachment.hasThumbnail(),
mediaStream.getMimeType(), mediaStream.getMimeType(),
databaseAttachment.getTransferState(), databaseAttachment.getTransferState(),
dataSize, dataInfo.length,
databaseAttachment.getFileName(), databaseAttachment.getFileName(),
databaseAttachment.getLocation(), databaseAttachment.getLocation(),
databaseAttachment.getKey(), databaseAttachment.getKey(),
@ -339,16 +340,11 @@ public class AttachmentDatabase extends Database {
} }
public void updateAttachmentFileName(@NonNull MasterSecret masterSecret, public void updateAttachmentFileName(@NonNull AttachmentId attachmentId,
@NonNull AttachmentId attachmentId,
@Nullable String fileName) @Nullable String fileName)
{ {
SQLiteDatabase database = databaseHelper.getWritableDatabase(); SQLiteDatabase database = databaseHelper.getWritableDatabase();
if (fileName != null) {
fileName = new MasterCipher(masterSecret).encryptBody(fileName);
}
ContentValues contentValues = new ContentValues(1); ContentValues contentValues = new ContentValues(1);
contentValues.put(FILE_NAME, fileName); contentValues.put(FILE_NAME, fileName);
@ -382,28 +378,43 @@ public class AttachmentDatabase extends Database {
notifyConversationListeners(DatabaseFactory.getMmsDatabase(context).getThreadIdForMessage(messageId)); notifyConversationListeners(DatabaseFactory.getMmsDatabase(context).getThreadIdForMessage(messageId));
} }
@SuppressWarnings("WeakerAccess")
@VisibleForTesting @VisibleForTesting
protected @Nullable InputStream getDataStream(MasterSecret masterSecret, AttachmentId attachmentId, String dataType) protected @Nullable InputStream getDataStream(AttachmentId attachmentId, String dataType)
{ {
File dataFile = getAttachmentDataFile(attachmentId, dataType); DataInfo dataInfo = getAttachmentDataFileInfo(attachmentId, dataType);
if (dataInfo == null) {
return null;
}
try { try {
if (dataFile != null) return DecryptingPartInputStream.createFor(masterSecret, dataFile); if (dataInfo.random != null && dataInfo.random.length == 32) {
else return null; return ModernDecryptingPartInputStream.createFor(attachmentSecret, dataInfo.random, dataInfo.file);
} else {
return ClassicDecryptingPartInputStream.createFor(attachmentSecret, dataInfo.file);
}
} catch (IOException e) { } catch (IOException e) {
Log.w(TAG, e); Log.w(TAG, e);
return null; return null;
} }
} }
private @Nullable File getAttachmentDataFile(@NonNull AttachmentId attachmentId, private @Nullable DataInfo getAttachmentDataFileInfo(@NonNull AttachmentId attachmentId, @NonNull String dataType)
@NonNull String dataType)
{ {
SQLiteDatabase database = databaseHelper.getReadableDatabase(); SQLiteDatabase database = databaseHelper.getReadableDatabase();
Cursor cursor = null; Cursor cursor = null;
String randomColumn;
switch (dataType) {
case DATA: randomColumn = DATA_RANDOM; break;
case THUMBNAIL: randomColumn = THUMBNAIL_RANDOM; break;
default:throw new AssertionError("Unknown data type: " + dataType);
}
try { try {
cursor = database.query(TABLE_NAME, new String[]{dataType}, PART_ID_WHERE, attachmentId.toStrings(), cursor = database.query(TABLE_NAME, new String[]{dataType, SIZE, randomColumn}, PART_ID_WHERE, attachmentId.toStrings(),
null, null, null); null, null, null);
if (cursor != null && cursor.moveToFirst()) { if (cursor != null && cursor.moveToFirst()) {
@ -411,7 +422,9 @@ public class AttachmentDatabase extends Database {
return null; return null;
} }
return new File(cursor.getString(0)); return new DataInfo(new File(cursor.getString(0)),
cursor.getLong(1),
cursor.getBlob(2));
} else { } else {
return null; return null;
} }
@ -422,57 +435,46 @@ public class AttachmentDatabase extends Database {
} }
private @NonNull Pair<File, Long> setAttachmentData(@NonNull MasterSecret masterSecret, private @NonNull DataInfo setAttachmentData(@NonNull Uri uri)
@NonNull Uri uri)
throws MmsException throws MmsException
{ {
try { try {
InputStream inputStream = PartAuthority.getAttachmentStream(context, masterSecret, uri); InputStream inputStream = PartAuthority.getAttachmentStream(context, uri);
return setAttachmentData(masterSecret, inputStream); return setAttachmentData(inputStream);
} catch (IOException e) { } catch (IOException e) {
throw new MmsException(e); throw new MmsException(e);
} }
} }
private @NonNull Pair<File, Long> setAttachmentData(@NonNull MasterSecret masterSecret, private @NonNull DataInfo setAttachmentData(@NonNull InputStream in)
@NonNull InputStream in)
throws MmsException throws MmsException
{ {
try { try {
File partsDirectory = context.getDir("parts", Context.MODE_PRIVATE); File partsDirectory = context.getDir("parts", Context.MODE_PRIVATE);
File dataFile = File.createTempFile("part", ".mms", partsDirectory); File dataFile = File.createTempFile("part", ".mms", partsDirectory);
return setAttachmentData(dataFile, in);
return new Pair<>(dataFile, setAttachmentData(masterSecret, dataFile, in));
} catch (IOException e) { } catch (IOException e) {
throw new MmsException(e); throw new MmsException(e);
} }
} }
private long setAttachmentData(@NonNull MasterSecret masterSecret, private @NonNull DataInfo setAttachmentData(@NonNull File destination, @NonNull InputStream in)
@NonNull File destination,
@NonNull InputStream in)
throws MmsException throws MmsException
{ {
try { try {
OutputStream out = new EncryptingPartOutputStream(destination, masterSecret); byte[] random = new byte[32];
return Util.copy(in, out); new SecureRandom().nextBytes(random);
OutputStream out = ModernEncryptingPartOutputStream.createFor(attachmentSecret, random, destination);
long length = Util.copy(in, out);
return new DataInfo(destination, length, random);
} catch (IOException e) { } catch (IOException e) {
throw new MmsException(e); throw new MmsException(e);
} }
} }
DatabaseAttachment getAttachment(@Nullable MasterSecret masterSecret, Cursor cursor) { DatabaseAttachment getAttachment(@NonNull Cursor cursor) {
String encryptedFileName = cursor.getString(cursor.getColumnIndexOrThrow(FILE_NAME));
String fileName = null;
if (masterSecret != null && !TextUtils.isEmpty(encryptedFileName)) {
try {
fileName = new MasterCipher(masterSecret).decryptBody(encryptedFileName);
} catch (InvalidMessageException e) {
Log.w(TAG, e);
}
}
return new DatabaseAttachment(new AttachmentId(cursor.getLong(cursor.getColumnIndexOrThrow(ATTACHMENT_ID_ALIAS)), return new DatabaseAttachment(new AttachmentId(cursor.getLong(cursor.getColumnIndexOrThrow(ATTACHMENT_ID_ALIAS)),
cursor.getLong(cursor.getColumnIndexOrThrow(UNIQUE_ID))), cursor.getLong(cursor.getColumnIndexOrThrow(UNIQUE_ID))),
cursor.getLong(cursor.getColumnIndexOrThrow(MMS_ID)), cursor.getLong(cursor.getColumnIndexOrThrow(MMS_ID)),
@ -481,7 +483,7 @@ public class AttachmentDatabase extends Database {
cursor.getString(cursor.getColumnIndexOrThrow(CONTENT_TYPE)), cursor.getString(cursor.getColumnIndexOrThrow(CONTENT_TYPE)),
cursor.getInt(cursor.getColumnIndexOrThrow(TRANSFER_STATE)), cursor.getInt(cursor.getColumnIndexOrThrow(TRANSFER_STATE)),
cursor.getLong(cursor.getColumnIndexOrThrow(SIZE)), cursor.getLong(cursor.getColumnIndexOrThrow(SIZE)),
fileName, cursor.getString(cursor.getColumnIndexOrThrow(FILE_NAME)),
cursor.getString(cursor.getColumnIndexOrThrow(CONTENT_LOCATION)), cursor.getString(cursor.getColumnIndexOrThrow(CONTENT_LOCATION)),
cursor.getString(cursor.getColumnIndexOrThrow(CONTENT_DISPOSITION)), cursor.getString(cursor.getColumnIndexOrThrow(CONTENT_DISPOSITION)),
cursor.getString(cursor.getColumnIndexOrThrow(NAME)), cursor.getString(cursor.getColumnIndexOrThrow(NAME)),
@ -491,23 +493,18 @@ public class AttachmentDatabase extends Database {
} }
private AttachmentId insertAttachment(MasterSecretUnion masterSecret, long mmsId, Attachment attachment) private AttachmentId insertAttachment(long mmsId, Attachment attachment)
throws MmsException throws MmsException
{ {
Log.w(TAG, "Inserting attachment for mms id: " + mmsId); Log.w(TAG, "Inserting attachment for mms id: " + mmsId);
SQLiteDatabase database = databaseHelper.getWritableDatabase(); SQLiteDatabase database = databaseHelper.getWritableDatabase();
Pair<File, Long> partData = null; DataInfo dataInfo = null;
long uniqueId = System.currentTimeMillis(); long uniqueId = System.currentTimeMillis();
String fileName = null;
if (masterSecret.getMasterSecret().isPresent() && attachment.getDataUri() != null) { if (attachment.getDataUri() != null) {
partData = setAttachmentData(masterSecret.getMasterSecret().get(), attachment.getDataUri()); dataInfo = setAttachmentData(attachment.getDataUri());
Log.w(TAG, "Wrote part to file: " + partData.first.getAbsolutePath()); Log.w(TAG, "Wrote part to file: " + dataInfo.file.getAbsolutePath());
}
if (masterSecret.getMasterSecret().isPresent() && !TextUtils.isEmpty(attachment.getFileName())) {
fileName = new MasterCipher(masterSecret.getMasterSecret().get()).encryptBody(attachment.getFileName());
} }
ContentValues contentValues = new ContentValues(); ContentValues contentValues = new ContentValues();
@ -519,33 +516,34 @@ public class AttachmentDatabase extends Database {
contentValues.put(DIGEST, attachment.getDigest()); contentValues.put(DIGEST, attachment.getDigest());
contentValues.put(CONTENT_DISPOSITION, attachment.getKey()); contentValues.put(CONTENT_DISPOSITION, attachment.getKey());
contentValues.put(NAME, attachment.getRelay()); contentValues.put(NAME, attachment.getRelay());
contentValues.put(FILE_NAME, fileName); contentValues.put(FILE_NAME, attachment.getFileName());
contentValues.put(SIZE, attachment.getSize()); contentValues.put(SIZE, attachment.getSize());
contentValues.put(FAST_PREFLIGHT_ID, attachment.getFastPreflightId()); contentValues.put(FAST_PREFLIGHT_ID, attachment.getFastPreflightId());
contentValues.put(VOICE_NOTE, attachment.isVoiceNote() ? 1 : 0); contentValues.put(VOICE_NOTE, attachment.isVoiceNote() ? 1 : 0);
if (partData != null) { if (dataInfo != null) {
contentValues.put(DATA, partData.first.getAbsolutePath()); contentValues.put(DATA, dataInfo.file.getAbsolutePath());
contentValues.put(SIZE, partData.second); contentValues.put(SIZE, dataInfo.length);
contentValues.put(DATA_RANDOM, dataInfo.random);
} }
long rowId = database.insert(TABLE_NAME, null, contentValues); long rowId = database.insert(TABLE_NAME, null, contentValues);
AttachmentId attachmentId = new AttachmentId(rowId, uniqueId); AttachmentId attachmentId = new AttachmentId(rowId, uniqueId);
if (partData != null) { if (dataInfo != null) {
if (MediaUtil.hasVideoThumbnail(attachment.getDataUri())) { if (MediaUtil.hasVideoThumbnail(attachment.getDataUri())) {
Bitmap bitmap = MediaUtil.getVideoThumbnail(context, attachment.getDataUri()); Bitmap bitmap = MediaUtil.getVideoThumbnail(context, attachment.getDataUri());
if (bitmap != null) { if (bitmap != null) {
ThumbnailData thumbnailData = new ThumbnailData(bitmap); ThumbnailData thumbnailData = new ThumbnailData(bitmap);
updateAttachmentThumbnail(masterSecret.getMasterSecret().get(), attachmentId, thumbnailData.toDataStream(), thumbnailData.getAspectRatio()); updateAttachmentThumbnail(attachmentId, thumbnailData.toDataStream(), thumbnailData.getAspectRatio());
} else { } else {
Log.w(TAG, "Retrieving video thumbnail failed, submitting thumbnail generation job..."); Log.w(TAG, "Retrieving video thumbnail failed, submitting thumbnail generation job...");
thumbnailExecutor.submit(new ThumbnailFetchCallable(masterSecret.getMasterSecret().get(), attachmentId)); thumbnailExecutor.submit(new ThumbnailFetchCallable(attachmentId));
} }
} else { } else {
Log.w(TAG, "Submitting thumbnail generation job..."); Log.w(TAG, "Submitting thumbnail generation job...");
thumbnailExecutor.submit(new ThumbnailFetchCallable(masterSecret.getMasterSecret().get(), attachmentId)); thumbnailExecutor.submit(new ThumbnailFetchCallable(attachmentId));
} }
} }
@ -553,19 +551,21 @@ public class AttachmentDatabase extends Database {
} }
@SuppressWarnings("WeakerAccess")
@VisibleForTesting @VisibleForTesting
protected void updateAttachmentThumbnail(MasterSecret masterSecret, AttachmentId attachmentId, InputStream in, float aspectRatio) protected void updateAttachmentThumbnail(AttachmentId attachmentId, InputStream in, float aspectRatio)
throws MmsException throws MmsException
{ {
Log.w(TAG, "updating part thumbnail for #" + attachmentId); Log.w(TAG, "updating part thumbnail for #" + attachmentId);
Pair<File, Long> thumbnailFile = setAttachmentData(masterSecret, in); DataInfo thumbnailFile = setAttachmentData(in);
SQLiteDatabase database = databaseHelper.getWritableDatabase(); SQLiteDatabase database = databaseHelper.getWritableDatabase();
ContentValues values = new ContentValues(2); ContentValues values = new ContentValues(2);
values.put(THUMBNAIL, thumbnailFile.first.getAbsolutePath()); values.put(THUMBNAIL, thumbnailFile.file.getAbsolutePath());
values.put(THUMBNAIL_ASPECT_RATIO, aspectRatio); values.put(THUMBNAIL_ASPECT_RATIO, aspectRatio);
values.put(THUMBNAIL_RANDOM, thumbnailFile.random);
database.update(TABLE_NAME, values, PART_ID_WHERE, attachmentId.toStrings()); database.update(TABLE_NAME, values, PART_ID_WHERE, attachmentId.toStrings());
@ -584,24 +584,22 @@ public class AttachmentDatabase extends Database {
@VisibleForTesting @VisibleForTesting
class ThumbnailFetchCallable implements Callable<InputStream> { class ThumbnailFetchCallable implements Callable<InputStream> {
private final MasterSecret masterSecret;
private final AttachmentId attachmentId; private final AttachmentId attachmentId;
ThumbnailFetchCallable(MasterSecret masterSecret, AttachmentId attachmentId) { ThumbnailFetchCallable(AttachmentId attachmentId) {
this.masterSecret = masterSecret;
this.attachmentId = attachmentId; this.attachmentId = attachmentId;
} }
@Override @Override
public @Nullable InputStream call() throws Exception { public @Nullable InputStream call() throws Exception {
Log.w(TAG, "Executing thumbnail job..."); Log.w(TAG, "Executing thumbnail job...");
final InputStream stream = getDataStream(masterSecret, attachmentId, THUMBNAIL); final InputStream stream = getDataStream(attachmentId, THUMBNAIL);
if (stream != null) { if (stream != null) {
return stream; return stream;
} }
DatabaseAttachment attachment = getAttachment(masterSecret, attachmentId); DatabaseAttachment attachment = getAttachment(attachmentId);
if (attachment == null || !attachment.hasData()) { if (attachment == null || !attachment.hasData()) {
return null; return null;
@ -610,34 +608,35 @@ public class AttachmentDatabase extends Database {
ThumbnailData data; ThumbnailData data;
if (MediaUtil.isVideoType(attachment.getContentType())) { if (MediaUtil.isVideoType(attachment.getContentType())) {
data = generateVideoThumbnail(masterSecret, attachmentId); data = generateVideoThumbnail(attachmentId);
} else{ } else{
data = MediaUtil.generateThumbnail(context, masterSecret, attachment.getContentType(), attachment.getDataUri()); data = MediaUtil.generateThumbnail(context, attachment.getContentType(), attachment.getDataUri());
} }
if (data == null) { if (data == null) {
return null; return null;
} }
updateAttachmentThumbnail(masterSecret, attachmentId, data.toDataStream(), data.getAspectRatio()); updateAttachmentThumbnail(attachmentId, data.toDataStream(), data.getAspectRatio());
return getDataStream(masterSecret, attachmentId, THUMBNAIL); return getDataStream(attachmentId, THUMBNAIL);
} }
private ThumbnailData generateVideoThumbnail(MasterSecret masterSecret, AttachmentId attachmentId) { @SuppressLint("NewApi")
private ThumbnailData generateVideoThumbnail(AttachmentId attachmentId) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
Log.w(TAG, "Video thumbnails not supported..."); Log.w(TAG, "Video thumbnails not supported...");
return null; return null;
} }
File mediaFile = getAttachmentDataFile(attachmentId, DATA); DataInfo dataInfo = getAttachmentDataFileInfo(attachmentId, DATA);
if (mediaFile == null) { if (dataInfo == null) {
Log.w(TAG, "No data file found for video thumbnail..."); Log.w(TAG, "No data file found for video thumbnail...");
return null; return null;
} }
EncryptedMediaDataSource dataSource = new EncryptedMediaDataSource(masterSecret, mediaFile); EncryptedMediaDataSource dataSource = new EncryptedMediaDataSource(attachmentSecret, dataInfo.file, dataInfo.random, dataInfo.length);
MediaMetadataRetriever retriever = new MediaMetadataRetriever(); MediaMetadataRetriever retriever = new MediaMetadataRetriever();
retriever.setDataSource(dataSource); retriever.setDataSource(dataSource);
@ -647,4 +646,16 @@ public class AttachmentDatabase extends Database {
return new ThumbnailData(bitmap); return new ThumbnailData(bitmap);
} }
} }
private static class DataInfo {
private final File file;
private final long length;
private final byte[] random;
private DataInfo(File file, long length, byte[] random) {
this.file = file;
this.length = length;
this.random = random;
}
}
} }

View File

@ -18,9 +18,10 @@ package org.thoughtcrime.securesms.database;
import android.content.Context; import android.content.Context;
import android.database.Cursor; import android.database.Cursor;
import android.database.sqlite.SQLiteOpenHelper;
import android.net.Uri; import android.net.Uri;
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper;
import java.util.Set; import java.util.Set;
public abstract class Database { public abstract class Database {
@ -29,10 +30,10 @@ public abstract class Database {
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 SQLiteOpenHelper databaseHelper; protected SQLCipherOpenHelper databaseHelper;
protected final Context context; protected final Context context;
public Database(Context context, SQLiteOpenHelper databaseHelper) { public Database(Context context, SQLCipherOpenHelper databaseHelper) {
this.context = context; this.context = context;
this.databaseHelper = databaseHelper; this.databaseHelper = databaseHelper;
} }
@ -58,7 +59,7 @@ public abstract class Database {
cursor.setNotificationUri(context.getContentResolver(), Uri.parse(CONVERSATION_LIST_URI)); cursor.setNotificationUri(context.getContentResolver(), Uri.parse(CONVERSATION_LIST_URI));
} }
public void reset(SQLiteOpenHelper databaseHelper) { public void reset(SQLCipherOpenHelper databaseHelper) {
this.databaseHelper = databaseHelper; this.databaseHelper = databaseHelper;
} }

File diff suppressed because it is too large Load Diff

View File

@ -3,15 +3,13 @@ package org.thoughtcrime.securesms.database;
import android.content.ContentValues; import android.content.ContentValues;
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.SQLiteOpenHelper;
import android.net.Uri; import android.net.Uri;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import android.util.Log;
import net.sqlcipher.database.SQLiteDatabase;
import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.R;
import org.whispersystems.libsignal.InvalidMessageException; import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper;
import org.thoughtcrime.securesms.crypto.MasterCipher;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
@ -32,18 +30,18 @@ public class DraftDatabase extends Database {
"CREATE INDEX IF NOT EXISTS draft_thread_index ON " + TABLE_NAME + " (" + THREAD_ID + ");", "CREATE INDEX IF NOT EXISTS draft_thread_index ON " + TABLE_NAME + " (" + THREAD_ID + ");",
}; };
public DraftDatabase(Context context, SQLiteOpenHelper databaseHelper) { public DraftDatabase(Context context, SQLCipherOpenHelper databaseHelper) {
super(context, databaseHelper); super(context, databaseHelper);
} }
public void insertDrafts(MasterCipher masterCipher, long threadId, List<Draft> drafts) { public void insertDrafts(long threadId, List<Draft> drafts) {
SQLiteDatabase db = databaseHelper.getWritableDatabase(); SQLiteDatabase db = databaseHelper.getWritableDatabase();
for (Draft draft : drafts) { for (Draft draft : drafts) {
ContentValues values = new ContentValues(3); ContentValues values = new ContentValues(3);
values.put(THREAD_ID, threadId); values.put(THREAD_ID, threadId);
values.put(DRAFT_TYPE, masterCipher.encryptBody(draft.getType())); values.put(DRAFT_TYPE, draft.getType());
values.put(DRAFT_VALUE, masterCipher.encryptBody(draft.getValue())); values.put(DRAFT_VALUE, draft.getValue());
db.insert(TABLE_NAME, null, values); db.insert(TABLE_NAME, null, values);
} }
@ -54,7 +52,7 @@ public class DraftDatabase extends Database {
db.delete(TABLE_NAME, THREAD_ID + " = ?", new String[] {threadId+""}); db.delete(TABLE_NAME, THREAD_ID + " = ?", new String[] {threadId+""});
} }
public void clearDrafts(Set<Long> threadIds) { void clearDrafts(Set<Long> threadIds) {
SQLiteDatabase db = databaseHelper.getWritableDatabase(); SQLiteDatabase db = databaseHelper.getWritableDatabase();
StringBuilder where = new StringBuilder(); StringBuilder where = new StringBuilder();
List<String> arguments = new LinkedList<>(); List<String> arguments = new LinkedList<>();
@ -70,29 +68,24 @@ public class DraftDatabase extends Database {
db.delete(TABLE_NAME, where.toString().substring(4), arguments.toArray(new String[0])); db.delete(TABLE_NAME, where.toString().substring(4), arguments.toArray(new String[0]));
} }
public void clearAllDrafts() { void clearAllDrafts() {
SQLiteDatabase db = databaseHelper.getWritableDatabase(); SQLiteDatabase db = databaseHelper.getWritableDatabase();
db.delete(TABLE_NAME, null, null); db.delete(TABLE_NAME, null, null);
} }
public List<Draft> getDrafts(MasterCipher masterCipher, long threadId) { public List<Draft> getDrafts(long threadId) {
SQLiteDatabase db = databaseHelper.getReadableDatabase(); SQLiteDatabase db = databaseHelper.getReadableDatabase();
List<Draft> results = new LinkedList<Draft>(); List<Draft> results = new LinkedList<>();
Cursor cursor = null; Cursor cursor = null;
try { try {
cursor = db.query(TABLE_NAME, null, THREAD_ID + " = ?", new String[] {threadId+""}, null, null, null); cursor = db.query(TABLE_NAME, null, THREAD_ID + " = ?", new String[] {threadId+""}, null, null, null);
while (cursor != null && cursor.moveToNext()) { while (cursor != null && cursor.moveToNext()) {
try { String type = cursor.getString(cursor.getColumnIndexOrThrow(DRAFT_TYPE));
String encryptedType = cursor.getString(cursor.getColumnIndexOrThrow(DRAFT_TYPE)); String value = cursor.getString(cursor.getColumnIndexOrThrow(DRAFT_VALUE));
String encryptedValue = cursor.getString(cursor.getColumnIndexOrThrow(DRAFT_VALUE));
results.add(new Draft(masterCipher.decryptBody(encryptedType), results.add(new Draft(type, value));
masterCipher.decryptBody(encryptedValue)));
} catch (InvalidMessageException ime) {
Log.w("DraftDatabase", ime);
}
} }
return results; return results;
@ -125,7 +118,7 @@ public class DraftDatabase extends Database {
return value; return value;
} }
public String getSnippet(Context context) { String getSnippet(Context context) {
switch (type) { switch (type) {
case TEXT: return value; case TEXT: return value;
case IMAGE: return context.getString(R.string.DraftDatabase_Draft_image_snippet); case IMAGE: return context.getString(R.string.DraftDatabase_Draft_image_snippet);
@ -158,7 +151,7 @@ public class DraftDatabase extends Database {
} }
} }
public @Nullable Uri getUriSnippet(Context context) { public @Nullable Uri getUriSnippet() {
Draft imageDraft = getDraftOfType(Draft.IMAGE); Draft imageDraft = getDraftOfType(Draft.IMAGE);
if (imageDraft != null && imageDraft.getValue() != null) { if (imageDraft != null && imageDraft.getValue() != null) {

View File

@ -1,233 +0,0 @@
/**
* Copyright (C) 2011 Whisper Systems
*
* 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
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.thoughtcrime.securesms.database;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteOpenHelper;
import android.support.annotation.NonNull;
import android.util.Log;
import android.util.Pair;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.crypto.AsymmetricMasterCipher;
import org.thoughtcrime.securesms.crypto.AsymmetricMasterSecret;
import org.thoughtcrime.securesms.crypto.MasterCipher;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.crypto.MasterSecretUnion;
import org.thoughtcrime.securesms.database.model.DisplayRecord;
import org.thoughtcrime.securesms.database.model.SmsMessageRecord;
import org.thoughtcrime.securesms.sms.IncomingTextMessage;
import org.thoughtcrime.securesms.sms.OutgoingTextMessage;
import org.thoughtcrime.securesms.util.LRUCache;
import org.whispersystems.libsignal.InvalidMessageException;
import org.whispersystems.libsignal.util.guava.Optional;
import java.lang.ref.SoftReference;
import java.util.Collections;
import java.util.Map;
public class EncryptingSmsDatabase extends SmsDatabase {
private final PlaintextCache plaintextCache = new PlaintextCache();
public EncryptingSmsDatabase(Context context, SQLiteOpenHelper databaseHelper) {
super(context, databaseHelper);
}
private String getAsymmetricEncryptedBody(AsymmetricMasterSecret masterSecret, String body) {
AsymmetricMasterCipher bodyCipher = new AsymmetricMasterCipher(masterSecret);
return bodyCipher.encryptBody(body);
}
private String getEncryptedBody(MasterSecret masterSecret, String body) {
MasterCipher bodyCipher = new MasterCipher(masterSecret);
String ciphertext = bodyCipher.encryptBody(body);
plaintextCache.put(ciphertext, body);
return ciphertext;
}
public long insertMessageOutbox(MasterSecretUnion masterSecret, long threadId,
OutgoingTextMessage message, boolean forceSms,
long timestamp, InsertListener insertListener)
{
long type = Types.BASE_SENDING_TYPE;
if (masterSecret.getMasterSecret().isPresent()) {
message = message.withBody(getEncryptedBody(masterSecret.getMasterSecret().get(), message.getMessageBody()));
type |= Types.ENCRYPTION_SYMMETRIC_BIT;
} else {
message = message.withBody(getAsymmetricEncryptedBody(masterSecret.getAsymmetricMasterSecret().get(), message.getMessageBody()));
type |= Types.ENCRYPTION_ASYMMETRIC_BIT;
}
return insertMessageOutbox(threadId, message, type, forceSms, timestamp, insertListener);
}
public Optional<InsertResult> insertMessageInbox(@NonNull MasterSecretUnion masterSecret,
@NonNull IncomingTextMessage message)
{
if (masterSecret.getMasterSecret().isPresent()) {
return insertMessageInbox(masterSecret.getMasterSecret().get(), message);
} else {
return insertMessageInbox(masterSecret.getAsymmetricMasterSecret().get(), message);
}
}
private Optional<InsertResult> insertMessageInbox(@NonNull MasterSecret masterSecret,
@NonNull IncomingTextMessage message)
{
long type = Types.BASE_INBOX_TYPE | Types.ENCRYPTION_SYMMETRIC_BIT;
message = message.withMessageBody(getEncryptedBody(masterSecret, message.getMessageBody()));
return insertMessageInbox(message, type);
}
private Optional<InsertResult> insertMessageInbox(@NonNull AsymmetricMasterSecret masterSecret,
@NonNull IncomingTextMessage message)
{
long type = Types.BASE_INBOX_TYPE | Types.ENCRYPTION_ASYMMETRIC_BIT;
message = message.withMessageBody(getAsymmetricEncryptedBody(masterSecret, message.getMessageBody()));
return insertMessageInbox(message, type);
}
public Pair<Long, Long> updateBundleMessageBody(MasterSecretUnion masterSecret, long messageId, String body) {
long type = Types.BASE_INBOX_TYPE | Types.SECURE_MESSAGE_BIT | Types.PUSH_MESSAGE_BIT;
String encryptedBody;
if (masterSecret.getMasterSecret().isPresent()) {
encryptedBody = getEncryptedBody(masterSecret.getMasterSecret().get(), body);
type |= Types.ENCRYPTION_SYMMETRIC_BIT;
} else {
encryptedBody = getAsymmetricEncryptedBody(masterSecret.getAsymmetricMasterSecret().get(), body);
type |= Types.ENCRYPTION_ASYMMETRIC_BIT;
}
return updateMessageBodyAndType(messageId, encryptedBody, Types.TOTAL_MASK, type);
}
public void updateMessageBody(MasterSecretUnion masterSecret, long messageId, String body) {
long type;
if (masterSecret.getMasterSecret().isPresent()) {
body = getEncryptedBody(masterSecret.getMasterSecret().get(), body);
type = Types.ENCRYPTION_SYMMETRIC_BIT;
} else {
body = getAsymmetricEncryptedBody(masterSecret.getAsymmetricMasterSecret().get(), body);
type = Types.ENCRYPTION_ASYMMETRIC_BIT;
}
updateMessageBodyAndType(messageId, body, Types.ENCRYPTION_MASK, type);
}
public Reader getMessages(MasterSecret masterSecret, int skip, int limit) {
Cursor cursor = super.getMessages(skip, limit);
return new DecryptingReader(masterSecret, cursor);
}
public Reader getOutgoingMessages(MasterSecret masterSecret) {
Cursor cursor = super.getOutgoingMessages();
return new DecryptingReader(masterSecret, cursor);
}
public SmsMessageRecord getMessage(MasterSecret masterSecret, long messageId) throws NoSuchMessageException {
Cursor cursor = super.getMessage(messageId);
DecryptingReader reader = new DecryptingReader(masterSecret, cursor);
SmsMessageRecord record = reader.getNext();
reader.close();
if (record == null) throw new NoSuchMessageException("No message for ID: " + messageId);
else return record;
}
public Reader getDecryptInProgressMessages(MasterSecret masterSecret) {
Cursor cursor = super.getDecryptInProgressMessages();
return new DecryptingReader(masterSecret, cursor);
}
public Reader readerFor(MasterSecret masterSecret, Cursor cursor) {
return new DecryptingReader(masterSecret, cursor);
}
public class DecryptingReader extends SmsDatabase.Reader {
private final MasterCipher masterCipher;
public DecryptingReader(MasterSecret masterSecret, Cursor cursor) {
super(cursor);
this.masterCipher = new MasterCipher(masterSecret);
}
@Override
protected DisplayRecord.Body getBody(Cursor cursor) {
long type = cursor.getLong(cursor.getColumnIndexOrThrow(SmsDatabase.TYPE));
String ciphertext = cursor.getString(cursor.getColumnIndexOrThrow(SmsDatabase.BODY));
if (ciphertext == null) {
return new DisplayRecord.Body("", true);
}
try {
if (SmsDatabase.Types.isSymmetricEncryption(type)) {
String plaintext = plaintextCache.get(ciphertext);
if (plaintext != null)
return new DisplayRecord.Body(plaintext, true);
plaintext = masterCipher.decryptBody(ciphertext);
plaintextCache.put(ciphertext, plaintext);
return new DisplayRecord.Body(plaintext, true);
} else {
return new DisplayRecord.Body(ciphertext, true);
}
} catch (InvalidMessageException e) {
Log.w("EncryptingSmsDatabase", e);
return new DisplayRecord.Body(context.getString(R.string.EncryptingSmsDatabase_error_decrypting_message), true);
}
}
}
private static class PlaintextCache {
private static final int MAX_CACHE_SIZE = 2000;
private static final Map<String, SoftReference<String>> decryptedBodyCache =
Collections.synchronizedMap(new LRUCache<String, SoftReference<String>>(MAX_CACHE_SIZE));
public void put(String ciphertext, String plaintext) {
decryptedBodyCache.put(ciphertext, new SoftReference<String>(plaintext));
}
public String get(String ciphertext) {
SoftReference<String> plaintextReference = decryptedBodyCache.get(ciphertext);
if (plaintextReference != null) {
String plaintext = plaintextReference.get();
if (plaintext != null) {
return plaintext;
}
}
return null;
}
}
}

View File

@ -5,8 +5,6 @@ import android.annotation.SuppressLint;
import android.content.ContentValues; import android.content.ContentValues;
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.SQLiteOpenHelper;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
@ -14,6 +12,9 @@ import android.text.TextUtils;
import com.annimon.stream.Stream; import com.annimon.stream.Stream;
import net.sqlcipher.database.SQLiteDatabase;
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper;
import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.util.BitmapUtil; import org.thoughtcrime.securesms.util.BitmapUtil;
import org.thoughtcrime.securesms.util.GroupUtil; import org.thoughtcrime.securesms.util.GroupUtil;
@ -64,7 +65,7 @@ public class GroupDatabase extends Database {
AVATAR_DIGEST + " BLOB, " + AVATAR_DIGEST + " BLOB, " +
MMS + " INTEGER DEFAULT 0);"; MMS + " INTEGER DEFAULT 0);";
static final String[] CREATE_INDEXS = { public static final String[] CREATE_INDEXS = {
"CREATE UNIQUE INDEX IF NOT EXISTS group_id_index ON " + TABLE_NAME + " (" + GROUP_ID + ");", "CREATE UNIQUE INDEX IF NOT EXISTS group_id_index ON " + TABLE_NAME + " (" + GROUP_ID + ");",
}; };
@ -75,7 +76,7 @@ public class GroupDatabase extends Database {
static final List<String> TYPED_GROUP_PROJECTION = Stream.of(GROUP_PROJECTION).map(columnName -> TABLE_NAME + "." + columnName).toList(); static final List<String> TYPED_GROUP_PROJECTION = Stream.of(GROUP_PROJECTION).map(columnName -> TABLE_NAME + "." + columnName).toList();
public GroupDatabase(Context context, SQLiteOpenHelper databaseHelper) { public GroupDatabase(Context context, SQLCipherOpenHelper databaseHelper) {
super(context, databaseHelper); super(context, databaseHelper);
} }

View File

@ -4,10 +4,12 @@ package org.thoughtcrime.securesms.database;
import android.content.ContentValues; import android.content.ContentValues;
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.SQLiteOpenHelper;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import net.sqlcipher.database.SQLiteDatabase;
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
@ -33,7 +35,7 @@ public class GroupReceiptDatabase extends Database {
"CREATE INDEX IF NOT EXISTS group_receipt_mms_id_index ON " + TABLE_NAME + " (" + MMS_ID + ");", "CREATE INDEX IF NOT EXISTS group_receipt_mms_id_index ON " + TABLE_NAME + " (" + MMS_ID + ");",
}; };
public GroupReceiptDatabase(Context context, SQLiteOpenHelper databaseHelper) { public GroupReceiptDatabase(Context context, SQLCipherOpenHelper databaseHelper) {
super(context, databaseHelper); super(context, databaseHelper);
} }

View File

@ -19,12 +19,13 @@ package org.thoughtcrime.securesms.database;
import android.content.ContentValues; import android.content.ContentValues;
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.SQLiteOpenHelper;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import net.sqlcipher.database.SQLiteDatabase;
import org.greenrobot.eventbus.EventBus; import org.greenrobot.eventbus.EventBus;
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper;
import org.thoughtcrime.securesms.util.Base64; import org.thoughtcrime.securesms.util.Base64;
import org.whispersystems.libsignal.IdentityKey; import org.whispersystems.libsignal.IdentityKey;
import org.whispersystems.libsignal.InvalidKeyException; import org.whispersystems.libsignal.InvalidKeyException;
@ -72,7 +73,7 @@ public class IdentityDatabase extends Database {
} }
} }
IdentityDatabase(Context context, SQLiteOpenHelper databaseHelper) { IdentityDatabase(Context context, SQLCipherOpenHelper databaseHelper) {
super(context, databaseHelper); super(context, databaseHelper);
} }

View File

@ -2,14 +2,14 @@ package org.thoughtcrime.securesms.database;
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.SQLiteOpenHelper;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import net.sqlcipher.database.SQLiteDatabase;
import org.thoughtcrime.securesms.attachments.Attachment; import org.thoughtcrime.securesms.attachments.Attachment;
import org.thoughtcrime.securesms.attachments.DatabaseAttachment; import org.thoughtcrime.securesms.attachments.DatabaseAttachment;
import org.thoughtcrime.securesms.crypto.MasterSecret; import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper;
public class MediaDatabase extends Database { public class MediaDatabase extends Database {
@ -44,7 +44,7 @@ public class MediaDatabase extends Database {
private static final String GALLERY_MEDIA_QUERY = String.format(BASE_MEDIA_QUERY, AttachmentDatabase.CONTENT_TYPE + " LIKE 'image/%' OR " + AttachmentDatabase.CONTENT_TYPE + " LIKE 'video/%'"); private static final String GALLERY_MEDIA_QUERY = String.format(BASE_MEDIA_QUERY, AttachmentDatabase.CONTENT_TYPE + " LIKE 'image/%' OR " + AttachmentDatabase.CONTENT_TYPE + " LIKE 'video/%'");
private static final String DOCUMENT_MEDIA_QUERY = String.format(BASE_MEDIA_QUERY, AttachmentDatabase.CONTENT_TYPE + " NOT LIKE 'image/%' AND " + AttachmentDatabase.CONTENT_TYPE + " NOT LIKE 'video/%' AND " + AttachmentDatabase.CONTENT_TYPE + " NOT LIKE 'audio/%'"); private static final String DOCUMENT_MEDIA_QUERY = String.format(BASE_MEDIA_QUERY, AttachmentDatabase.CONTENT_TYPE + " NOT LIKE 'image/%' AND " + AttachmentDatabase.CONTENT_TYPE + " NOT LIKE 'video/%' AND " + AttachmentDatabase.CONTENT_TYPE + " NOT LIKE 'audio/%'");
public MediaDatabase(Context context, SQLiteOpenHelper databaseHelper) { public MediaDatabase(Context context, SQLCipherOpenHelper databaseHelper) {
super(context, databaseHelper); super(context, databaseHelper);
} }
@ -76,9 +76,9 @@ public class MediaDatabase extends Database {
this.outgoing = outgoing; this.outgoing = outgoing;
} }
public static MediaRecord from(@NonNull Context context, @NonNull MasterSecret masterSecret, @NonNull Cursor cursor) { public static MediaRecord from(@NonNull Context context, @NonNull Cursor cursor) {
AttachmentDatabase attachmentDatabase = DatabaseFactory.getAttachmentDatabase(context); AttachmentDatabase attachmentDatabase = DatabaseFactory.getAttachmentDatabase(context);
DatabaseAttachment attachment = attachmentDatabase.getAttachment(masterSecret, cursor); DatabaseAttachment attachment = attachmentDatabase.getAttachment(cursor);
String serializedAddress = cursor.getString(cursor.getColumnIndexOrThrow(MmsDatabase.ADDRESS)); String serializedAddress = cursor.getString(cursor.getColumnIndexOrThrow(MmsDatabase.ADDRESS));
boolean outgoing = MessagingDatabase.Types.isOutgoingMessageType(cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.MESSAGE_BOX))); boolean outgoing = MessagingDatabase.Types.isOutgoingMessageType(cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.MESSAGE_BOX)));
Address address = null; Address address = null;

View File

@ -3,14 +3,15 @@ package org.thoughtcrime.securesms.database;
import android.content.ContentValues; import android.content.ContentValues;
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.SQLiteOpenHelper;
import android.text.TextUtils; import android.text.TextUtils;
import android.util.Log; import android.util.Log;
import net.sqlcipher.database.SQLiteDatabase;
import org.thoughtcrime.securesms.database.documents.Document; import org.thoughtcrime.securesms.database.documents.Document;
import org.thoughtcrime.securesms.database.documents.IdentityKeyMismatch; import org.thoughtcrime.securesms.database.documents.IdentityKeyMismatch;
import org.thoughtcrime.securesms.database.documents.IdentityKeyMismatchList; import org.thoughtcrime.securesms.database.documents.IdentityKeyMismatchList;
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper;
import org.thoughtcrime.securesms.util.JsonUtils; import org.thoughtcrime.securesms.util.JsonUtils;
import org.whispersystems.libsignal.IdentityKey; import org.whispersystems.libsignal.IdentityKey;
@ -23,7 +24,7 @@ public abstract class MessagingDatabase extends Database implements MmsSmsColumn
private static final String TAG = MessagingDatabase.class.getSimpleName(); private static final String TAG = MessagingDatabase.class.getSimpleName();
public MessagingDatabase(Context context, SQLiteOpenHelper databaseHelper) { public MessagingDatabase(Context context, SQLCipherOpenHelper databaseHelper) {
super(context, databaseHelper); super(context, databaseHelper);
} }

View File

@ -19,8 +19,6 @@ package org.thoughtcrime.securesms.database;
import android.content.ContentValues; import android.content.ContentValues;
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.SQLiteOpenHelper;
import android.net.Uri; import android.net.Uri;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
@ -32,19 +30,17 @@ import com.annimon.stream.Stream;
import com.google.android.mms.pdu_alt.NotificationInd; import com.google.android.mms.pdu_alt.NotificationInd;
import com.google.android.mms.pdu_alt.PduHeaders; import com.google.android.mms.pdu_alt.PduHeaders;
import net.sqlcipher.database.SQLiteDatabase;
import org.thoughtcrime.securesms.ApplicationContext; import org.thoughtcrime.securesms.ApplicationContext;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.attachments.Attachment; import org.thoughtcrime.securesms.attachments.Attachment;
import org.thoughtcrime.securesms.attachments.DatabaseAttachment; import org.thoughtcrime.securesms.attachments.DatabaseAttachment;
import org.thoughtcrime.securesms.attachments.MmsNotificationAttachment; import org.thoughtcrime.securesms.attachments.MmsNotificationAttachment;
import org.thoughtcrime.securesms.crypto.AsymmetricMasterCipher;
import org.thoughtcrime.securesms.crypto.MasterCipher;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.crypto.MasterSecretUnion;
import org.thoughtcrime.securesms.database.documents.IdentityKeyMismatch; import org.thoughtcrime.securesms.database.documents.IdentityKeyMismatch;
import org.thoughtcrime.securesms.database.documents.IdentityKeyMismatchList; import org.thoughtcrime.securesms.database.documents.IdentityKeyMismatchList;
import org.thoughtcrime.securesms.database.documents.NetworkFailure; import org.thoughtcrime.securesms.database.documents.NetworkFailure;
import org.thoughtcrime.securesms.database.documents.NetworkFailureList; import org.thoughtcrime.securesms.database.documents.NetworkFailureList;
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper;
import org.thoughtcrime.securesms.database.model.DisplayRecord; import org.thoughtcrime.securesms.database.model.DisplayRecord;
import org.thoughtcrime.securesms.database.model.MediaMmsMessageRecord; import org.thoughtcrime.securesms.database.model.MediaMmsMessageRecord;
import org.thoughtcrime.securesms.database.model.MessageRecord; import org.thoughtcrime.securesms.database.model.MessageRecord;
@ -63,7 +59,6 @@ import org.thoughtcrime.securesms.util.JsonUtils;
import org.thoughtcrime.securesms.util.TextSecurePreferences; import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.Util; import org.thoughtcrime.securesms.util.Util;
import org.whispersystems.jobqueue.JobManager; import org.whispersystems.jobqueue.JobManager;
import org.whispersystems.libsignal.InvalidMessageException;
import org.whispersystems.libsignal.util.guava.Optional; import org.whispersystems.libsignal.util.guava.Optional;
import java.io.IOException; import java.io.IOException;
@ -153,7 +148,7 @@ public class MmsDatabase extends MessagingDatabase {
private final JobManager jobManager; private final JobManager jobManager;
public MmsDatabase(Context context, SQLiteOpenHelper databaseHelper) { public MmsDatabase(Context context, SQLCipherOpenHelper databaseHelper) {
super(context, databaseHelper); super(context, databaseHelper);
this.jobManager = ApplicationContext.getInstance(context).getJobManager(); this.jobManager = ApplicationContext.getInstance(context).getJobManager();
} }
@ -289,14 +284,14 @@ public class MmsDatabase extends MessagingDatabase {
return cursor; return cursor;
} }
public Reader getExpireStartedMessages(@Nullable MasterSecret masterSecret) { public Reader getExpireStartedMessages() {
String where = EXPIRE_STARTED + " > 0"; String where = EXPIRE_STARTED + " > 0";
return readerFor(masterSecret, rawQuery(where, null)); return readerFor(rawQuery(where, null));
} }
public Reader getDecryptInProgressMessages(MasterSecret masterSecret) { public Reader getDecryptInProgressMessages() {
String where = MESSAGE_BOX + " & " + (Types.ENCRYPTION_ASYMMETRIC_BIT) + " != 0"; String where = MESSAGE_BOX + " & " + (Types.ENCRYPTION_ASYMMETRIC_BIT) + " != 0";
return readerFor(masterSecret, rawQuery(where, null)); return readerFor(rawQuery(where, null));
} }
private void updateMailboxBitmask(long id, long maskOff, long maskOn, Optional<Long> threadId) { private void updateMailboxBitmask(long id, long maskOff, long maskOn, Optional<Long> threadId) {
@ -494,16 +489,8 @@ public class MmsDatabase extends MessagingDatabase {
return expiring; return expiring;
} }
public void updateMessageBody(MasterSecretUnion masterSecret, long messageId, String body) { public void updateMessageBody(long messageId, String body) {
body = getEncryptedBody(masterSecret, body); long type = 0;
long type;
if (masterSecret.getMasterSecret().isPresent()) {
type = Types.ENCRYPTION_SYMMETRIC_BIT;
} else {
type = Types.ENCRYPTION_ASYMMETRIC_BIT;
}
updateMessageBodyAndType(messageId, body, Types.ENCRYPTION_MASK, type); updateMessageBodyAndType(messageId, body, Types.ENCRYPTION_MASK, type);
} }
@ -544,7 +531,7 @@ public class MmsDatabase extends MessagingDatabase {
} }
} }
public OutgoingMediaMessage getOutgoingMessage(MasterSecret masterSecret, long messageId) public OutgoingMediaMessage getOutgoingMessage(long messageId)
throws MmsException, NoSuchMessageException throws MmsException, NoSuchMessageException
{ {
AttachmentDatabase attachmentDatabase = DatabaseFactory.getAttachmentDatabase(context); AttachmentDatabase attachmentDatabase = DatabaseFactory.getAttachmentDatabase(context);
@ -555,13 +542,12 @@ public class MmsDatabase extends MessagingDatabase {
if (cursor != null && cursor.moveToNext()) { if (cursor != null && cursor.moveToNext()) {
long outboxType = cursor.getLong(cursor.getColumnIndexOrThrow(MESSAGE_BOX)); long outboxType = cursor.getLong(cursor.getColumnIndexOrThrow(MESSAGE_BOX));
String messageText = cursor.getString(cursor.getColumnIndexOrThrow(BODY)); String body = cursor.getString(cursor.getColumnIndexOrThrow(BODY));
long timestamp = cursor.getLong(cursor.getColumnIndexOrThrow(NORMALIZED_DATE_SENT)); long timestamp = cursor.getLong(cursor.getColumnIndexOrThrow(NORMALIZED_DATE_SENT));
int subscriptionId = cursor.getInt(cursor.getColumnIndexOrThrow(SUBSCRIPTION_ID)); int subscriptionId = cursor.getInt(cursor.getColumnIndexOrThrow(SUBSCRIPTION_ID));
long expiresIn = cursor.getLong(cursor.getColumnIndexOrThrow(EXPIRES_IN)); long expiresIn = cursor.getLong(cursor.getColumnIndexOrThrow(EXPIRES_IN));
List<Attachment> attachments = new LinkedList<Attachment>(attachmentDatabase.getAttachmentsForMessage(masterSecret, messageId)); List<Attachment> attachments = new LinkedList<>(attachmentDatabase.getAttachmentsForMessage(messageId));
String address = cursor.getString(cursor.getColumnIndexOrThrow(ADDRESS)); String address = cursor.getString(cursor.getColumnIndexOrThrow(ADDRESS));
String body = getDecryptedBody(masterSecret, messageText, outboxType);
long threadId = cursor.getLong(cursor.getColumnIndexOrThrow(THREAD_ID)); long threadId = cursor.getLong(cursor.getColumnIndexOrThrow(THREAD_ID));
int distributionType = DatabaseFactory.getThreadDatabase(context).getDistributionType(threadId); int distributionType = DatabaseFactory.getThreadDatabase(context).getDistributionType(threadId);
@ -591,9 +577,9 @@ public class MmsDatabase extends MessagingDatabase {
} }
} }
public long copyMessageInbox(MasterSecret masterSecret, long messageId) throws MmsException { public long copyMessageInbox(long messageId) throws MmsException {
try { try {
OutgoingMediaMessage request = getOutgoingMessage(masterSecret, messageId); OutgoingMediaMessage request = getOutgoingMessage(messageId);
ContentValues contentValues = new ContentValues(); ContentValues contentValues = new ContentValues();
contentValues.put(ADDRESS, request.getRecipient().getAddress().serialize()); contentValues.put(ADDRESS, request.getRecipient().getAddress().serialize());
contentValues.put(DATE_SENT, request.getSentTimeMillis()); contentValues.put(DATE_SENT, request.getSentTimeMillis());
@ -623,8 +609,7 @@ public class MmsDatabase extends MessagingDatabase {
databaseAttachment.isVoiceNote())); databaseAttachment.isVoiceNote()));
} }
return insertMediaMessage(new MasterSecretUnion(masterSecret), return insertMediaMessage(request.getBody(),
request.getBody(),
attachments, attachments,
contentValues, contentValues,
null); null);
@ -633,8 +618,7 @@ public class MmsDatabase extends MessagingDatabase {
} }
} }
private Optional<InsertResult> insertMessageInbox(MasterSecretUnion masterSecret, private Optional<InsertResult> insertMessageInbox(IncomingMediaMessage retrieved,
IncomingMediaMessage retrieved,
String contentLocation, String contentLocation,
long threadId, long mailbox) long threadId, long mailbox)
throws MmsException throws MmsException
@ -674,7 +658,7 @@ public class MmsDatabase extends MessagingDatabase {
return Optional.absent(); return Optional.absent();
} }
long messageId = insertMediaMessage(masterSecret, retrieved.getBody(), retrieved.getAttachments(), contentValues, null); long messageId = insertMediaMessage(retrieved.getBody(), retrieved.getAttachments(), contentValues, null);
if (!Types.isExpirationTimerUpdate(mailbox)) { if (!Types.isExpirationTimerUpdate(mailbox)) {
DatabaseFactory.getThreadDatabase(context).incrementUnread(threadId, 1); DatabaseFactory.getThreadDatabase(context).incrementUnread(threadId, 1);
@ -687,19 +671,12 @@ public class MmsDatabase extends MessagingDatabase {
return Optional.of(new InsertResult(messageId, threadId)); return Optional.of(new InsertResult(messageId, threadId));
} }
public Optional<InsertResult> insertMessageInbox(MasterSecretUnion masterSecret, public Optional<InsertResult> insertMessageInbox(IncomingMediaMessage retrieved,
IncomingMediaMessage retrieved,
String contentLocation, long threadId) String contentLocation, long threadId)
throws MmsException throws MmsException
{ {
long type = Types.BASE_INBOX_TYPE; long type = Types.BASE_INBOX_TYPE;
if (masterSecret.getMasterSecret().isPresent()) {
type |= Types.ENCRYPTION_SYMMETRIC_BIT;
} else {
type |= Types.ENCRYPTION_ASYMMETRIC_BIT;
}
if (retrieved.isPushMessage()) { if (retrieved.isPushMessage()) {
type |= Types.PUSH_MESSAGE_BIT; type |= Types.PUSH_MESSAGE_BIT;
} }
@ -708,22 +685,14 @@ public class MmsDatabase extends MessagingDatabase {
type |= Types.EXPIRATION_TIMER_UPDATE_BIT; type |= Types.EXPIRATION_TIMER_UPDATE_BIT;
} }
return insertMessageInbox(masterSecret, retrieved, contentLocation, threadId, type); return insertMessageInbox(retrieved, contentLocation, threadId, type);
} }
public Optional<InsertResult> insertSecureDecryptedMessageInbox(MasterSecretUnion masterSecret, public Optional<InsertResult> insertSecureDecryptedMessageInbox(IncomingMediaMessage retrieved, long threadId)
IncomingMediaMessage retrieved,
long threadId)
throws MmsException throws MmsException
{ {
long type = Types.BASE_INBOX_TYPE | Types.SECURE_MESSAGE_BIT; long type = Types.BASE_INBOX_TYPE | Types.SECURE_MESSAGE_BIT;
if (masterSecret.getMasterSecret().isPresent()) {
type |= Types.ENCRYPTION_SYMMETRIC_BIT;
} else {
type |= Types.ENCRYPTION_ASYMMETRIC_BIT;
}
if (retrieved.isPushMessage()) { if (retrieved.isPushMessage()) {
type |= Types.PUSH_MESSAGE_BIT; type |= Types.PUSH_MESSAGE_BIT;
} }
@ -732,7 +701,7 @@ public class MmsDatabase extends MessagingDatabase {
type |= Types.EXPIRATION_TIMER_UPDATE_BIT; type |= Types.EXPIRATION_TIMER_UPDATE_BIT;
} }
return insertMessageInbox(masterSecret, retrieved, "", threadId, type); return insertMessageInbox(retrieved, "", threadId, type);
} }
public Pair<Long, Long> insertMessageInbox(@NonNull NotificationInd notification, int subscriptionId) { public Pair<Long, Long> insertMessageInbox(@NonNull NotificationInd notification, int subscriptionId) {
@ -781,17 +750,13 @@ public class MmsDatabase extends MessagingDatabase {
jobManager.add(new TrimThreadJob(context, threadId)); jobManager.add(new TrimThreadJob(context, threadId));
} }
public long insertMessageOutbox(@NonNull MasterSecretUnion masterSecret, public long insertMessageOutbox(@NonNull OutgoingMediaMessage message,
@NonNull OutgoingMediaMessage message,
long threadId, boolean forceSms, long threadId, boolean forceSms,
@Nullable SmsDatabase.InsertListener insertListener) @Nullable SmsDatabase.InsertListener insertListener)
throws MmsException throws MmsException
{ {
long type = Types.BASE_SENDING_TYPE; long type = Types.BASE_SENDING_TYPE;
if (masterSecret.getMasterSecret().isPresent()) type |= Types.ENCRYPTION_SYMMETRIC_BIT;
else type |= Types.ENCRYPTION_ASYMMETRIC_BIT;
if (message.isSecure()) type |= (Types.SECURE_MESSAGE_BIT | Types.PUSH_MESSAGE_BIT); if (message.isSecure()) type |= (Types.SECURE_MESSAGE_BIT | Types.PUSH_MESSAGE_BIT);
if (forceSms) type |= Types.MESSAGE_FORCE_SMS_BIT; if (forceSms) type |= Types.MESSAGE_FORCE_SMS_BIT;
@ -821,7 +786,7 @@ public class MmsDatabase extends MessagingDatabase {
contentValues.put(DELIVERY_RECEIPT_COUNT, Stream.of(earlyDeliveryReceipts.values()).mapToLong(Long::longValue).sum()); contentValues.put(DELIVERY_RECEIPT_COUNT, Stream.of(earlyDeliveryReceipts.values()).mapToLong(Long::longValue).sum());
contentValues.put(READ_RECEIPT_COUNT, Stream.of(earlyReadReceipts.values()).mapToLong(Long::longValue).sum()); contentValues.put(READ_RECEIPT_COUNT, Stream.of(earlyReadReceipts.values()).mapToLong(Long::longValue).sum());
long messageId = insertMediaMessage(masterSecret, message.getBody(), message.getAttachments(), contentValues, insertListener); long messageId = insertMediaMessage(message.getBody(), message.getAttachments(), contentValues, insertListener);
if (message.getRecipient().getAddress().isGroup()) { if (message.getRecipient().getAddress().isGroup()) {
List<Recipient> members = DatabaseFactory.getGroupDatabase(context).getGroupMembers(message.getRecipient().getAddress().toGroupString(), false); List<Recipient> members = DatabaseFactory.getGroupDatabase(context).getGroupMembers(message.getRecipient().getAddress().toGroupString(), false);
@ -841,33 +806,7 @@ public class MmsDatabase extends MessagingDatabase {
return messageId; return messageId;
} }
private String getEncryptedBody(MasterSecretUnion masterSecret, String body) { private long insertMediaMessage(@Nullable String body,
if (masterSecret.getMasterSecret().isPresent()) {
return new MasterCipher(masterSecret.getMasterSecret().get()).encryptBody(body);
} else {
return new AsymmetricMasterCipher(masterSecret.getAsymmetricMasterSecret().get()).encryptBody(body);
}
}
private @Nullable String getDecryptedBody(@NonNull MasterSecret masterSecret,
@Nullable String body, long outboxType)
{
try {
if (!TextUtils.isEmpty(body) && Types.isSymmetricEncryption(outboxType)) {
MasterCipher masterCipher = new MasterCipher(masterSecret);
return masterCipher.decryptBody(body);
} else {
return body;
}
} catch (InvalidMessageException e) {
Log.w(TAG, e);
}
return null;
}
private long insertMediaMessage(@NonNull MasterSecretUnion masterSecret,
@Nullable String body,
@NonNull List<Attachment> attachments, @NonNull List<Attachment> attachments,
@NonNull ContentValues contentValues, @NonNull ContentValues contentValues,
@Nullable SmsDatabase.InsertListener insertListener) @Nullable SmsDatabase.InsertListener insertListener)
@ -876,21 +815,14 @@ public class MmsDatabase extends MessagingDatabase {
SQLiteDatabase db = databaseHelper.getWritableDatabase(); SQLiteDatabase db = databaseHelper.getWritableDatabase();
AttachmentDatabase partsDatabase = DatabaseFactory.getAttachmentDatabase(context); AttachmentDatabase partsDatabase = DatabaseFactory.getAttachmentDatabase(context);
if (Types.isSymmetricEncryption(contentValues.getAsLong(MESSAGE_BOX)) || contentValues.put(BODY, body);
Types.isAsymmetricEncryption(contentValues.getAsLong(MESSAGE_BOX)))
{
if (!TextUtils.isEmpty(body)) {
contentValues.put(BODY, getEncryptedBody(masterSecret, body));
}
}
contentValues.put(PART_COUNT, attachments.size()); contentValues.put(PART_COUNT, attachments.size());
db.beginTransaction(); db.beginTransaction();
try { try {
long messageId = db.insert(TABLE_NAME, null, contentValues); long messageId = db.insert(TABLE_NAME, null, contentValues);
partsDatabase.insertAttachmentsForMessage(masterSecret, messageId, attachments); partsDatabase.insertAttachmentsForMessage(messageId, attachments);
db.setTransactionSuccessful(); db.setTransactionSuccessful();
return messageId; return messageId;
@ -1016,8 +948,8 @@ public class MmsDatabase extends MessagingDatabase {
} }
} }
public Reader readerFor(MasterSecret masterSecret, Cursor cursor) { public Reader readerFor(Cursor cursor) {
return new Reader(masterSecret, cursor); return new Reader(cursor);
} }
public OutgoingMessageReader readerFor(OutgoingMediaMessage message, long threadId) { public OutgoingMessageReader readerFor(OutgoingMediaMessage message, long threadId) {
@ -1098,15 +1030,9 @@ public class MmsDatabase extends MessagingDatabase {
public class Reader { public class Reader {
private final Cursor cursor; private final Cursor cursor;
private final MasterSecret masterSecret;
private final MasterCipher masterCipher;
public Reader(MasterSecret masterSecret, Cursor cursor) { public Reader(Cursor cursor) {
this.cursor = cursor; this.cursor = cursor;
this.masterSecret = masterSecret;
if (masterSecret != null) masterCipher = new MasterCipher(masterSecret);
else masterCipher = null;
} }
public MessageRecord getNext() { public MessageRecord getNext() {
@ -1239,27 +1165,12 @@ public class MmsDatabase extends MessagingDatabase {
} }
private DisplayRecord.Body getBody(Cursor cursor) { private DisplayRecord.Body getBody(Cursor cursor) {
try {
String body = cursor.getString(cursor.getColumnIndexOrThrow(MmsDatabase.BODY)); String body = cursor.getString(cursor.getColumnIndexOrThrow(MmsDatabase.BODY));
long box = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.MESSAGE_BOX));
if (!TextUtils.isEmpty(body) && masterCipher != null && Types.isSymmetricEncryption(box)) {
return new DisplayRecord.Body(masterCipher.decryptBody(body), true);
} else if (!TextUtils.isEmpty(body) && masterCipher == null && Types.isSymmetricEncryption(box)) {
return new DisplayRecord.Body(body, false);
} else if (!TextUtils.isEmpty(body) && Types.isAsymmetricEncryption(box)) {
return new DisplayRecord.Body(body, false);
} else {
return new DisplayRecord.Body(body == null ? "" : body, true); return new DisplayRecord.Body(body == null ? "" : body, true);
} }
} catch (InvalidMessageException e) {
Log.w("MmsDatabase", e);
return new DisplayRecord.Body(context.getString(R.string.MmsDatabase_error_decrypting_message), true);
}
}
private SlideDeck getSlideDeck(@NonNull Cursor cursor) { private SlideDeck getSlideDeck(@NonNull Cursor cursor) {
Attachment attachment = DatabaseFactory.getAttachmentDatabase(context).getAttachment(masterSecret, cursor); Attachment attachment = DatabaseFactory.getAttachmentDatabase(context).getAttachment(cursor);
return new SlideDeck(context, attachment); return new SlideDeck(context, attachment);
} }

View File

@ -71,9 +71,9 @@ public interface MmsSmsColumns {
protected static final long GROUP_QUIT_BIT = 0x20000; protected static final long GROUP_QUIT_BIT = 0x20000;
protected static final long EXPIRATION_TIMER_UPDATE_BIT = 0x40000; protected static final long EXPIRATION_TIMER_UPDATE_BIT = 0x40000;
// Encrypted Storage Information // Encrypted Storage Information XXX
protected static final long ENCRYPTION_MASK = 0xFF000000; public static final long ENCRYPTION_MASK = 0xFF000000;
protected static final long ENCRYPTION_SYMMETRIC_BIT = 0x80000000; public static final long ENCRYPTION_SYMMETRIC_BIT = 0x80000000;
protected static final long ENCRYPTION_ASYMMETRIC_BIT = 0x40000000; protected static final long ENCRYPTION_ASYMMETRIC_BIT = 0x40000000;
protected static final long ENCRYPTION_REMOTE_BIT = 0x20000000; protected static final long ENCRYPTION_REMOTE_BIT = 0x20000000;
protected static final long ENCRYPTION_REMOTE_FAILED_BIT = 0x10000000; protected static final long ENCRYPTION_REMOTE_FAILED_BIT = 0x10000000;

View File

@ -1,4 +1,4 @@
/** /*
* 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
@ -18,23 +18,22 @@ package org.thoughtcrime.securesms.database;
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.SQLiteOpenHelper;
import android.database.sqlite.SQLiteQueryBuilder;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.Log; import android.util.Log;
import org.thoughtcrime.securesms.crypto.MasterSecret; import net.sqlcipher.database.SQLiteDatabase;
import net.sqlcipher.database.SQLiteQueryBuilder;
import org.thoughtcrime.securesms.database.MessagingDatabase.SyncMessageId; import org.thoughtcrime.securesms.database.MessagingDatabase.SyncMessageId;
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper;
import org.thoughtcrime.securesms.database.model.MessageRecord; import org.thoughtcrime.securesms.database.model.MessageRecord;
import org.whispersystems.libsignal.util.guava.Optional;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
public class MmsSmsDatabase extends Database { public class MmsSmsDatabase extends Database {
@SuppressWarnings("unused")
private static final String TAG = MmsSmsDatabase.class.getSimpleName(); private static final String TAG = MmsSmsDatabase.class.getSimpleName();
public static final String TRANSPORT = "transport_type"; public static final String TRANSPORT = "transport_type";
@ -77,7 +76,7 @@ public class MmsSmsDatabase extends Database {
AttachmentDatabase.NAME, AttachmentDatabase.NAME,
AttachmentDatabase.TRANSFER_STATE}; AttachmentDatabase.TRANSFER_STATE};
public MmsSmsDatabase(Context context, SQLiteOpenHelper databaseHelper) { public MmsSmsDatabase(Context context, SQLCipherOpenHelper databaseHelper) {
super(context, databaseHelper); super(context, databaseHelper);
} }
@ -309,10 +308,6 @@ public class MmsSmsDatabase extends Database {
return db.rawQuery(query, null); return db.rawQuery(query, null);
} }
public Reader readerFor(@NonNull Cursor cursor, @Nullable MasterSecret masterSecret) {
return new Reader(cursor, masterSecret);
}
public Reader readerFor(@NonNull Cursor cursor) { public Reader readerFor(@NonNull Cursor cursor) {
return new Reader(cursor); return new Reader(cursor);
} }
@ -320,23 +315,16 @@ public class MmsSmsDatabase extends Database {
public class Reader { public class Reader {
private final Cursor cursor; private final Cursor cursor;
private final Optional<MasterSecret> masterSecret; private SmsDatabase.Reader smsReader;
private EncryptingSmsDatabase.Reader smsReader;
private MmsDatabase.Reader mmsReader; private MmsDatabase.Reader mmsReader;
public Reader(Cursor cursor, @Nullable MasterSecret masterSecret) {
this.cursor = cursor;
this.masterSecret = Optional.fromNullable(masterSecret);
}
public Reader(Cursor cursor) { public Reader(Cursor cursor) {
this(cursor, null); this.cursor = cursor;
} }
private EncryptingSmsDatabase.Reader getSmsReader() { private SmsDatabase.Reader getSmsReader() {
if (smsReader == null) { if (smsReader == null) {
if (masterSecret.isPresent()) smsReader = DatabaseFactory.getEncryptingSmsDatabase(context).readerFor(masterSecret.get(), cursor); smsReader = DatabaseFactory.getSmsDatabase(context).readerFor(cursor);
else smsReader = DatabaseFactory.getSmsDatabase(context).readerFor(cursor);
} }
return smsReader; return smsReader;
@ -344,7 +332,7 @@ public class MmsSmsDatabase extends Database {
private MmsDatabase.Reader getMmsReader() { private MmsDatabase.Reader getMmsReader() {
if (mmsReader == null) { if (mmsReader == null) {
mmsReader = DatabaseFactory.getMmsDatabase(context).readerFor(masterSecret.orNull(), cursor); mmsReader = DatabaseFactory.getMmsDatabase(context).readerFor(cursor);
} }
return mmsReader; return mmsReader;

View File

@ -3,7 +3,6 @@ package org.thoughtcrime.securesms.database;
import android.content.Context; import android.content.Context;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.database.model.SmsMessageRecord; import org.thoughtcrime.securesms.database.model.SmsMessageRecord;
import org.thoughtcrime.securesms.util.StorageUtil; import org.thoughtcrime.securesms.util.StorageUtil;
@ -14,25 +13,27 @@ public class PlaintextBackupExporter {
private static final String FILENAME = "SignalPlaintextBackup.xml"; private static final String FILENAME = "SignalPlaintextBackup.xml";
public static void exportPlaintextToSd(Context context, MasterSecret masterSecret) public static void exportPlaintextToSd(Context context)
throws NoExternalStorageException, IOException throws NoExternalStorageException, IOException
{ {
exportPlaintext(context, masterSecret); exportPlaintext(context);
} }
public static File getPlaintextExportFile() throws NoExternalStorageException { public static File getPlaintextExportFile() throws NoExternalStorageException {
return new File(StorageUtil.getBackupDir(), FILENAME); return new File(StorageUtil.getBackupDir(), FILENAME);
} }
private static void exportPlaintext(Context context, MasterSecret masterSecret) private static void exportPlaintext(Context context)
throws NoExternalStorageException, IOException throws NoExternalStorageException, IOException
{ {
int count = DatabaseFactory.getSmsDatabase(context).getMessageCount(); SmsDatabase database = DatabaseFactory.getSmsDatabase(context);
int count = database.getMessageCount();
XmlBackup.Writer writer = new XmlBackup.Writer(getPlaintextExportFile().getAbsolutePath(), count); XmlBackup.Writer writer = new XmlBackup.Writer(getPlaintextExportFile().getAbsolutePath(), count);
SmsMessageRecord record; SmsMessageRecord record;
EncryptingSmsDatabase.Reader reader = null;
SmsDatabase.Reader reader = null;
int skip = 0; int skip = 0;
int ROW_LIMIT = 500; int ROW_LIMIT = 500;
@ -40,7 +41,7 @@ public class PlaintextBackupExporter {
if (reader != null) if (reader != null)
reader.close(); reader.close();
reader = DatabaseFactory.getEncryptingSmsDatabase(context).getMessages(masterSecret, skip, ROW_LIMIT); reader = database.readerFor(database.getMessages(skip, ROW_LIMIT));
while ((record = reader.getNext()) != null) { while ((record = reader.getNext()) != null) {
XmlBackup.XmlBackupItem item = XmlBackup.XmlBackupItem item =

View File

@ -1,13 +1,12 @@
package org.thoughtcrime.securesms.database; package org.thoughtcrime.securesms.database;
import android.content.Context; import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteStatement;
import android.os.Environment; import android.os.Environment;
import android.util.Log; import android.util.Log;
import org.thoughtcrime.securesms.crypto.MasterCipher; import net.sqlcipher.database.SQLiteDatabase;
import org.thoughtcrime.securesms.crypto.MasterSecret; import net.sqlcipher.database.SQLiteStatement;
import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.Recipient;
import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlPullParserException;
@ -18,7 +17,7 @@ import java.util.Set;
public class PlaintextBackupImporter { public class PlaintextBackupImporter {
public static void importPlaintextFromSd(Context context, MasterSecret masterSecret) public static void importPlaintextFromSd(Context context)
throws NoExternalStorageException, IOException throws NoExternalStorageException, IOException
{ {
Log.w("PlaintextBackupImporter", "importPlaintext()"); Log.w("PlaintextBackupImporter", "importPlaintext()");
@ -28,7 +27,6 @@ public class PlaintextBackupImporter {
try { try {
ThreadDatabase threads = DatabaseFactory.getThreadDatabase(context); ThreadDatabase threads = DatabaseFactory.getThreadDatabase(context);
XmlBackup backup = new XmlBackup(getPlaintextExportFile().getAbsolutePath()); XmlBackup backup = new XmlBackup(getPlaintextExportFile().getAbsolutePath());
MasterCipher masterCipher = new MasterCipher(masterSecret);
Set<Long> modifiedThreads = new HashSet<>(); Set<Long> modifiedThreads = new HashSet<>();
XmlBackup.XmlBackupItem item; XmlBackup.XmlBackupItem item;
@ -53,7 +51,7 @@ public class PlaintextBackupImporter {
addTranslatedTypeToStatement(statement, 8, item.getType()); addTranslatedTypeToStatement(statement, 8, item.getType());
addNullToStatement(statement, 9); addNullToStatement(statement, 9);
addStringToStatement(statement, 10, item.getSubject()); addStringToStatement(statement, 10, item.getSubject());
addEncryptedStringToStatement(masterCipher, statement, 11, item.getBody()); addStringToStatement(statement, 11, item.getBody());
addStringToStatement(statement, 12, item.getServiceCenter()); addStringToStatement(statement, 12, item.getServiceCenter());
addLongToStatement(statement, 13, threadId); addLongToStatement(statement, 13, threadId);
modifiedThreads.add(threadId); modifiedThreads.add(threadId);
@ -80,14 +78,7 @@ public class PlaintextBackupImporter {
return !backup.exists() && oldBackup.exists() ? oldBackup : backup; return !backup.exists() && oldBackup.exists() ? oldBackup : backup;
} }
private static void addEncryptedStringToStatement(MasterCipher masterCipher, SQLiteStatement statement, int index, String value) { @SuppressWarnings("SameParameterValue")
if (value == null || value.equals("null")) {
statement.bindNull(index);
} else {
statement.bindString(index, masterCipher.encryptBody(value));
}
}
private static void addTranslatedTypeToStatement(SQLiteStatement statement, int index, int type) { private static void addTranslatedTypeToStatement(SQLiteStatement statement, int index, int type) {
statement.bindLong(index, SmsDatabase.Types.translateFromSystemBaseType(type) | SmsDatabase.Types.ENCRYPTION_SYMMETRIC_BIT); statement.bindLong(index, SmsDatabase.Types.translateFromSystemBaseType(type) | SmsDatabase.Types.ENCRYPTION_SYMMETRIC_BIT);
} }

View File

@ -3,11 +3,12 @@ package org.thoughtcrime.securesms.database;
import android.content.ContentValues; import android.content.ContentValues;
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.SQLiteOpenHelper;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.util.Log; import android.util.Log;
import net.sqlcipher.database.SQLiteDatabase;
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper;
import org.thoughtcrime.securesms.util.Base64; import org.thoughtcrime.securesms.util.Base64;
import org.whispersystems.libsignal.util.guava.Optional; import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.api.messages.SignalServiceEnvelope; import org.whispersystems.signalservice.api.messages.SignalServiceEnvelope;
@ -31,7 +32,7 @@ public class PushDatabase 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, " +
TYPE + " INTEGER, " + SOURCE + " TEXT, " + DEVICE_ID + " INTEGER, " + LEGACY_MSG + " TEXT, " + CONTENT + " TEXT, " + TIMESTAMP + " INTEGER);"; TYPE + " INTEGER, " + SOURCE + " TEXT, " + DEVICE_ID + " INTEGER, " + LEGACY_MSG + " TEXT, " + CONTENT + " TEXT, " + TIMESTAMP + " INTEGER);";
public PushDatabase(Context context, SQLiteOpenHelper databaseHelper) { public PushDatabase(Context context, SQLCipherOpenHelper databaseHelper) {
super(context, databaseHelper); super(context, databaseHelper);
} }

View File

@ -3,8 +3,6 @@ package org.thoughtcrime.securesms.database;
import android.content.ContentValues; import android.content.ContentValues;
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.SQLiteOpenHelper;
import android.net.Uri; import android.net.Uri;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
@ -12,7 +10,10 @@ import android.util.Log;
import com.annimon.stream.Stream; import com.annimon.stream.Stream;
import net.sqlcipher.database.SQLiteDatabase;
import org.thoughtcrime.securesms.color.MaterialColor; import org.thoughtcrime.securesms.color.MaterialColor;
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper;
import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.util.Base64; import org.thoughtcrime.securesms.util.Base64;
import org.thoughtcrime.securesms.util.Util; import org.thoughtcrime.securesms.util.Util;
@ -119,7 +120,7 @@ public class RecipientDatabase extends Database {
SIGNAL_PROFILE_AVATAR + " TEXT DEFAULT NULL, " + SIGNAL_PROFILE_AVATAR + " TEXT DEFAULT NULL, " +
PROFILE_SHARING + " INTEGER DEFAULT 0);"; PROFILE_SHARING + " INTEGER DEFAULT 0);";
public RecipientDatabase(Context context, SQLiteOpenHelper databaseHelper) { public RecipientDatabase(Context context, SQLCipherOpenHelper databaseHelper) {
super(context, databaseHelper); super(context, databaseHelper);
} }

View File

@ -20,9 +20,6 @@ package org.thoughtcrime.securesms.database;
import android.content.ContentValues; import android.content.ContentValues;
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.SQLiteOpenHelper;
import android.database.sqlite.SQLiteStatement;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.text.TextUtils; import android.text.TextUtils;
import android.util.Log; import android.util.Log;
@ -30,9 +27,13 @@ import android.util.Pair;
import com.annimon.stream.Stream; import com.annimon.stream.Stream;
import net.sqlcipher.database.SQLiteDatabase;
import net.sqlcipher.database.SQLiteStatement;
import org.thoughtcrime.securesms.ApplicationContext; import org.thoughtcrime.securesms.ApplicationContext;
import org.thoughtcrime.securesms.database.documents.IdentityKeyMismatch; import org.thoughtcrime.securesms.database.documents.IdentityKeyMismatch;
import org.thoughtcrime.securesms.database.documents.IdentityKeyMismatchList; import org.thoughtcrime.securesms.database.documents.IdentityKeyMismatchList;
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper;
import org.thoughtcrime.securesms.database.model.DisplayRecord; import org.thoughtcrime.securesms.database.model.DisplayRecord;
import org.thoughtcrime.securesms.database.model.MessageRecord; import org.thoughtcrime.securesms.database.model.MessageRecord;
import org.thoughtcrime.securesms.database.model.SmsMessageRecord; import org.thoughtcrime.securesms.database.model.SmsMessageRecord;
@ -107,7 +108,7 @@ public class SmsDatabase extends MessagingDatabase {
private final JobManager jobManager; private final JobManager jobManager;
public SmsDatabase(Context context, SQLiteOpenHelper databaseHelper) { public SmsDatabase(Context context, SQLCipherOpenHelper databaseHelper) {
super(context, databaseHelper); super(context, databaseHelper);
this.jobManager = ApplicationContext.getInstance(context).getJobManager(); this.jobManager = ApplicationContext.getInstance(context).getJobManager();
} }
@ -410,7 +411,17 @@ public class SmsDatabase extends MessagingDatabase {
return results; return results;
} }
protected Pair<Long, Long> updateMessageBodyAndType(long messageId, String body, long maskOff, long maskOn) { public Pair<Long, Long> updateBundleMessageBody(long messageId, String body) {
long type = Types.BASE_INBOX_TYPE | Types.SECURE_MESSAGE_BIT | Types.PUSH_MESSAGE_BIT;
return updateMessageBodyAndType(messageId, body, Types.TOTAL_MASK, type);
}
public void updateMessageBody(long messageId, String body) {
long type = 0;
updateMessageBodyAndType(messageId, body, Types.ENCRYPTION_MASK, type);
}
private Pair<Long, Long> updateMessageBodyAndType(long messageId, String body, long maskOff, long maskOn) {
SQLiteDatabase db = databaseHelper.getWritableDatabase(); SQLiteDatabase db = databaseHelper.getWritableDatabase();
db.execSQL("UPDATE " + TABLE_NAME + " SET " + BODY + " = ?, " + db.execSQL("UPDATE " + TABLE_NAME + " SET " + BODY + " = ?, " +
TYPE + " = (" + TYPE + " & " + (Types.TOTAL_MASK - maskOff) + " | " + maskOn + ") " + TYPE + " = (" + TYPE + " & " + (Types.TOTAL_MASK - maskOff) + " | " + maskOn + ") " +
@ -427,8 +438,8 @@ public class SmsDatabase extends MessagingDatabase {
} }
public Pair<Long, Long> copyMessageInbox(long messageId) { public Pair<Long, Long> copyMessageInbox(long messageId) {
Reader reader = readerFor(getMessage(messageId)); try {
SmsMessageRecord record = reader.getNext(); SmsMessageRecord record = getMessage(messageId);
ContentValues contentValues = new ContentValues(); ContentValues contentValues = new ContentValues();
contentValues.put(TYPE, (record.getType() & ~Types.BASE_TYPE_MASK) | Types.BASE_INBOX_TYPE); contentValues.put(TYPE, (record.getType() & ~Types.BASE_TYPE_MASK) | Types.BASE_INBOX_TYPE);
@ -449,9 +460,11 @@ public class SmsDatabase extends MessagingDatabase {
notifyConversationListeners(record.getThreadId()); notifyConversationListeners(record.getThreadId());
jobManager.add(new TrimThreadJob(context, record.getThreadId())); jobManager.add(new TrimThreadJob(context, record.getThreadId()));
reader.close();
return new Pair<>(newMessageId, record.getThreadId()); return new Pair<>(newMessageId, record.getThreadId());
} catch (NoSuchMessageException e) {
throw new AssertionError(e);
}
} }
public @NonNull Pair<Long, Long> insertReceivedCall(@NonNull Address address) { public @NonNull Pair<Long, Long> insertReceivedCall(@NonNull Address address) {
@ -587,10 +600,11 @@ public class SmsDatabase extends MessagingDatabase {
return insertMessageInbox(message, Types.BASE_INBOX_TYPE); return insertMessageInbox(message, Types.BASE_INBOX_TYPE);
} }
protected long insertMessageOutbox(long threadId, OutgoingTextMessage message, public long insertMessageOutbox(long threadId, OutgoingTextMessage message,
long type, boolean forceSms, long date, boolean forceSms, long date, InsertListener insertListener)
InsertListener insertListener)
{ {
long type = Types.BASE_SENDING_TYPE;
if (message.isKeyExchange()) type |= Types.KEY_EXCHANGE_BIT; if (message.isKeyExchange()) type |= Types.KEY_EXCHANGE_BIT;
else if (message.isSecureMessage()) type |= (Types.SECURE_MESSAGE_BIT | Types.PUSH_MESSAGE_BIT); else if (message.isSecureMessage()) type |= (Types.SECURE_MESSAGE_BIT | Types.PUSH_MESSAGE_BIT);
else if (message.isEndSession()) type |= Types.END_SESSION_BIT; else if (message.isEndSession()) type |= Types.END_SESSION_BIT;
@ -670,12 +684,21 @@ public class SmsDatabase extends MessagingDatabase {
return db.query(TABLE_NAME, MESSAGE_PROJECTION, where, null, null, null, null); return db.query(TABLE_NAME, MESSAGE_PROJECTION, where, null, null, null, null);
} }
public Cursor getMessage(long messageId) { public SmsMessageRecord getMessage(long messageId) throws NoSuchMessageException {
SQLiteDatabase db = databaseHelper.getReadableDatabase(); SQLiteDatabase db = databaseHelper.getReadableDatabase();
Cursor cursor = db.query(TABLE_NAME, MESSAGE_PROJECTION, ID_WHERE, new String[]{messageId + ""}, Cursor cursor = db.query(TABLE_NAME, MESSAGE_PROJECTION, ID_WHERE, new String[]{messageId + ""}, null, null, null);
null, null, null); Reader reader = new Reader(cursor);
setNotifyConverationListeners(cursor, getThreadIdForMessage(messageId)); SmsMessageRecord record = reader.getNext();
return cursor;
reader.close();
if (record == null) throw new NoSuchMessageException("No message for ID: " + messageId);
else return record;
}
public Cursor getMessageCursor(long messageId) {
SQLiteDatabase db = databaseHelper.getReadableDatabase();
return db.query(TABLE_NAME, MESSAGE_PROJECTION, ID_WHERE, new String[] {messageId + ""}, null, null, null);
} }
public boolean deleteMessage(long messageId) { public boolean deleteMessage(long messageId) {

View File

@ -1,4 +1,4 @@
/** /*
* 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
@ -18,15 +18,14 @@ package org.thoughtcrime.securesms.database;
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.SQLiteException; import android.database.sqlite.SQLiteException;
import android.database.sqlite.SQLiteStatement;
import android.net.Uri; import android.net.Uri;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import android.util.Log; import android.util.Log;
import org.thoughtcrime.securesms.crypto.MasterCipher; import net.sqlcipher.database.SQLiteDatabase;
import org.thoughtcrime.securesms.crypto.MasterSecret; import net.sqlcipher.database.SQLiteStatement;
import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.util.TextSecurePreferences; import org.thoughtcrime.securesms.util.TextSecurePreferences;
@ -40,19 +39,6 @@ public class SmsMigrator {
private static final String TAG = SmsMigrator.class.getSimpleName(); private static final String TAG = SmsMigrator.class.getSimpleName();
private static void addEncryptedStringToStatement(Context context, SQLiteStatement statement,
Cursor cursor, MasterSecret masterSecret,
int index, String key)
{
int columnIndex = cursor.getColumnIndexOrThrow(key);
if (cursor.isNull(columnIndex)) {
statement.bindNull(index);
} else {
statement.bindString(index, encrypt(masterSecret, cursor.getString(columnIndex)));
}
}
private static void addStringToStatement(SQLiteStatement statement, Cursor cursor, private static void addStringToStatement(SQLiteStatement statement, Cursor cursor,
int index, String key) int index, String key)
{ {
@ -77,8 +63,8 @@ public class SmsMigrator {
} }
} }
private static void addTranslatedTypeToStatement(SQLiteStatement statement, Cursor cursor, @SuppressWarnings("SameParameterValue")
int index, String key) private static void addTranslatedTypeToStatement(SQLiteStatement statement, Cursor cursor, int index, String key)
{ {
int columnIndex = cursor.getColumnIndexOrThrow(key); int columnIndex = cursor.getColumnIndexOrThrow(key);
@ -99,9 +85,8 @@ public class SmsMigrator {
ourType == MmsSmsColumns.Types.BASE_SENT_FAILED_TYPE; ourType == MmsSmsColumns.Types.BASE_SENT_FAILED_TYPE;
} }
private static void getContentValuesForRow(Context context, MasterSecret masterSecret, private static void getContentValuesForRow(Context context, Cursor cursor,
Cursor cursor, long threadId, long threadId, SQLiteStatement statement)
SQLiteStatement statement)
{ {
String theirAddress = cursor.getString(cursor.getColumnIndexOrThrow(SmsDatabase.ADDRESS)); String theirAddress = cursor.getString(cursor.getColumnIndexOrThrow(SmsDatabase.ADDRESS));
statement.bindString(1, Address.fromExternal(context, theirAddress).serialize()); statement.bindString(1, Address.fromExternal(context, theirAddress).serialize());
@ -115,7 +100,7 @@ public class SmsMigrator {
addTranslatedTypeToStatement(statement, cursor, 8, SmsDatabase.TYPE); addTranslatedTypeToStatement(statement, cursor, 8, SmsDatabase.TYPE);
addIntToStatement(statement, cursor, 9, SmsDatabase.REPLY_PATH_PRESENT); addIntToStatement(statement, cursor, 9, SmsDatabase.REPLY_PATH_PRESENT);
addStringToStatement(statement, cursor, 10, SmsDatabase.SUBJECT); addStringToStatement(statement, cursor, 10, SmsDatabase.SUBJECT);
addEncryptedStringToStatement(context, statement, cursor, masterSecret, 11, SmsDatabase.BODY); addStringToStatement(statement, cursor, 11, SmsDatabase.BODY);
addStringToStatement(statement, cursor, 12, SmsDatabase.SERVICE_CENTER); addStringToStatement(statement, cursor, 12, SmsDatabase.SERVICE_CENTER);
statement.bindLong(13, threadId); statement.bindLong(13, threadId);
@ -159,14 +144,7 @@ public class SmsMigrator {
else return recipientList; else return recipientList;
} }
private static String encrypt(MasterSecret masterSecret, String body) private static void migrateConversation(Context context, SmsMigrationProgressListener listener,
{
MasterCipher masterCipher = new MasterCipher(masterSecret);
return masterCipher.encryptBody(body);
}
private static void migrateConversation(Context context, MasterSecret masterSecret,
SmsMigrationProgressListener listener,
ProgressDescription progress, ProgressDescription progress,
long theirThreadId, long ourThreadId) long theirThreadId, long ourThreadId)
{ {
@ -191,7 +169,7 @@ public class SmsMigrator {
int typeColumn = cursor.getColumnIndex(SmsDatabase.TYPE); int typeColumn = cursor.getColumnIndex(SmsDatabase.TYPE);
if (cursor.isNull(typeColumn) || isAppropriateTypeForMigration(cursor, typeColumn)) { if (cursor.isNull(typeColumn) || isAppropriateTypeForMigration(cursor, typeColumn)) {
getContentValuesForRow(context, masterSecret, cursor, ourThreadId, statement); getContentValuesForRow(context, cursor, ourThreadId, statement);
statement.execute(); statement.execute();
} }
@ -208,9 +186,7 @@ public class SmsMigrator {
} }
} }
public static void migrateDatabase(Context context, public static void migrateDatabase(Context context, SmsMigrationProgressListener listener)
MasterSecret masterSecret,
SmsMigrationProgressListener listener)
{ {
// if (context.getSharedPreferences("SecureSMS", Context.MODE_PRIVATE).getBoolean("migrated", false)) // if (context.getSharedPreferences("SecureSMS", Context.MODE_PRIVATE).getBoolean("migrated", false))
// return; // return;
@ -231,7 +207,7 @@ public class SmsMigrator {
if (ourRecipients != null) { if (ourRecipients != null) {
if (ourRecipients.size() == 1) { if (ourRecipients.size() == 1) {
long ourThreadId = threadDatabase.getThreadIdFor(ourRecipients.iterator().next()); long ourThreadId = threadDatabase.getThreadIdFor(ourRecipients.iterator().next());
migrateConversation(context, masterSecret, listener, progress, theirThreadId, ourThreadId); migrateConversation(context, listener, progress, theirThreadId, ourThreadId);
} else if (ourRecipients.size() > 1) { } else if (ourRecipients.size() > 1) {
ourRecipients.add(Recipient.from(context, Address.fromSerialized(TextSecurePreferences.getLocalNumber(context)), true)); ourRecipients.add(Recipient.from(context, Address.fromSerialized(TextSecurePreferences.getLocalNumber(context)), true));
@ -245,7 +221,7 @@ public class SmsMigrator {
Recipient ourGroupRecipient = Recipient.from(context, Address.fromSerialized(ourGroupId), true); Recipient ourGroupRecipient = Recipient.from(context, Address.fromSerialized(ourGroupId), true);
long ourThreadId = threadDatabase.getThreadIdFor(ourGroupRecipient, ThreadDatabase.DistributionTypes.CONVERSATION); long ourThreadId = threadDatabase.getThreadIdFor(ourGroupRecipient, ThreadDatabase.DistributionTypes.CONVERSATION);
migrateConversation(context, masterSecret, listener, progress, theirThreadId, ourThreadId); migrateConversation(context, listener, progress, theirThreadId, ourThreadId);
} }
} }
@ -262,7 +238,7 @@ public class SmsMigrator {
} }
public interface SmsMigrationProgressListener { public interface SmsMigrationProgressListener {
public void progressUpdate(ProgressDescription description); void progressUpdate(ProgressDescription description);
} }
public static class ProgressDescription { public static class ProgressDescription {
@ -271,7 +247,7 @@ public class SmsMigrator {
public final int secondaryTotal; public final int secondaryTotal;
public final int secondaryComplete; public final int secondaryComplete;
public ProgressDescription(int primaryTotal, int primaryComplete, ProgressDescription(int primaryTotal, int primaryComplete,
int secondaryTotal, int secondaryComplete) int secondaryTotal, int secondaryComplete)
{ {
this.primaryTotal = primaryTotal; this.primaryTotal = primaryTotal;
@ -280,14 +256,14 @@ public class SmsMigrator {
this.secondaryComplete = secondaryComplete; this.secondaryComplete = secondaryComplete;
} }
public ProgressDescription(ProgressDescription that, int secondaryTotal, int secondaryComplete) { ProgressDescription(ProgressDescription that, int secondaryTotal, int secondaryComplete) {
this.primaryComplete = that.primaryComplete; this.primaryComplete = that.primaryComplete;
this.primaryTotal = that.primaryTotal; this.primaryTotal = that.primaryTotal;
this.secondaryComplete = secondaryComplete; this.secondaryComplete = secondaryComplete;
this.secondaryTotal = secondaryTotal; this.secondaryTotal = secondaryTotal;
} }
public void incrementPrimaryComplete() { void incrementPrimaryComplete() {
primaryComplete += 1; primaryComplete += 1;
} }
} }

View File

@ -21,21 +21,19 @@ import android.content.ContentValues;
import android.content.Context; import android.content.Context;
import android.database.Cursor; import android.database.Cursor;
import android.database.MergeCursor; import android.database.MergeCursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.net.Uri; import android.net.Uri;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import android.text.TextUtils;
import android.util.Log; import android.util.Log;
import com.annimon.stream.Stream; import com.annimon.stream.Stream;
import org.thoughtcrime.securesms.R; import net.sqlcipher.database.SQLiteDatabase;
import org.thoughtcrime.securesms.crypto.MasterCipher;
import org.thoughtcrime.securesms.database.GroupDatabase.GroupRecord; import org.thoughtcrime.securesms.database.GroupDatabase.GroupRecord;
import org.thoughtcrime.securesms.database.MessagingDatabase.MarkedMessageInfo; import org.thoughtcrime.securesms.database.MessagingDatabase.MarkedMessageInfo;
import org.thoughtcrime.securesms.database.RecipientDatabase.RecipientSettings; import org.thoughtcrime.securesms.database.RecipientDatabase.RecipientSettings;
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper;
import org.thoughtcrime.securesms.database.model.DisplayRecord; import org.thoughtcrime.securesms.database.model.DisplayRecord;
import org.thoughtcrime.securesms.database.model.MediaMmsMessageRecord; import org.thoughtcrime.securesms.database.model.MediaMmsMessageRecord;
import org.thoughtcrime.securesms.database.model.MessageRecord; import org.thoughtcrime.securesms.database.model.MessageRecord;
@ -46,7 +44,6 @@ import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.util.DelimiterUtil; import org.thoughtcrime.securesms.util.DelimiterUtil;
import org.thoughtcrime.securesms.util.TextSecurePreferences; import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.Util; import org.thoughtcrime.securesms.util.Util;
import org.whispersystems.libsignal.InvalidMessageException;
import org.whispersystems.libsignal.util.Pair; import org.whispersystems.libsignal.util.Pair;
import org.whispersystems.libsignal.util.guava.Optional; import org.whispersystems.libsignal.util.guava.Optional;
@ -90,7 +87,7 @@ public class ThreadDatabase extends Database {
LAST_SEEN + " INTEGER DEFAULT 0, " + HAS_SENT + " INTEGER DEFAULT 0, " + LAST_SEEN + " INTEGER DEFAULT 0, " + HAS_SENT + " INTEGER DEFAULT 0, " +
READ_RECEIPT_COUNT + " INTEGER DEFAULT 0, " + UNREAD_COUNT + " INTEGER DEFAULT 0);"; READ_RECEIPT_COUNT + " INTEGER DEFAULT 0, " + UNREAD_COUNT + " INTEGER DEFAULT 0);";
static final String[] CREATE_INDEXS = { public static final String[] CREATE_INDEXS = {
"CREATE INDEX IF NOT EXISTS thread_recipient_ids_index ON " + TABLE_NAME + " (" + ADDRESS + ");", "CREATE INDEX IF NOT EXISTS thread_recipient_ids_index ON " + TABLE_NAME + " (" + ADDRESS + ");",
"CREATE INDEX IF NOT EXISTS archived_count_index ON " + TABLE_NAME + " (" + ARCHIVED + ", " + MESSAGE_COUNT + ");", "CREATE INDEX IF NOT EXISTS archived_count_index ON " + TABLE_NAME + " (" + ARCHIVED + ", " + MESSAGE_COUNT + ");",
}; };
@ -109,7 +106,7 @@ public class ThreadDatabase extends Database {
Stream.of(GroupDatabase.TYPED_GROUP_PROJECTION)) Stream.of(GroupDatabase.TYPED_GROUP_PROJECTION))
.toList(); .toList();
public ThreadDatabase(Context context, SQLiteOpenHelper databaseHelper) { public ThreadDatabase(Context context, SQLCipherOpenHelper databaseHelper) {
super(context, databaseHelper); super(context, databaseHelper);
} }
@ -617,8 +614,8 @@ public class ThreadDatabase extends Database {
void onProgress(int complete, int total); void onProgress(int complete, int total);
} }
public Reader readerFor(Cursor cursor, MasterCipher masterCipher) { public Reader readerFor(Cursor cursor) {
return new Reader(cursor, masterCipher); return new Reader(cursor);
} }
public static class DistributionTypes { public static class DistributionTypes {
@ -632,11 +629,9 @@ public class ThreadDatabase extends Database {
public class Reader { public class Reader {
private final Cursor cursor; private final Cursor cursor;
private final MasterCipher masterCipher;
public Reader(Cursor cursor, MasterCipher masterCipher) { public Reader(Cursor cursor) {
this.cursor = cursor; this.cursor = cursor;
this.masterCipher = masterCipher;
} }
public ThreadRecord getNext() { public ThreadRecord getNext() {
@ -686,21 +681,7 @@ public class ThreadDatabase extends Database {
} }
private DisplayRecord.Body getPlaintextBody(Cursor cursor) { private DisplayRecord.Body getPlaintextBody(Cursor cursor) {
try { return new DisplayRecord.Body(cursor.getString(cursor.getColumnIndexOrThrow(SNIPPET)), true);
long type = cursor.getLong(cursor.getColumnIndexOrThrow(ThreadDatabase.SNIPPET_TYPE));
String body = cursor.getString(cursor.getColumnIndexOrThrow(SNIPPET));
if (!TextUtils.isEmpty(body) && masterCipher != null && MmsSmsColumns.Types.isSymmetricEncryption(type)) {
return new DisplayRecord.Body(masterCipher.decryptBody(body), true);
} else if (!TextUtils.isEmpty(body) && masterCipher == null && MmsSmsColumns.Types.isSymmetricEncryption(type)) {
return new DisplayRecord.Body(body, false);
} else {
return new DisplayRecord.Body(body, true);
}
} catch (InvalidMessageException e) {
Log.w("ThreadDatabase", e);
return new DisplayRecord.Body(context.getString(R.string.ThreadDatabase_error_decrypting_message), true);
}
} }
private @Nullable Uri getSnippetUri(Cursor cursor) { private @Nullable Uri getSnippetUri(Cursor cursor) {

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,197 @@
package org.thoughtcrime.securesms.database.helpers;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.text.TextUtils;
import android.util.Log;
import android.util.Pair;
import com.annimon.stream.function.Function;
import org.thoughtcrime.securesms.crypto.AsymmetricMasterCipher;
import org.thoughtcrime.securesms.crypto.AttachmentSecretProvider;
import org.thoughtcrime.securesms.crypto.MasterCipher;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.crypto.MasterSecretUtil;
import org.thoughtcrime.securesms.util.Base64;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.whispersystems.libsignal.InvalidMessageException;
import java.io.IOException;
public class SQLCipherMigrationHelper {
private static final String TAG = SQLCipherMigrationHelper.class.getSimpleName();
private static final long ENCRYPTION_SYMMETRIC_BIT = 0x80000000;
private static final long ENCRYPTION_ASYMMETRIC_BIT = 0x40000000;
static void migratePlaintext(@NonNull android.database.sqlite.SQLiteDatabase legacyDb,
@NonNull net.sqlcipher.database.SQLiteDatabase modernDb)
{
modernDb.beginTransaction();
try {
copyTable("identities", legacyDb, modernDb, null);
copyTable("push", legacyDb, modernDb, null);
copyTable("groups", legacyDb, modernDb, null);
copyTable("recipient_preferences", legacyDb, modernDb, null);
copyTable("group_receipts", legacyDb, modernDb, null);
modernDb.setTransactionSuccessful();
} finally {
modernDb.endTransaction();
}
}
public static void migrateCiphertext(@NonNull Context context,
@NonNull MasterSecret masterSecret,
@NonNull android.database.sqlite.SQLiteDatabase legacyDb,
@NonNull net.sqlcipher.database.SQLiteDatabase modernDb)
{
MasterCipher legacyCipher = new MasterCipher(masterSecret);
AsymmetricMasterCipher legacyAsymmetricCipher = new AsymmetricMasterCipher(MasterSecretUtil.getAsymmetricMasterSecret(context, masterSecret));
modernDb.beginTransaction();
try {
copyTable("sms", legacyDb, modernDb, (row) -> {
Pair<Long, String> plaintext = getPlaintextBody(legacyCipher, legacyAsymmetricCipher,
row.getAsLong("type"),
row.getAsString("body"));
row.put("body", plaintext.second);
row.put("type", plaintext.first);
return row;
});
copyTable("mms", legacyDb, modernDb, (row) -> {
Pair<Long, String> plaintext = getPlaintextBody(legacyCipher, legacyAsymmetricCipher,
row.getAsLong("msg_box"),
row.getAsString("body"));
row.put("body", plaintext.second);
row.put("msg_box", plaintext.first);
return row;
});
copyTable("part", legacyDb, modernDb, (row) -> {
String fileName = row.getAsString("file_name");
String mediaKey = row.getAsString("cd");
try {
if (!TextUtils.isEmpty(fileName)) {
row.put("file_name", legacyCipher.decryptBody(fileName));
}
} catch (InvalidMessageException e) {
Log.w(TAG, e);
}
try {
if (!TextUtils.isEmpty(mediaKey)) {
byte[] plaintext;
if (mediaKey.startsWith("?ASYNC-")) {
plaintext = legacyAsymmetricCipher.decryptBytes(Base64.decode(mediaKey.substring("?ASYNC-".length())));
} else {
plaintext = legacyCipher.decryptBytes(Base64.decode(mediaKey));
}
row.put("cd", Base64.encodeBytes(plaintext));
}
} catch (IOException | InvalidMessageException e) {
Log.w(TAG, e);
}
return row;
});
copyTable("thread", legacyDb, modernDb, (row) -> {
Pair<Long, String> plaintext = getPlaintextBody(legacyCipher, legacyAsymmetricCipher,
row.getAsLong("snippet_type"),
row.getAsString("snippet"));
row.put("snippet", plaintext.second);
row.put("snippet_type", plaintext.first);
return row;
});
copyTable("drafts", legacyDb, modernDb, (row) -> {
String draftType = row.getAsString("type");
String draft = row.getAsString("value");
try {
if (!TextUtils.isEmpty(draftType)) row.put("type", legacyCipher.decryptBody(draftType));
if (!TextUtils.isEmpty(draft)) row.put("value", legacyCipher.decryptBody(draft));
} catch (InvalidMessageException e) {
Log.w(TAG, e);
}
return row;
});
TextSecurePreferences.setNeedsSqlCipherMigration(context, false);
modernDb.setTransactionSuccessful();
} finally {
modernDb.endTransaction();
}
AttachmentSecretProvider.getInstance(context).setClassicKey(context, masterSecret.getEncryptionKey().getEncoded(), masterSecret.getMacKey().getEncoded());
}
private static void copyTable(@NonNull String tableName,
@NonNull android.database.sqlite.SQLiteDatabase legacyDb,
@NonNull net.sqlcipher.database.SQLiteDatabase modernDb,
@Nullable Function<ContentValues, ContentValues> transformer)
{
try (Cursor cursor = legacyDb.query(tableName, null, null, null, null, null, null)) {
while (cursor != null && cursor.moveToNext()) {
ContentValues row = new ContentValues();
for (int i=0;i<cursor.getColumnCount();i++) {
String columnName = cursor.getColumnName(i);
switch (cursor.getType(i)) {
case Cursor.FIELD_TYPE_STRING: row.put(columnName, cursor.getString(i)); break;
case Cursor.FIELD_TYPE_FLOAT: row.put(columnName, cursor.getFloat(i)); break;
case Cursor.FIELD_TYPE_INTEGER: row.put(columnName, cursor.getLong(i)); break;
case Cursor.FIELD_TYPE_BLOB: row.put(columnName, cursor.getBlob(i)); break;
}
}
if (transformer != null) {
row = transformer.apply(row);
}
modernDb.insert(tableName, null, row);
}
}
}
private static Pair<Long, String> getPlaintextBody(@NonNull MasterCipher legacyCipher,
@NonNull AsymmetricMasterCipher legacyAsymmetricCipher,
long type,
@Nullable String body)
{
try {
if (!TextUtils.isEmpty(body)) {
if ((type & ENCRYPTION_SYMMETRIC_BIT) != 0) body = legacyCipher.decryptBody(body);
else if ((type & ENCRYPTION_ASYMMETRIC_BIT) != 0) body = legacyAsymmetricCipher.decryptBody(body);
}
} catch (InvalidMessageException | IOException e) {
Log.w(TAG, e);
}
type &= ~(ENCRYPTION_SYMMETRIC_BIT);
type &= ~(ENCRYPTION_ASYMMETRIC_BIT);
return new Pair<>(type, body);
}
}

View File

@ -0,0 +1,109 @@
package org.thoughtcrime.securesms.database.helpers;
import android.content.Context;
import android.support.annotation.NonNull;
import android.util.Log;
import net.sqlcipher.database.SQLiteDatabase;
import net.sqlcipher.database.SQLiteDatabaseHook;
import net.sqlcipher.database.SQLiteOpenHelper;
import org.thoughtcrime.securesms.crypto.DatabaseSecret;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.database.AttachmentDatabase;
import org.thoughtcrime.securesms.database.DraftDatabase;
import org.thoughtcrime.securesms.database.GroupDatabase;
import org.thoughtcrime.securesms.database.GroupReceiptDatabase;
import org.thoughtcrime.securesms.database.IdentityDatabase;
import org.thoughtcrime.securesms.database.MmsDatabase;
import org.thoughtcrime.securesms.database.PushDatabase;
import org.thoughtcrime.securesms.database.RecipientDatabase;
import org.thoughtcrime.securesms.database.SmsDatabase;
import org.thoughtcrime.securesms.database.ThreadDatabase;
import org.thoughtcrime.securesms.service.KeyCachingService;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
public class SQLCipherOpenHelper extends SQLiteOpenHelper {
private static final String TAG = SQLCipherOpenHelper.class.getSimpleName();
private static final int DATABASE_VERSION = 1;
private static final String DATABASE_NAME = "signal.db";
private final Context context;
private final DatabaseSecret databaseSecret;
public SQLCipherOpenHelper(@NonNull Context context, @NonNull DatabaseSecret databaseSecret) {
super(context, DATABASE_NAME, null, DATABASE_VERSION, new SQLiteDatabaseHook() {
@Override
public void preKey(SQLiteDatabase db) {
db.rawExecSQL("PRAGMA cipher_default_kdf_iter = 1;");
db.rawExecSQL("PRAGMA cipher_default_page_size = 4096;");
}
@Override
public void postKey(SQLiteDatabase db) {
db.rawExecSQL("PRAGMA kdf_iter = '1';");
db.rawExecSQL("PRAGMA cipher_page_size = 4096;");
}
});
this.context = context.getApplicationContext();
this.databaseSecret = databaseSecret;
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(SmsDatabase.CREATE_TABLE);
db.execSQL(MmsDatabase.CREATE_TABLE);
db.execSQL(AttachmentDatabase.CREATE_TABLE);
db.execSQL(ThreadDatabase.CREATE_TABLE);
db.execSQL(IdentityDatabase.CREATE_TABLE);
db.execSQL(DraftDatabase.CREATE_TABLE);
db.execSQL(PushDatabase.CREATE_TABLE);
db.execSQL(GroupDatabase.CREATE_TABLE);
db.execSQL(RecipientDatabase.CREATE_TABLE);
db.execSQL(GroupReceiptDatabase.CREATE_TABLE);
executeStatements(db, SmsDatabase.CREATE_INDEXS);
executeStatements(db, MmsDatabase.CREATE_INDEXS);
executeStatements(db, AttachmentDatabase.CREATE_INDEXS);
executeStatements(db, ThreadDatabase.CREATE_INDEXS);
executeStatements(db, DraftDatabase.CREATE_INDEXS);
executeStatements(db, GroupDatabase.CREATE_INDEXS);
executeStatements(db, GroupReceiptDatabase.CREATE_INDEXES);
if (context.getDatabasePath(ClassicOpenHelper.NAME).exists()) {
ClassicOpenHelper legacyHelper = new ClassicOpenHelper(context);
android.database.sqlite.SQLiteDatabase legacyDb = legacyHelper.getWritableDatabase();
SQLCipherMigrationHelper.migratePlaintext(legacyDb, db);
MasterSecret masterSecret = KeyCachingService.getMasterSecret(context);
if (masterSecret != null) SQLCipherMigrationHelper.migrateCiphertext(context, masterSecret, legacyDb, db);
else TextSecurePreferences.setNeedsSqlCipherMigration(context, true);
}
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
public SQLiteDatabase getReadableDatabase() {
return getReadableDatabase(databaseSecret.asString());
}
public SQLiteDatabase getWritableDatabase() {
return getWritableDatabase(databaseSecret.asString());
}
private void executeStatements(SQLiteDatabase db, String[] statements) {
for (String statement : statements)
db.execSQL(statement);
}
}

View File

@ -9,7 +9,6 @@ import android.support.v4.content.AsyncTaskLoader;
import com.annimon.stream.Stream; import com.annimon.stream.Stream;
import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.database.Address; import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.MediaDatabase; import org.thoughtcrime.securesms.database.MediaDatabase;
@ -28,14 +27,13 @@ import java.util.Map;
public class BucketedThreadMediaLoader extends AsyncTaskLoader<BucketedThreadMediaLoader.BucketedThreadMedia> { public class BucketedThreadMediaLoader extends AsyncTaskLoader<BucketedThreadMediaLoader.BucketedThreadMedia> {
@SuppressWarnings("unused")
private static final String TAG = BucketedThreadMediaLoader.class.getSimpleName(); private static final String TAG = BucketedThreadMediaLoader.class.getSimpleName();
private final MasterSecret masterSecret;
private final Address address; private final Address address;
public BucketedThreadMediaLoader(@NonNull Context context, @NonNull MasterSecret masterSecret, @NonNull Address address) { public BucketedThreadMediaLoader(@NonNull Context context, @NonNull Address address) {
super(context); super(context);
this.masterSecret = masterSecret;
this.address = address; this.address = address;
onContentChanged(); onContentChanged();
@ -60,7 +58,7 @@ public class BucketedThreadMediaLoader extends AsyncTaskLoader<BucketedThreadMed
try (Cursor cursor = DatabaseFactory.getMediaDatabase(getContext()).getGalleryMediaForThread(threadId)) { try (Cursor cursor = DatabaseFactory.getMediaDatabase(getContext()).getGalleryMediaForThread(threadId)) {
while (cursor != null && cursor.moveToNext()) { while (cursor != null && cursor.moveToNext()) {
result.add(MediaDatabase.MediaRecord.from(getContext(), masterSecret, cursor)); result.add(MediaDatabase.MediaRecord.from(getContext(), cursor));
} }
} }

View File

@ -37,7 +37,7 @@ public class MessageDetailsLoader extends AbstractCursorLoader {
public Cursor getCursor() { public Cursor getCursor() {
switch (type) { switch (type) {
case MmsSmsDatabase.SMS_TRANSPORT: case MmsSmsDatabase.SMS_TRANSPORT:
return DatabaseFactory.getEncryptingSmsDatabase(context).getMessage(messageId); return DatabaseFactory.getSmsDatabase(context).getMessageCursor(messageId);
case MmsSmsDatabase.MMS_TRANSPORT: case MmsSmsDatabase.MMS_TRANSPORT:
return DatabaseFactory.getMmsDatabase(context).getMessage(messageId); return DatabaseFactory.getMmsDatabase(context).getMessage(messageId);
default: default:

View File

@ -10,7 +10,6 @@ import com.google.protobuf.ByteString;
import org.thoughtcrime.securesms.attachments.Attachment; import org.thoughtcrime.securesms.attachments.Attachment;
import org.thoughtcrime.securesms.attachments.UriAttachment; import org.thoughtcrime.securesms.attachments.UriAttachment;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.database.Address; import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.AttachmentDatabase; import org.thoughtcrime.securesms.database.AttachmentDatabase;
import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.DatabaseFactory;
@ -37,7 +36,6 @@ import java.util.Set;
public class GroupManager { public class GroupManager {
public static @NonNull GroupActionResult createGroup(@NonNull Context context, public static @NonNull GroupActionResult createGroup(@NonNull Context context,
@NonNull MasterSecret masterSecret,
@NonNull Set<Recipient> members, @NonNull Set<Recipient> members,
@Nullable Bitmap avatar, @Nullable Bitmap avatar,
@Nullable String name, @Nullable String name,
@ -55,7 +53,7 @@ public class GroupManager {
if (!mms) { if (!mms) {
groupDatabase.updateAvatar(groupId, avatarBytes); groupDatabase.updateAvatar(groupId, avatarBytes);
DatabaseFactory.getRecipientDatabase(context).setProfileSharing(groupRecipient, true); DatabaseFactory.getRecipientDatabase(context).setProfileSharing(groupRecipient, true);
return sendGroupUpdate(context, masterSecret, groupId, memberAddresses, name, avatarBytes); return sendGroupUpdate(context, groupId, memberAddresses, name, avatarBytes);
} else { } else {
long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(groupRecipient, ThreadDatabase.DistributionTypes.CONVERSATION); long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(groupRecipient, ThreadDatabase.DistributionTypes.CONVERSATION);
return new GroupActionResult(groupRecipient, threadId); return new GroupActionResult(groupRecipient, threadId);
@ -63,7 +61,6 @@ public class GroupManager {
} }
public static GroupActionResult updateGroup(@NonNull Context context, public static GroupActionResult updateGroup(@NonNull Context context,
@NonNull MasterSecret masterSecret,
@NonNull String groupId, @NonNull String groupId,
@NonNull Set<Recipient> members, @NonNull Set<Recipient> members,
@Nullable Bitmap avatar, @Nullable Bitmap avatar,
@ -80,7 +77,7 @@ public class GroupManager {
groupDatabase.updateAvatar(groupId, avatarBytes); groupDatabase.updateAvatar(groupId, avatarBytes);
if (!GroupUtil.isMmsGroup(groupId)) { if (!GroupUtil.isMmsGroup(groupId)) {
return sendGroupUpdate(context, masterSecret, groupId, memberAddresses, name, avatarBytes); return sendGroupUpdate(context, groupId, memberAddresses, name, avatarBytes);
} else { } else {
Recipient groupRecipient = Recipient.from(context, Address.fromSerialized(groupId), true); Recipient groupRecipient = Recipient.from(context, Address.fromSerialized(groupId), true);
long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(groupRecipient); long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(groupRecipient);
@ -89,7 +86,6 @@ public class GroupManager {
} }
private static GroupActionResult sendGroupUpdate(@NonNull Context context, private static GroupActionResult sendGroupUpdate(@NonNull Context context,
@NonNull MasterSecret masterSecret,
@NonNull String groupId, @NonNull String groupId,
@NonNull Set<Address> members, @NonNull Set<Address> members,
@Nullable String groupName, @Nullable String groupName,
@ -119,7 +115,7 @@ public class GroupManager {
} }
OutgoingGroupMediaMessage outgoingMessage = new OutgoingGroupMediaMessage(groupRecipient, groupContext, avatarAttachment, System.currentTimeMillis(), 0); OutgoingGroupMediaMessage outgoingMessage = new OutgoingGroupMediaMessage(groupRecipient, groupContext, avatarAttachment, System.currentTimeMillis(), 0);
long threadId = MessageSender.send(context, masterSecret, outgoingMessage, -1, false, null); long threadId = MessageSender.send(context, outgoingMessage, -1, false, null);
return new GroupActionResult(groupRecipient, threadId); return new GroupActionResult(groupRecipient, threadId);
} catch (IOException e) { } catch (IOException e) {

View File

@ -9,13 +9,12 @@ import android.util.Log;
import com.google.protobuf.ByteString; import com.google.protobuf.ByteString;
import org.thoughtcrime.securesms.ApplicationContext; import org.thoughtcrime.securesms.ApplicationContext;
import org.thoughtcrime.securesms.crypto.MasterSecretUnion;
import org.thoughtcrime.securesms.database.Address; import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.EncryptingSmsDatabase;
import org.thoughtcrime.securesms.database.GroupDatabase; import org.thoughtcrime.securesms.database.GroupDatabase;
import org.thoughtcrime.securesms.database.MessagingDatabase.InsertResult; import org.thoughtcrime.securesms.database.MessagingDatabase.InsertResult;
import org.thoughtcrime.securesms.database.MmsDatabase; import org.thoughtcrime.securesms.database.MmsDatabase;
import org.thoughtcrime.securesms.database.SmsDatabase;
import org.thoughtcrime.securesms.jobs.AvatarDownloadJob; import org.thoughtcrime.securesms.jobs.AvatarDownloadJob;
import org.thoughtcrime.securesms.jobs.PushGroupUpdateJob; import org.thoughtcrime.securesms.jobs.PushGroupUpdateJob;
import org.thoughtcrime.securesms.mms.MmsException; import org.thoughtcrime.securesms.mms.MmsException;
@ -47,7 +46,6 @@ public class GroupMessageProcessor {
private static final String TAG = GroupMessageProcessor.class.getSimpleName(); private static final String TAG = GroupMessageProcessor.class.getSimpleName();
public static @Nullable Long process(@NonNull Context context, public static @Nullable Long process(@NonNull Context context,
@NonNull MasterSecretUnion masterSecret,
@NonNull SignalServiceEnvelope envelope, @NonNull SignalServiceEnvelope envelope,
@NonNull SignalServiceDataMessage message, @NonNull SignalServiceDataMessage message,
boolean outgoing) boolean outgoing)
@ -63,11 +61,11 @@ public class GroupMessageProcessor {
Optional<GroupRecord> record = database.getGroup(id); Optional<GroupRecord> record = database.getGroup(id);
if (record.isPresent() && group.getType() == Type.UPDATE) { if (record.isPresent() && group.getType() == Type.UPDATE) {
return handleGroupUpdate(context, masterSecret, envelope, group, record.get(), outgoing); return handleGroupUpdate(context, envelope, group, record.get(), outgoing);
} else if (!record.isPresent() && group.getType() == Type.UPDATE) { } else if (!record.isPresent() && group.getType() == Type.UPDATE) {
return handleGroupCreate(context, masterSecret, envelope, group, outgoing); return handleGroupCreate(context, envelope, group, outgoing);
} else if (record.isPresent() && group.getType() == Type.QUIT) { } else if (record.isPresent() && group.getType() == Type.QUIT) {
return handleGroupLeave(context, masterSecret, envelope, group, record.get(), outgoing); return handleGroupLeave(context, envelope, group, record.get(), outgoing);
} else if (record.isPresent() && group.getType() == Type.REQUEST_INFO) { } else if (record.isPresent() && group.getType() == Type.REQUEST_INFO) {
return handleGroupInfoRequest(context, envelope, group, record.get()); return handleGroupInfoRequest(context, envelope, group, record.get());
} else { } else {
@ -77,7 +75,6 @@ public class GroupMessageProcessor {
} }
private static @Nullable Long handleGroupCreate(@NonNull Context context, private static @Nullable Long handleGroupCreate(@NonNull Context context,
@NonNull MasterSecretUnion masterSecret,
@NonNull SignalServiceEnvelope envelope, @NonNull SignalServiceEnvelope envelope,
@NonNull SignalServiceGroup group, @NonNull SignalServiceGroup group,
boolean outgoing) boolean outgoing)
@ -100,11 +97,10 @@ public class GroupMessageProcessor {
avatar != null && avatar.isPointer() ? avatar.asPointer() : null, avatar != null && avatar.isPointer() ? avatar.asPointer() : null,
envelope.getRelay()); envelope.getRelay());
return storeMessage(context, masterSecret, envelope, group, builder.build(), outgoing); return storeMessage(context, envelope, group, builder.build(), outgoing);
} }
private static @Nullable Long handleGroupUpdate(@NonNull Context context, private static @Nullable Long handleGroupUpdate(@NonNull Context context,
@NonNull MasterSecretUnion masterSecret,
@NonNull SignalServiceEnvelope envelope, @NonNull SignalServiceEnvelope envelope,
@NonNull SignalServiceGroup group, @NonNull SignalServiceGroup group,
@NonNull GroupRecord groupRecord, @NonNull GroupRecord groupRecord,
@ -159,7 +155,7 @@ public class GroupMessageProcessor {
if (!groupRecord.isActive()) database.setActive(id, true); if (!groupRecord.isActive()) database.setActive(id, true);
return storeMessage(context, masterSecret, envelope, group, builder.build(), outgoing); return storeMessage(context, envelope, group, builder.build(), outgoing);
} }
private static Long handleGroupInfoRequest(@NonNull Context context, private static Long handleGroupInfoRequest(@NonNull Context context,
@ -177,7 +173,6 @@ public class GroupMessageProcessor {
} }
private static Long handleGroupLeave(@NonNull Context context, private static Long handleGroupLeave(@NonNull Context context,
@NonNull MasterSecretUnion masterSecret,
@NonNull SignalServiceEnvelope envelope, @NonNull SignalServiceEnvelope envelope,
@NonNull SignalServiceGroup group, @NonNull SignalServiceGroup group,
@NonNull GroupRecord record, @NonNull GroupRecord record,
@ -194,7 +189,7 @@ public class GroupMessageProcessor {
database.remove(id, Address.fromExternal(context, envelope.getSource())); database.remove(id, Address.fromExternal(context, envelope.getSource()));
if (outgoing) database.setActive(id, false); if (outgoing) database.setActive(id, false);
return storeMessage(context, masterSecret, envelope, group, builder.build(), outgoing); return storeMessage(context, envelope, group, builder.build(), outgoing);
} }
return null; return null;
@ -202,7 +197,6 @@ public class GroupMessageProcessor {
private static @Nullable Long storeMessage(@NonNull Context context, private static @Nullable Long storeMessage(@NonNull Context context,
@NonNull MasterSecretUnion masterSecret,
@NonNull SignalServiceEnvelope envelope, @NonNull SignalServiceEnvelope envelope,
@NonNull SignalServiceGroup group, @NonNull SignalServiceGroup group,
@NonNull GroupContext storage, @NonNull GroupContext storage,
@ -220,21 +214,21 @@ public class GroupMessageProcessor {
Recipient recipient = Recipient.from(context, addres, false); Recipient recipient = Recipient.from(context, addres, false);
OutgoingGroupMediaMessage outgoingMessage = new OutgoingGroupMediaMessage(recipient, storage, null, envelope.getTimestamp(), 0); OutgoingGroupMediaMessage outgoingMessage = new OutgoingGroupMediaMessage(recipient, storage, null, envelope.getTimestamp(), 0);
long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipient); long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipient);
long messageId = mmsDatabase.insertMessageOutbox(masterSecret, outgoingMessage, threadId, false, null); long messageId = mmsDatabase.insertMessageOutbox(outgoingMessage, threadId, false, null);
mmsDatabase.markAsSent(messageId, true); mmsDatabase.markAsSent(messageId, true);
return threadId; return threadId;
} else { } else {
EncryptingSmsDatabase smsDatabase = DatabaseFactory.getEncryptingSmsDatabase(context); SmsDatabase smsDatabase = DatabaseFactory.getSmsDatabase(context);
String body = Base64.encodeBytes(storage.toByteArray()); String body = Base64.encodeBytes(storage.toByteArray());
IncomingTextMessage incoming = new IncomingTextMessage(Address.fromExternal(context, envelope.getSource()), envelope.getSourceDevice(), envelope.getTimestamp(), body, Optional.of(group), 0); IncomingTextMessage incoming = new IncomingTextMessage(Address.fromExternal(context, envelope.getSource()), envelope.getSourceDevice(), envelope.getTimestamp(), body, Optional.of(group), 0);
IncomingGroupMessage groupMessage = new IncomingGroupMessage(incoming, storage, body); IncomingGroupMessage groupMessage = new IncomingGroupMessage(incoming, storage, body);
Optional<InsertResult> insertResult = smsDatabase.insertMessageInbox(masterSecret, groupMessage); Optional<InsertResult> insertResult = smsDatabase.insertMessageInbox(groupMessage);
if (insertResult.isPresent()) { if (insertResult.isPresent()) {
MessageNotifier.updateNotification(context, masterSecret.getMasterSecret().orNull(), insertResult.get().getThreadId()); MessageNotifier.updateNotification(context, insertResult.get().getThreadId());
return insertResult.get().getThreadId(); return insertResult.get().getThreadId();
} else { } else {
return null; return null;

View File

@ -8,10 +8,7 @@ import android.util.Log;
import org.greenrobot.eventbus.EventBus; import org.greenrobot.eventbus.EventBus;
import org.thoughtcrime.securesms.attachments.Attachment; import org.thoughtcrime.securesms.attachments.Attachment;
import org.thoughtcrime.securesms.attachments.AttachmentId; import org.thoughtcrime.securesms.attachments.AttachmentId;
import org.thoughtcrime.securesms.crypto.AsymmetricMasterSecret;
import org.thoughtcrime.securesms.crypto.MasterSecret; import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.crypto.MasterSecretUtil;
import org.thoughtcrime.securesms.crypto.MediaKey;
import org.thoughtcrime.securesms.database.AttachmentDatabase; import org.thoughtcrime.securesms.database.AttachmentDatabase;
import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.dependencies.InjectableType; import org.thoughtcrime.securesms.dependencies.InjectableType;
@ -20,6 +17,7 @@ import org.thoughtcrime.securesms.jobs.requirements.MasterSecretRequirement;
import org.thoughtcrime.securesms.mms.MmsException; import org.thoughtcrime.securesms.mms.MmsException;
import org.thoughtcrime.securesms.notifications.MessageNotifier; import org.thoughtcrime.securesms.notifications.MessageNotifier;
import org.thoughtcrime.securesms.util.AttachmentUtil; import org.thoughtcrime.securesms.util.AttachmentUtil;
import org.thoughtcrime.securesms.util.Base64;
import org.thoughtcrime.securesms.util.Hex; import org.thoughtcrime.securesms.util.Hex;
import org.thoughtcrime.securesms.util.Util; import org.thoughtcrime.securesms.util.Util;
import org.whispersystems.jobqueue.JobParameters; import org.whispersystems.jobqueue.JobParameters;
@ -27,7 +25,6 @@ import org.whispersystems.jobqueue.requirements.NetworkRequirement;
import org.whispersystems.libsignal.InvalidMessageException; import org.whispersystems.libsignal.InvalidMessageException;
import org.whispersystems.libsignal.util.guava.Optional; import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.api.SignalServiceMessageReceiver; import org.whispersystems.signalservice.api.SignalServiceMessageReceiver;
import org.whispersystems.signalservice.api.messages.SignalServiceAttachment.ProgressListener;
import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentPointer; import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentPointer;
import org.whispersystems.signalservice.api.push.exceptions.NonSuccessfulResponseCodeException; import org.whispersystems.signalservice.api.push.exceptions.NonSuccessfulResponseCodeException;
import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException; import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException;
@ -72,7 +69,7 @@ public class AttachmentDownloadJob extends MasterSecretJob implements Injectable
public void onRun(MasterSecret masterSecret) throws IOException { public void onRun(MasterSecret masterSecret) throws IOException {
final AttachmentDatabase database = DatabaseFactory.getAttachmentDatabase(context); final AttachmentDatabase database = DatabaseFactory.getAttachmentDatabase(context);
final AttachmentId attachmentId = new AttachmentId(partRowId, partUniqueId); final AttachmentId attachmentId = new AttachmentId(partRowId, partUniqueId);
final Attachment attachment = database.getAttachment(masterSecret, attachmentId); final Attachment attachment = database.getAttachment(attachmentId);
if (attachment == null) { if (attachment == null) {
Log.w(TAG, "attachment no longer exists."); Log.w(TAG, "attachment no longer exists.");
@ -92,8 +89,8 @@ public class AttachmentDownloadJob extends MasterSecretJob implements Injectable
Log.w(TAG, "Downloading push part " + attachmentId); Log.w(TAG, "Downloading push part " + attachmentId);
database.setTransferState(messageId, attachmentId, AttachmentDatabase.TRANSFER_PROGRESS_STARTED); database.setTransferState(messageId, attachmentId, AttachmentDatabase.TRANSFER_PROGRESS_STARTED);
retrieveAttachment(masterSecret, messageId, attachmentId, attachment); retrieveAttachment(messageId, attachmentId, attachment);
MessageNotifier.updateNotification(context, masterSecret); MessageNotifier.updateNotification(context);
} }
@Override @Override
@ -107,8 +104,7 @@ public class AttachmentDownloadJob extends MasterSecretJob implements Injectable
return (exception instanceof PushNetworkException); return (exception instanceof PushNetworkException);
} }
private void retrieveAttachment(MasterSecret masterSecret, private void retrieveAttachment(long messageId,
long messageId,
final AttachmentId attachmentId, final AttachmentId attachmentId,
final Attachment attachment) final Attachment attachment)
throws IOException throws IOException
@ -120,26 +116,23 @@ public class AttachmentDownloadJob extends MasterSecretJob implements Injectable
try { try {
attachmentFile = createTempFile(); attachmentFile = createTempFile();
SignalServiceAttachmentPointer pointer = createAttachmentPointer(masterSecret, attachment); SignalServiceAttachmentPointer pointer = createAttachmentPointer(attachment);
InputStream stream = messageReceiver.retrieveAttachment(pointer, attachmentFile, MAX_ATTACHMENT_SIZE, new ProgressListener() { InputStream stream = messageReceiver.retrieveAttachment(pointer, attachmentFile, MAX_ATTACHMENT_SIZE, (total, progress) -> EventBus.getDefault().postSticky(new PartProgressEvent(attachment, total, progress)));
@Override
public void onAttachmentProgress(long total, long progress) {
EventBus.getDefault().postSticky(new PartProgressEvent(attachment, total, progress));
}
});
database.insertAttachmentsForPlaceholder(masterSecret, messageId, attachmentId, stream); database.insertAttachmentsForPlaceholder(messageId, attachmentId, stream);
} catch (InvalidPartException | NonSuccessfulResponseCodeException | InvalidMessageException | MmsException e) { } catch (InvalidPartException | NonSuccessfulResponseCodeException | InvalidMessageException | MmsException e) {
Log.w(TAG, e); Log.w(TAG, e);
markFailed(messageId, attachmentId); markFailed(messageId, attachmentId);
} finally { } finally {
if (attachmentFile != null) if (attachmentFile != null) {
//noinspection ResultOfMethodCallIgnored
attachmentFile.delete(); attachmentFile.delete();
} }
} }
}
@VisibleForTesting @VisibleForTesting
SignalServiceAttachmentPointer createAttachmentPointer(MasterSecret masterSecret, Attachment attachment) SignalServiceAttachmentPointer createAttachmentPointer(Attachment attachment)
throws InvalidPartException throws InvalidPartException
{ {
if (TextUtils.isEmpty(attachment.getLocation())) { if (TextUtils.isEmpty(attachment.getLocation())) {
@ -151,9 +144,8 @@ public class AttachmentDownloadJob extends MasterSecretJob implements Injectable
} }
try { try {
AsymmetricMasterSecret asymmetricMasterSecret = MasterSecretUtil.getAsymmetricMasterSecret(context, masterSecret);
long id = Long.parseLong(attachment.getLocation()); long id = Long.parseLong(attachment.getLocation());
byte[] key = MediaKey.getDecrypted(masterSecret, asymmetricMasterSecret, attachment.getKey()); byte[] key = Base64.decode(attachment.getKey());
String relay = null; String relay = null;
if (TextUtils.isEmpty(attachment.getRelay())) { if (TextUtils.isEmpty(attachment.getRelay())) {
@ -173,7 +165,7 @@ public class AttachmentDownloadJob extends MasterSecretJob implements Injectable
Optional.fromNullable(attachment.getDigest()), Optional.fromNullable(attachment.getDigest()),
Optional.fromNullable(attachment.getFileName()), Optional.fromNullable(attachment.getFileName()),
attachment.isVoiceNote()); attachment.isVoiceNote());
} catch (InvalidMessageException | IOException | ArithmeticException e) { } catch (IOException | ArithmeticException e) {
Log.w(TAG, e); Log.w(TAG, e);
throw new InvalidPartException(e); throw new InvalidPartException(e);
} }
@ -200,8 +192,8 @@ public class AttachmentDownloadJob extends MasterSecretJob implements Injectable
} }
@VisibleForTesting static class InvalidPartException extends Exception { @VisibleForTesting static class InvalidPartException extends Exception {
public InvalidPartException(String s) {super(s);} InvalidPartException(String s) {super(s);}
public InvalidPartException(Exception e) {super(e);} InvalidPartException(Exception e) {super(e);}
} }
} }

View File

@ -48,7 +48,7 @@ public class AttachmentFileNameJob extends MasterSecretJob {
AttachmentId attachmentId = new AttachmentId(attachmentRowId, attachmentUniqueId); AttachmentId attachmentId = new AttachmentId(attachmentRowId, attachmentUniqueId);
String plaintextFileName = new AsymmetricMasterCipher(MasterSecretUtil.getAsymmetricMasterSecret(context, masterSecret)).decryptBody(encryptedFileName); String plaintextFileName = new AsymmetricMasterCipher(MasterSecretUtil.getAsymmetricMasterSecret(context, masterSecret)).decryptBody(encryptedFileName);
DatabaseFactory.getAttachmentDatabase(context).updateAttachmentFileName(masterSecret, attachmentId, plaintextFileName); DatabaseFactory.getAttachmentDatabase(context).updateAttachmentFileName(attachmentId, plaintextFileName);
} }
@Override @Override

View File

@ -6,9 +6,7 @@ import android.support.annotation.NonNull;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import android.util.Log; import android.util.Log;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.service.KeyCachingService;
import org.thoughtcrime.securesms.util.DirectoryHelper; import org.thoughtcrime.securesms.util.DirectoryHelper;
import org.whispersystems.jobqueue.JobParameters; import org.whispersystems.jobqueue.JobParameters;
import org.whispersystems.jobqueue.requirements.NetworkRequirement; import org.whispersystems.jobqueue.requirements.NetworkRequirement;
@ -19,15 +17,13 @@ import java.io.IOException;
public class DirectoryRefreshJob extends ContextJob { public class DirectoryRefreshJob extends ContextJob {
@Nullable private transient Recipient recipient; @Nullable private transient Recipient recipient;
@Nullable private transient MasterSecret masterSecret;
private transient boolean notifyOfNewUsers; private transient boolean notifyOfNewUsers;
public DirectoryRefreshJob(@NonNull Context context, boolean notifyOfNewUsers) { public DirectoryRefreshJob(@NonNull Context context, boolean notifyOfNewUsers) {
this(context, null, null, notifyOfNewUsers); this(context, null, notifyOfNewUsers);
} }
public DirectoryRefreshJob(@NonNull Context context, public DirectoryRefreshJob(@NonNull Context context,
@Nullable MasterSecret masterSecret,
@Nullable Recipient recipient, @Nullable Recipient recipient,
boolean notifyOfNewUsers) boolean notifyOfNewUsers)
{ {
@ -37,7 +33,6 @@ public class DirectoryRefreshJob extends ContextJob {
.create()); .create());
this.recipient = recipient; this.recipient = recipient;
this.masterSecret = masterSecret;
this.notifyOfNewUsers = notifyOfNewUsers; this.notifyOfNewUsers = notifyOfNewUsers;
} }
@ -53,9 +48,9 @@ public class DirectoryRefreshJob extends ContextJob {
try { try {
wakeLock.acquire(); wakeLock.acquire();
if (recipient == null) { if (recipient == null) {
DirectoryHelper.refreshDirectory(context, KeyCachingService.getMasterSecret(context), notifyOfNewUsers); DirectoryHelper.refreshDirectory(context, notifyOfNewUsers);
} else { } else {
DirectoryHelper.refreshDirectoryFor(context, masterSecret, recipient); DirectoryHelper.refreshDirectoryFor(context, recipient);
} }
} finally { } finally {
if (wakeLock.isHeld()) wakeLock.release(); if (wakeLock.isHeld()) wakeLock.release();

View File

@ -1,103 +0,0 @@
package org.thoughtcrime.securesms.jobs;
import android.content.Context;
import android.text.TextUtils;
import android.util.Log;
import org.thoughtcrime.securesms.crypto.AsymmetricMasterCipher;
import org.thoughtcrime.securesms.crypto.AsymmetricMasterSecret;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.crypto.MasterSecretUnion;
import org.thoughtcrime.securesms.crypto.MasterSecretUtil;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.EncryptingSmsDatabase;
import org.thoughtcrime.securesms.database.MmsDatabase;
import org.thoughtcrime.securesms.database.SmsDatabase;
import org.thoughtcrime.securesms.database.model.MessageRecord;
import org.thoughtcrime.securesms.database.model.SmsMessageRecord;
import org.thoughtcrime.securesms.jobs.requirements.MasterSecretRequirement;
import org.thoughtcrime.securesms.notifications.MessageNotifier;
import org.whispersystems.jobqueue.JobParameters;
import org.whispersystems.libsignal.InvalidMessageException;
import java.io.IOException;
public class MasterSecretDecryptJob extends MasterSecretJob {
private static final long serialVersionUID = 1L;
private static final String TAG = MasterSecretDecryptJob.class.getSimpleName();
public MasterSecretDecryptJob(Context context) {
super(context, JobParameters.newBuilder()
.withRequirement(new MasterSecretRequirement(context))
.create());
}
@Override
public void onRun(MasterSecret masterSecret) {
EncryptingSmsDatabase smsDatabase = DatabaseFactory.getEncryptingSmsDatabase(context);
SmsDatabase.Reader smsReader = smsDatabase.getDecryptInProgressMessages(masterSecret);
SmsMessageRecord smsRecord;
while ((smsRecord = smsReader.getNext()) != null) {
try {
String body = getAsymmetricDecryptedBody(masterSecret, smsRecord.getBody().getBody());
smsDatabase.updateMessageBody(new MasterSecretUnion(masterSecret), smsRecord.getId(), body);
} catch (InvalidMessageException e) {
Log.w(TAG, e);
}
}
MmsDatabase mmsDatabase = DatabaseFactory.getMmsDatabase(context);
MmsDatabase.Reader mmsReader = mmsDatabase.getDecryptInProgressMessages(masterSecret);
MessageRecord mmsRecord;
while ((mmsRecord = mmsReader.getNext()) != null) {
try {
String body = getAsymmetricDecryptedBody(masterSecret, mmsRecord.getBody().getBody());
mmsDatabase.updateMessageBody(new MasterSecretUnion(masterSecret), mmsRecord.getId(), body);
} catch (InvalidMessageException e) {
Log.w(TAG, e);
}
}
smsReader.close();
mmsReader.close();
MessageNotifier.updateNotification(context, masterSecret);
}
@Override
public boolean onShouldRetryThrowable(Exception exception) {
return false;
}
@Override
public void onAdded() {
}
@Override
public void onCanceled() {
}
private String getAsymmetricDecryptedBody(MasterSecret masterSecret, String body)
throws InvalidMessageException
{
try {
AsymmetricMasterSecret asymmetricMasterSecret = MasterSecretUtil.getAsymmetricMasterSecret(context, masterSecret);
AsymmetricMasterCipher asymmetricMasterCipher = new AsymmetricMasterCipher(asymmetricMasterSecret);
if (TextUtils.isEmpty(body)) return "";
else return asymmetricMasterCipher.decryptBody(body);
} catch (IOException e) {
throw new InvalidMessageException(e);
}
}
}

View File

@ -14,7 +14,6 @@ import com.google.android.mms.pdu_alt.RetrieveConf;
import org.thoughtcrime.securesms.attachments.Attachment; import org.thoughtcrime.securesms.attachments.Attachment;
import org.thoughtcrime.securesms.attachments.UriAttachment; import org.thoughtcrime.securesms.attachments.UriAttachment;
import org.thoughtcrime.securesms.crypto.MasterSecret; import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.crypto.MasterSecretUnion;
import org.thoughtcrime.securesms.database.Address; import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.AttachmentDatabase; import org.thoughtcrime.securesms.database.AttachmentDatabase;
import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.DatabaseFactory;
@ -50,6 +49,8 @@ import java.util.concurrent.TimeUnit;
public class MmsDownloadJob extends MasterSecretJob { public class MmsDownloadJob extends MasterSecretJob {
private static final long serialVersionUID = 1L;
private static final String TAG = MmsDownloadJob.class.getSimpleName(); private static final String TAG = MmsDownloadJob.class.getSimpleName();
private final long messageId; private final long messageId;
@ -74,7 +75,7 @@ public class MmsDownloadJob extends MasterSecretJob {
public void onAdded() { public void onAdded() {
if (automatic && KeyCachingService.getMasterSecret(context) == null) { if (automatic && KeyCachingService.getMasterSecret(context) == null) {
DatabaseFactory.getMmsDatabase(context).markIncomingNotificationReceived(threadId); DatabaseFactory.getMmsDatabase(context).markIncomingNotificationReceived(threadId);
MessageNotifier.updateNotification(context, null); MessageNotifier.updateNotification(context);
} }
} }
@ -120,19 +121,19 @@ public class MmsDownloadJob extends MasterSecretJob {
throw new MmsException("RetrieveConf was null"); throw new MmsException("RetrieveConf was null");
} }
storeRetrievedMms(masterSecret, contentLocation, messageId, threadId, retrieveConf, notification.get().getSubscriptionId(), notification.get().getFrom()); storeRetrievedMms(contentLocation, messageId, threadId, retrieveConf, notification.get().getSubscriptionId(), notification.get().getFrom());
} catch (ApnUnavailableException e) { } catch (ApnUnavailableException e) {
Log.w(TAG, e); Log.w(TAG, e);
handleDownloadError(masterSecret, messageId, threadId, MmsDatabase.Status.DOWNLOAD_APN_UNAVAILABLE, handleDownloadError(messageId, threadId, MmsDatabase.Status.DOWNLOAD_APN_UNAVAILABLE,
automatic); automatic);
} catch (MmsException e) { } catch (MmsException e) {
Log.w(TAG, e); Log.w(TAG, e);
handleDownloadError(masterSecret, messageId, threadId, handleDownloadError(messageId, threadId,
MmsDatabase.Status.DOWNLOAD_HARD_FAILURE, MmsDatabase.Status.DOWNLOAD_HARD_FAILURE,
automatic); automatic);
} catch (MmsRadioException | IOException e) { } catch (MmsRadioException | IOException e) {
Log.w(TAG, e); Log.w(TAG, e);
handleDownloadError(masterSecret, messageId, threadId, handleDownloadError(messageId, threadId,
MmsDatabase.Status.DOWNLOAD_SOFT_FAILURE, MmsDatabase.Status.DOWNLOAD_SOFT_FAILURE,
automatic); automatic);
} catch (DuplicateMessageException e) { } catch (DuplicateMessageException e) {
@ -157,7 +158,7 @@ public class MmsDownloadJob extends MasterSecretJob {
if (automatic) { if (automatic) {
database.markIncomingNotificationReceived(threadId); database.markIncomingNotificationReceived(threadId);
MessageNotifier.updateNotification(context, null, threadId); MessageNotifier.updateNotification(context, threadId);
} }
} }
@ -166,7 +167,7 @@ public class MmsDownloadJob extends MasterSecretJob {
return false; return false;
} }
private void storeRetrievedMms(MasterSecret masterSecret, String contentLocation, private void storeRetrievedMms(String contentLocation,
long messageId, long threadId, RetrieveConf retrieved, long messageId, long threadId, RetrieveConf retrieved,
int subscriptionId, @Nullable Address notificationFrom) int subscriptionId, @Nullable Address notificationFrom)
throws MmsException, NoSessionException, DuplicateMessageException, InvalidMessageException, throws MmsException, NoSessionException, DuplicateMessageException, InvalidMessageException,
@ -229,17 +230,15 @@ public class MmsDownloadJob extends MasterSecretJob {
} }
IncomingMediaMessage message = new IncomingMediaMessage(from, group, body, retrieved.getDate() * 1000L, attachments, subscriptionId, 0, false); IncomingMediaMessage message = new IncomingMediaMessage(from, group, body, retrieved.getDate() * 1000L, attachments, subscriptionId, 0, false);
Optional<InsertResult> insertResult = database.insertMessageInbox(new MasterSecretUnion(masterSecret), Optional<InsertResult> insertResult = database.insertMessageInbox(message, contentLocation, threadId);
message, contentLocation, threadId);
if (insertResult.isPresent()) { if (insertResult.isPresent()) {
database.delete(messageId); database.delete(messageId);
MessageNotifier.updateNotification(context, masterSecret, insertResult.get().getThreadId()); MessageNotifier.updateNotification(context, insertResult.get().getThreadId());
} }
} }
private void handleDownloadError(MasterSecret masterSecret, long messageId, long threadId, private void handleDownloadError(long messageId, long threadId, int downloadStatus, boolean automatic)
int downloadStatus, boolean automatic)
{ {
MmsDatabase db = DatabaseFactory.getMmsDatabase(context); MmsDatabase db = DatabaseFactory.getMmsDatabase(context);
@ -247,7 +246,7 @@ public class MmsDownloadJob extends MasterSecretJob {
if (automatic) { if (automatic) {
db.markIncomingNotificationReceived(threadId); db.markIncomingNotificationReceived(threadId);
MessageNotifier.updateNotification(context, masterSecret, threadId); MessageNotifier.updateNotification(context, threadId);
} }
} }
} }

View File

@ -76,10 +76,10 @@ public class MmsSendJob extends SendJob {
@Override @Override
public void onSend(MasterSecret masterSecret) throws MmsException, NoSuchMessageException, IOException { public void onSend(MasterSecret masterSecret) throws MmsException, NoSuchMessageException, IOException {
MmsDatabase database = DatabaseFactory.getMmsDatabase(context); MmsDatabase database = DatabaseFactory.getMmsDatabase(context);
OutgoingMediaMessage message = database.getOutgoingMessage(masterSecret, messageId); OutgoingMediaMessage message = database.getOutgoingMessage(messageId);
try { try {
SendReq pdu = constructSendPdu(masterSecret, message); SendReq pdu = constructSendPdu(message);
validateDestinations(message, pdu); validateDestinations(message, pdu);
@ -168,14 +168,14 @@ public class MmsSendJob extends SendJob {
} }
} }
private SendReq constructSendPdu(MasterSecret masterSecret, OutgoingMediaMessage message) private SendReq constructSendPdu(OutgoingMediaMessage message)
throws UndeliverableMessageException throws UndeliverableMessageException
{ {
SendReq req = new SendReq(); SendReq req = new SendReq();
String lineNumber = getMyNumber(context); String lineNumber = getMyNumber(context);
Address destination = message.getRecipient().getAddress(); Address destination = message.getRecipient().getAddress();
MediaConstraints mediaConstraints = MediaConstraints.getMmsMediaConstraints(message.getSubscriptionId()); MediaConstraints mediaConstraints = MediaConstraints.getMmsMediaConstraints(message.getSubscriptionId());
List<Attachment> scaledAttachments = scaleAttachments(masterSecret, mediaConstraints, message.getAttachments()); List<Attachment> scaledAttachments = scaleAttachments(mediaConstraints, message.getAttachments());
if (!TextUtils.isEmpty(lineNumber)) { if (!TextUtils.isEmpty(lineNumber)) {
req.setFrom(new EncodedStringValue(lineNumber)); req.setFrom(new EncodedStringValue(lineNumber));
@ -241,7 +241,7 @@ public class MmsSendJob extends SendJob {
int index = fileName.lastIndexOf("."); int index = fileName.lastIndexOf(".");
String contentId = (index == -1) ? fileName : fileName.substring(0, index); String contentId = (index == -1) ? fileName : fileName.substring(0, index);
part.setContentId(contentId.getBytes()); part.setContentId(contentId.getBytes());
part.setData(Util.readFully(PartAuthority.getAttachmentStream(context, masterSecret, attachment.getDataUri()))); part.setData(Util.readFully(PartAuthority.getAttachmentStream(context, attachment.getDataUri())));
body.addPart(part); body.addPart(part);
size += getPartSize(part); size += getPartSize(part);

View File

@ -11,15 +11,11 @@ import org.thoughtcrime.securesms.ApplicationContext;
import org.thoughtcrime.securesms.attachments.DatabaseAttachment; import org.thoughtcrime.securesms.attachments.DatabaseAttachment;
import org.thoughtcrime.securesms.attachments.PointerAttachment; import org.thoughtcrime.securesms.attachments.PointerAttachment;
import org.thoughtcrime.securesms.crypto.IdentityKeyUtil; import org.thoughtcrime.securesms.crypto.IdentityKeyUtil;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.crypto.MasterSecretUnion;
import org.thoughtcrime.securesms.crypto.MasterSecretUtil;
import org.thoughtcrime.securesms.crypto.SecurityEvent; import org.thoughtcrime.securesms.crypto.SecurityEvent;
import org.thoughtcrime.securesms.crypto.storage.SignalProtocolStoreImpl; import org.thoughtcrime.securesms.crypto.storage.SignalProtocolStoreImpl;
import org.thoughtcrime.securesms.crypto.storage.TextSecureSessionStore; import org.thoughtcrime.securesms.crypto.storage.TextSecureSessionStore;
import org.thoughtcrime.securesms.database.Address; import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.EncryptingSmsDatabase;
import org.thoughtcrime.securesms.database.GroupDatabase; import org.thoughtcrime.securesms.database.GroupDatabase;
import org.thoughtcrime.securesms.database.MessagingDatabase; import org.thoughtcrime.securesms.database.MessagingDatabase;
import org.thoughtcrime.securesms.database.MessagingDatabase.InsertResult; import org.thoughtcrime.securesms.database.MessagingDatabase.InsertResult;
@ -39,7 +35,6 @@ import org.thoughtcrime.securesms.mms.OutgoingSecureMediaMessage;
import org.thoughtcrime.securesms.mms.SlideDeck; import org.thoughtcrime.securesms.mms.SlideDeck;
import org.thoughtcrime.securesms.notifications.MessageNotifier; import org.thoughtcrime.securesms.notifications.MessageNotifier;
import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.service.KeyCachingService;
import org.thoughtcrime.securesms.service.WebRtcCallService; import org.thoughtcrime.securesms.service.WebRtcCallService;
import org.thoughtcrime.securesms.sms.IncomingEncryptedMessage; import org.thoughtcrime.securesms.sms.IncomingEncryptedMessage;
import org.thoughtcrime.securesms.sms.IncomingEndSessionMessage; import org.thoughtcrime.securesms.sms.IncomingEndSessionMessage;
@ -67,7 +62,6 @@ import org.whispersystems.libsignal.state.SessionStore;
import org.whispersystems.libsignal.state.SignalProtocolStore; import org.whispersystems.libsignal.state.SignalProtocolStore;
import org.whispersystems.libsignal.util.guava.Optional; import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.api.crypto.SignalServiceCipher; import org.whispersystems.signalservice.api.crypto.SignalServiceCipher;
import org.whispersystems.signalservice.api.messages.SignalServiceAttachment;
import org.whispersystems.signalservice.api.messages.SignalServiceContent; import org.whispersystems.signalservice.api.messages.SignalServiceContent;
import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage; import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage;
import org.whispersystems.signalservice.api.messages.SignalServiceEnvelope; import org.whispersystems.signalservice.api.messages.SignalServiceEnvelope;
@ -124,18 +118,11 @@ public class PushDecryptJob extends ContextJob {
return; return;
} }
MasterSecret masterSecret = KeyCachingService.getMasterSecret(context);
PushDatabase database = DatabaseFactory.getPushDatabase(context); PushDatabase database = DatabaseFactory.getPushDatabase(context);
SignalServiceEnvelope envelope = database.get(messageId); SignalServiceEnvelope envelope = database.get(messageId);
Optional<Long> optionalSmsMessageId = smsMessageId > 0 ? Optional.of(smsMessageId) : Optional<Long> optionalSmsMessageId = smsMessageId > 0 ? Optional.of(smsMessageId) : Optional.absent();
Optional.<Long>absent();
MasterSecretUnion masterSecretUnion; handleMessage(envelope, optionalSmsMessageId);
if (masterSecret == null) masterSecretUnion = new MasterSecretUnion(MasterSecretUtil.getAsymmetricMasterSecret(context, null));
else masterSecretUnion = new MasterSecretUnion(masterSecret);
handleMessage(masterSecretUnion, envelope, optionalSmsMessageId);
database.delete(messageId); database.delete(messageId);
} }
@ -149,7 +136,7 @@ public class PushDecryptJob extends ContextJob {
} }
private void handleMessage(MasterSecretUnion masterSecret, SignalServiceEnvelope envelope, Optional<Long> smsMessageId) { private void handleMessage(SignalServiceEnvelope envelope, Optional<Long> smsMessageId) {
try { try {
GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context); GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context);
SignalProtocolStore axolotlStore = new SignalProtocolStoreImpl(context); SignalProtocolStore axolotlStore = new SignalProtocolStoreImpl(context);
@ -161,11 +148,11 @@ public class PushDecryptJob extends ContextJob {
if (content.getDataMessage().isPresent()) { if (content.getDataMessage().isPresent()) {
SignalServiceDataMessage message = content.getDataMessage().get(); SignalServiceDataMessage message = content.getDataMessage().get();
if (message.isEndSession()) handleEndSessionMessage(masterSecret, envelope, message, smsMessageId); if (message.isEndSession()) handleEndSessionMessage(envelope, message, smsMessageId);
else if (message.isGroupUpdate()) handleGroupMessage(masterSecret, envelope, message, smsMessageId); else if (message.isGroupUpdate()) handleGroupMessage(envelope, message, smsMessageId);
else if (message.isExpirationUpdate()) handleExpirationUpdate(masterSecret, envelope, message, smsMessageId); else if (message.isExpirationUpdate()) handleExpirationUpdate(envelope, message, smsMessageId);
else if (message.getAttachments().isPresent()) handleMediaMessage(masterSecret, envelope, message, smsMessageId); else if (message.getAttachments().isPresent()) handleMediaMessage(envelope, message, smsMessageId);
else if (message.getBody().isPresent()) handleTextMessage(masterSecret, envelope, message, smsMessageId); else if (message.getBody().isPresent()) handleTextMessage(envelope, message, smsMessageId);
if (message.getGroupInfo().isPresent() && groupDatabase.isUnknownGroup(GroupUtil.getEncodedId(message.getGroupInfo().get().getGroupId(), false))) { if (message.getGroupInfo().isPresent() && groupDatabase.isUnknownGroup(GroupUtil.getEncodedId(message.getGroupInfo().get().getGroupId(), false))) {
handleUnknownGroupMessage(envelope, message.getGroupInfo().get()); handleUnknownGroupMessage(envelope, message.getGroupInfo().get());
@ -177,10 +164,10 @@ public class PushDecryptJob extends ContextJob {
} else if (content.getSyncMessage().isPresent()) { } else if (content.getSyncMessage().isPresent()) {
SignalServiceSyncMessage syncMessage = content.getSyncMessage().get(); SignalServiceSyncMessage syncMessage = content.getSyncMessage().get();
if (syncMessage.getSent().isPresent()) handleSynchronizeSentMessage(masterSecret, envelope, syncMessage.getSent().get()); if (syncMessage.getSent().isPresent()) handleSynchronizeSentMessage(envelope, syncMessage.getSent().get());
else if (syncMessage.getRequest().isPresent()) handleSynchronizeRequestMessage(masterSecret, syncMessage.getRequest().get()); else if (syncMessage.getRequest().isPresent()) handleSynchronizeRequestMessage(syncMessage.getRequest().get());
else if (syncMessage.getRead().isPresent()) handleSynchronizeReadMessage(masterSecret, syncMessage.getRead().get(), envelope.getTimestamp()); else if (syncMessage.getRead().isPresent()) handleSynchronizeReadMessage(syncMessage.getRead().get(), envelope.getTimestamp());
else if (syncMessage.getVerified().isPresent()) handleSynchronizeVerifiedMessage(masterSecret, syncMessage.getVerified().get()); else if (syncMessage.getVerified().isPresent()) handleSynchronizeVerifiedMessage(syncMessage.getVerified().get());
else Log.w(TAG, "Contains no known sync types..."); else Log.w(TAG, "Contains no known sync types...");
} else if (content.getCallMessage().isPresent()) { } else if (content.getCallMessage().isPresent()) {
Log.w(TAG, "Got call message..."); Log.w(TAG, "Got call message...");
@ -205,22 +192,22 @@ public class PushDecryptJob extends ContextJob {
} }
} catch (InvalidVersionException e) { } catch (InvalidVersionException e) {
Log.w(TAG, e); Log.w(TAG, e);
handleInvalidVersionMessage(masterSecret, envelope, smsMessageId); handleInvalidVersionMessage(envelope, smsMessageId);
} catch (InvalidMessageException | InvalidKeyIdException | InvalidKeyException | MmsException e) { } catch (InvalidMessageException | InvalidKeyIdException | InvalidKeyException | MmsException e) {
Log.w(TAG, e); Log.w(TAG, e);
handleCorruptMessage(masterSecret, envelope, smsMessageId); handleCorruptMessage(envelope, smsMessageId);
} catch (NoSessionException e) { } catch (NoSessionException e) {
Log.w(TAG, e); Log.w(TAG, e);
handleNoSessionMessage(masterSecret, envelope, smsMessageId); handleNoSessionMessage(envelope, smsMessageId);
} catch (LegacyMessageException e) { } catch (LegacyMessageException e) {
Log.w(TAG, e); Log.w(TAG, e);
handleLegacyMessage(masterSecret, envelope, smsMessageId); handleLegacyMessage(envelope, smsMessageId);
} catch (DuplicateMessageException e) { } catch (DuplicateMessageException e) {
Log.w(TAG, e); Log.w(TAG, e);
handleDuplicateMessage(masterSecret, envelope, smsMessageId); handleDuplicateMessage(envelope, smsMessageId);
} catch (UntrustedIdentityException e) { } catch (UntrustedIdentityException e) {
Log.w(TAG, e); Log.w(TAG, e);
handleUntrustedIdentityMessage(masterSecret, envelope, smsMessageId); handleUntrustedIdentityMessage(envelope, smsMessageId);
} }
} }
@ -304,22 +291,21 @@ public class PushDecryptJob extends ContextJob {
context.startService(intent); context.startService(intent);
} }
private void handleEndSessionMessage(@NonNull MasterSecretUnion masterSecret, private void handleEndSessionMessage(@NonNull SignalServiceEnvelope envelope,
@NonNull SignalServiceEnvelope envelope,
@NonNull SignalServiceDataMessage message, @NonNull SignalServiceDataMessage message,
@NonNull Optional<Long> smsMessageId) @NonNull Optional<Long> smsMessageId)
{ {
EncryptingSmsDatabase smsDatabase = DatabaseFactory.getEncryptingSmsDatabase(context); SmsDatabase smsDatabase = DatabaseFactory.getSmsDatabase(context);
IncomingTextMessage incomingTextMessage = new IncomingTextMessage(Address.fromExternal(context, envelope.getSource()), IncomingTextMessage incomingTextMessage = new IncomingTextMessage(Address.fromExternal(context, envelope.getSource()),
envelope.getSourceDevice(), envelope.getSourceDevice(),
message.getTimestamp(), message.getTimestamp(),
"", Optional.<SignalServiceGroup>absent(), 0); "", Optional.absent(), 0);
Long threadId; Long threadId;
if (!smsMessageId.isPresent()) { if (!smsMessageId.isPresent()) {
IncomingEndSessionMessage incomingEndSessionMessage = new IncomingEndSessionMessage(incomingTextMessage); IncomingEndSessionMessage incomingEndSessionMessage = new IncomingEndSessionMessage(incomingTextMessage);
Optional<InsertResult> insertResult = smsDatabase.insertMessageInbox(masterSecret, incomingEndSessionMessage); Optional<InsertResult> insertResult = smsDatabase.insertMessageInbox(incomingEndSessionMessage);
if (insertResult.isPresent()) threadId = insertResult.get().getThreadId(); if (insertResult.isPresent()) threadId = insertResult.get().getThreadId();
else threadId = null; else threadId = null;
@ -333,14 +319,13 @@ public class PushDecryptJob extends ContextJob {
sessionStore.deleteAllSessions(envelope.getSource()); sessionStore.deleteAllSessions(envelope.getSource());
SecurityEvent.broadcastSecurityUpdateEvent(context); SecurityEvent.broadcastSecurityUpdateEvent(context);
MessageNotifier.updateNotification(context, masterSecret.getMasterSecret().orNull(), threadId); MessageNotifier.updateNotification(context, threadId);
} }
} }
private long handleSynchronizeSentEndSessionMessage(@NonNull MasterSecretUnion masterSecret, private long handleSynchronizeSentEndSessionMessage(@NonNull SentTranscriptMessage message)
@NonNull SentTranscriptMessage message)
{ {
EncryptingSmsDatabase database = DatabaseFactory.getEncryptingSmsDatabase(context); SmsDatabase database = DatabaseFactory.getSmsDatabase(context);
Recipient recipient = getSyncMessageDestination(message); Recipient recipient = getSyncMessageDestination(message);
OutgoingTextMessage outgoingTextMessage = new OutgoingTextMessage(recipient, "", -1); OutgoingTextMessage outgoingTextMessage = new OutgoingTextMessage(recipient, "", -1);
OutgoingEndSessionMessage outgoingEndSessionMessage = new OutgoingEndSessionMessage(outgoingTextMessage); OutgoingEndSessionMessage outgoingEndSessionMessage = new OutgoingEndSessionMessage(outgoingTextMessage);
@ -353,20 +338,20 @@ public class PushDecryptJob extends ContextJob {
SecurityEvent.broadcastSecurityUpdateEvent(context); SecurityEvent.broadcastSecurityUpdateEvent(context);
long messageId = database.insertMessageOutbox(masterSecret, threadId, outgoingEndSessionMessage, long messageId = database.insertMessageOutbox(threadId, outgoingEndSessionMessage,
false, message.getTimestamp(), null); false, message.getTimestamp(),
null);
database.markAsSent(messageId, true); database.markAsSent(messageId, true);
} }
return threadId; return threadId;
} }
private void handleGroupMessage(@NonNull MasterSecretUnion masterSecret, private void handleGroupMessage(@NonNull SignalServiceEnvelope envelope,
@NonNull SignalServiceEnvelope envelope,
@NonNull SignalServiceDataMessage message, @NonNull SignalServiceDataMessage message,
@NonNull Optional<Long> smsMessageId) @NonNull Optional<Long> smsMessageId)
{ {
GroupMessageProcessor.process(context, masterSecret, envelope, message, false); GroupMessageProcessor.process(context, envelope, message, false);
if (smsMessageId.isPresent()) { if (smsMessageId.isPresent()) {
DatabaseFactory.getSmsDatabase(context).deleteMessage(smsMessageId.get()); DatabaseFactory.getSmsDatabase(context).deleteMessage(smsMessageId.get());
@ -381,25 +366,23 @@ public class PushDecryptJob extends ContextJob {
.add(new RequestGroupInfoJob(context, envelope.getSource(), group.getGroupId())); .add(new RequestGroupInfoJob(context, envelope.getSource(), group.getGroupId()));
} }
private void handleExpirationUpdate(@NonNull MasterSecretUnion masterSecret, private void handleExpirationUpdate(@NonNull SignalServiceEnvelope envelope,
@NonNull SignalServiceEnvelope envelope,
@NonNull SignalServiceDataMessage message, @NonNull SignalServiceDataMessage message,
@NonNull Optional<Long> smsMessageId) @NonNull Optional<Long> smsMessageId)
throws MmsException throws MmsException
{ {
MmsDatabase database = DatabaseFactory.getMmsDatabase(context); MmsDatabase database = DatabaseFactory.getMmsDatabase(context);
Recipient recipient = getMessageDestination(envelope, message); Recipient recipient = getMessageDestination(envelope, message);
IncomingMediaMessage mediaMessage = new IncomingMediaMessage(masterSecret, IncomingMediaMessage mediaMessage = new IncomingMediaMessage(Address.fromExternal(context, envelope.getSource()),
Address.fromExternal(context, envelope.getSource()),
message.getTimestamp(), -1, message.getTimestamp(), -1,
message.getExpiresInSeconds() * 1000, true, message.getExpiresInSeconds() * 1000, true,
Optional.fromNullable(envelope.getRelay()), Optional.fromNullable(envelope.getRelay()),
Optional.<String>absent(), message.getGroupInfo(), Optional.absent(), message.getGroupInfo(),
Optional.<List<SignalServiceAttachment>>absent()); Optional.absent());
database.insertSecureDecryptedMessageInbox(masterSecret, mediaMessage, -1); database.insertSecureDecryptedMessageInbox(mediaMessage, -1);
DatabaseFactory.getRecipientDatabase(context).setExpireMessages(recipient, message.getExpiresInSeconds()); DatabaseFactory.getRecipientDatabase(context).setExpireMessages(recipient, message.getExpiresInSeconds());
@ -408,14 +391,11 @@ public class PushDecryptJob extends ContextJob {
} }
} }
private void handleSynchronizeVerifiedMessage(@NonNull MasterSecretUnion masterSecret, private void handleSynchronizeVerifiedMessage(@NonNull VerifiedMessage verifiedMessage) {
@NonNull VerifiedMessage verifiedMessage) IdentityUtil.processVerifiedMessage(context, verifiedMessage);
{
IdentityUtil.processVerifiedMessage(context, masterSecret, verifiedMessage);
} }
private void handleSynchronizeSentMessage(@NonNull MasterSecretUnion masterSecret, private void handleSynchronizeSentMessage(@NonNull SignalServiceEnvelope envelope,
@NonNull SignalServiceEnvelope envelope,
@NonNull SentTranscriptMessage message) @NonNull SentTranscriptMessage message)
throws MmsException throws MmsException
{ {
@ -424,15 +404,15 @@ public class PushDecryptJob extends ContextJob {
Long threadId; Long threadId;
if (message.getMessage().isEndSession()) { if (message.getMessage().isEndSession()) {
threadId = handleSynchronizeSentEndSessionMessage(masterSecret, message); threadId = handleSynchronizeSentEndSessionMessage(message);
} else if (message.getMessage().isGroupUpdate()) { } else if (message.getMessage().isGroupUpdate()) {
threadId = GroupMessageProcessor.process(context, masterSecret, envelope, message.getMessage(), true); threadId = GroupMessageProcessor.process(context, envelope, message.getMessage(), true);
} else if (message.getMessage().isExpirationUpdate()) { } else if (message.getMessage().isExpirationUpdate()) {
threadId = handleSynchronizeSentExpirationUpdate(masterSecret, message); threadId = handleSynchronizeSentExpirationUpdate(message);
} else if (message.getMessage().getAttachments().isPresent()) { } else if (message.getMessage().getAttachments().isPresent()) {
threadId = handleSynchronizeSentMediaMessage(masterSecret, message); threadId = handleSynchronizeSentMediaMessage(message);
} else { } else {
threadId = handleSynchronizeSentTextMessage(masterSecret, message); threadId = handleSynchronizeSentTextMessage(message);
} }
if (message.getMessage().getGroupInfo().isPresent() && groupDatabase.isUnknownGroup(GroupUtil.getEncodedId(message.getMessage().getGroupInfo().get().getGroupId(), false))) { if (message.getMessage().getGroupInfo().isPresent() && groupDatabase.isUnknownGroup(GroupUtil.getEncodedId(message.getMessage().getGroupInfo().get().getGroupId(), false))) {
@ -453,14 +433,13 @@ public class PushDecryptJob extends ContextJob {
if (threadId != null) { if (threadId != null) {
DatabaseFactory.getThreadDatabase(getContext()).setRead(threadId, true); DatabaseFactory.getThreadDatabase(getContext()).setRead(threadId, true);
MessageNotifier.updateNotification(getContext(), masterSecret.getMasterSecret().orNull()); MessageNotifier.updateNotification(getContext());
} }
MessageNotifier.setLastDesktopActivityTimestamp(message.getTimestamp()); MessageNotifier.setLastDesktopActivityTimestamp(message.getTimestamp());
} }
private void handleSynchronizeRequestMessage(@NonNull MasterSecretUnion masterSecret, private void handleSynchronizeRequestMessage(@NonNull RequestMessage message)
@NonNull RequestMessage message)
{ {
if (message.isContactsRequest()) { if (message.isContactsRequest()) {
ApplicationContext.getInstance(context) ApplicationContext.getInstance(context)
@ -487,9 +466,7 @@ public class PushDecryptJob extends ContextJob {
} }
} }
private void handleSynchronizeReadMessage(@NonNull MasterSecretUnion masterSecret, private void handleSynchronizeReadMessage(@NonNull List<ReadMessage> readMessages, long envelopeTimestamp)
@NonNull List<ReadMessage> readMessages,
long envelopeTimestamp)
{ {
for (ReadMessage readMessage : readMessages) { for (ReadMessage readMessage : readMessages) {
List<Pair<Long, Long>> expiringText = DatabaseFactory.getSmsDatabase(context).setTimestampRead(new SyncMessageId(Address.fromExternal(context, readMessage.getSender()), readMessage.getTimestamp()), envelopeTimestamp); List<Pair<Long, Long>> expiringText = DatabaseFactory.getSmsDatabase(context).setTimestampRead(new SyncMessageId(Address.fromExternal(context, readMessage.getSender()), readMessage.getTimestamp()), envelopeTimestamp);
@ -510,19 +487,17 @@ public class PushDecryptJob extends ContextJob {
MessageNotifier.setLastDesktopActivityTimestamp(envelopeTimestamp); MessageNotifier.setLastDesktopActivityTimestamp(envelopeTimestamp);
MessageNotifier.cancelDelayedNotifications(); MessageNotifier.cancelDelayedNotifications();
MessageNotifier.updateNotification(context, masterSecret.getMasterSecret().orNull()); MessageNotifier.updateNotification(context);
} }
private void handleMediaMessage(@NonNull MasterSecretUnion masterSecret, private void handleMediaMessage(@NonNull SignalServiceEnvelope envelope,
@NonNull SignalServiceEnvelope envelope,
@NonNull SignalServiceDataMessage message, @NonNull SignalServiceDataMessage message,
@NonNull Optional<Long> smsMessageId) @NonNull Optional<Long> smsMessageId)
throws MmsException throws MmsException
{ {
MmsDatabase database = DatabaseFactory.getMmsDatabase(context); MmsDatabase database = DatabaseFactory.getMmsDatabase(context);
Recipient recipient = getMessageDestination(envelope, message); Recipient recipient = getMessageDestination(envelope, message);
IncomingMediaMessage mediaMessage = new IncomingMediaMessage(masterSecret, IncomingMediaMessage mediaMessage = new IncomingMediaMessage(Address.fromExternal(context, envelope.getSource()),
Address.fromExternal(context, envelope.getSource()),
message.getTimestamp(), -1, message.getTimestamp(), -1,
message.getExpiresInSeconds() * 1000, false, message.getExpiresInSeconds() * 1000, false,
Optional.fromNullable(envelope.getRelay()), Optional.fromNullable(envelope.getRelay()),
@ -531,36 +506,29 @@ public class PushDecryptJob extends ContextJob {
message.getAttachments()); message.getAttachments());
if (message.getExpiresInSeconds() != recipient.getExpireMessages()) { if (message.getExpiresInSeconds() != recipient.getExpireMessages()) {
handleExpirationUpdate(masterSecret, envelope, message, Optional.<Long>absent()); handleExpirationUpdate(envelope, message, Optional.absent());
} }
Optional<InsertResult> insertResult = database.insertSecureDecryptedMessageInbox(masterSecret, mediaMessage, -1); Optional<InsertResult> insertResult = database.insertSecureDecryptedMessageInbox(mediaMessage, -1);
if (insertResult.isPresent()) { if (insertResult.isPresent()) {
List<DatabaseAttachment> attachments = DatabaseFactory.getAttachmentDatabase(context).getAttachmentsForMessage(null, insertResult.get().getMessageId()); List<DatabaseAttachment> attachments = DatabaseFactory.getAttachmentDatabase(context).getAttachmentsForMessage(insertResult.get().getMessageId());
for (DatabaseAttachment attachment : attachments) { for (DatabaseAttachment attachment : attachments) {
ApplicationContext.getInstance(context) ApplicationContext.getInstance(context)
.getJobManager() .getJobManager()
.add(new AttachmentDownloadJob(context, insertResult.get().getMessageId(), attachment.getAttachmentId(), false)); .add(new AttachmentDownloadJob(context, insertResult.get().getMessageId(), attachment.getAttachmentId(), false));
if (!masterSecret.getMasterSecret().isPresent()) {
ApplicationContext.getInstance(context)
.getJobManager()
.add(new AttachmentFileNameJob(context, masterSecret.getAsymmetricMasterSecret().get(), attachment, mediaMessage));
}
} }
if (smsMessageId.isPresent()) { if (smsMessageId.isPresent()) {
DatabaseFactory.getSmsDatabase(context).deleteMessage(smsMessageId.get()); DatabaseFactory.getSmsDatabase(context).deleteMessage(smsMessageId.get());
} }
MessageNotifier.updateNotification(context, masterSecret.getMasterSecret().orNull(), insertResult.get().getThreadId()); MessageNotifier.updateNotification(context, insertResult.get().getThreadId());
} }
} }
private long handleSynchronizeSentExpirationUpdate(@NonNull MasterSecretUnion masterSecret, private long handleSynchronizeSentExpirationUpdate(@NonNull SentTranscriptMessage message)
@NonNull SentTranscriptMessage message)
throws MmsException throws MmsException
{ {
MmsDatabase database = DatabaseFactory.getMmsDatabase(context); MmsDatabase database = DatabaseFactory.getMmsDatabase(context);
@ -571,7 +539,7 @@ public class PushDecryptJob extends ContextJob {
message.getMessage().getExpiresInSeconds() * 1000); message.getMessage().getExpiresInSeconds() * 1000);
long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipient); long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipient);
long messageId = database.insertMessageOutbox(masterSecret, expirationUpdateMessage, threadId, false, null); long messageId = database.insertMessageOutbox(expirationUpdateMessage, threadId, false, null);
database.markAsSent(messageId, true); database.markAsSent(messageId, true);
@ -580,14 +548,13 @@ public class PushDecryptJob extends ContextJob {
return threadId; return threadId;
} }
private long handleSynchronizeSentMediaMessage(@NonNull MasterSecretUnion masterSecret, private long handleSynchronizeSentMediaMessage(@NonNull SentTranscriptMessage message)
@NonNull SentTranscriptMessage message)
throws MmsException throws MmsException
{ {
MmsDatabase database = DatabaseFactory.getMmsDatabase(context); MmsDatabase database = DatabaseFactory.getMmsDatabase(context);
Recipient recipients = getSyncMessageDestination(message); Recipient recipients = getSyncMessageDestination(message);
OutgoingMediaMessage mediaMessage = new OutgoingMediaMessage(recipients, message.getMessage().getBody().orNull(), OutgoingMediaMessage mediaMessage = new OutgoingMediaMessage(recipients, message.getMessage().getBody().orNull(),
PointerAttachment.forPointers(masterSecret, message.getMessage().getAttachments()), PointerAttachment.forPointers(message.getMessage().getAttachments()),
message.getTimestamp(), -1, message.getTimestamp(), -1,
message.getMessage().getExpiresInSeconds() * 1000, message.getMessage().getExpiresInSeconds() * 1000,
ThreadDatabase.DistributionTypes.DEFAULT); ThreadDatabase.DistributionTypes.DEFAULT);
@ -595,15 +562,15 @@ public class PushDecryptJob extends ContextJob {
mediaMessage = new OutgoingSecureMediaMessage(mediaMessage); mediaMessage = new OutgoingSecureMediaMessage(mediaMessage);
if (recipients.getExpireMessages() != message.getMessage().getExpiresInSeconds()) { if (recipients.getExpireMessages() != message.getMessage().getExpiresInSeconds()) {
handleSynchronizeSentExpirationUpdate(masterSecret, message); handleSynchronizeSentExpirationUpdate(message);
} }
long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipients); long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipients);
long messageId = database.insertMessageOutbox(masterSecret, mediaMessage, threadId, false, null); long messageId = database.insertMessageOutbox(mediaMessage, threadId, false, null);
database.markAsSent(messageId, true); database.markAsSent(messageId, true);
for (DatabaseAttachment attachment : DatabaseFactory.getAttachmentDatabase(context).getAttachmentsForMessage(null, messageId)) { for (DatabaseAttachment attachment : DatabaseFactory.getAttachmentDatabase(context).getAttachmentsForMessage(messageId)) {
ApplicationContext.getInstance(context) ApplicationContext.getInstance(context)
.getJobManager() .getJobManager()
.add(new AttachmentDownloadJob(context, messageId, attachment.getAttachmentId(), false)); .add(new AttachmentDownloadJob(context, messageId, attachment.getAttachmentId(), false));
@ -621,24 +588,23 @@ public class PushDecryptJob extends ContextJob {
return threadId; return threadId;
} }
private void handleTextMessage(@NonNull MasterSecretUnion masterSecret, private void handleTextMessage(@NonNull SignalServiceEnvelope envelope,
@NonNull SignalServiceEnvelope envelope,
@NonNull SignalServiceDataMessage message, @NonNull SignalServiceDataMessage message,
@NonNull Optional<Long> smsMessageId) @NonNull Optional<Long> smsMessageId)
throws MmsException throws MmsException
{ {
EncryptingSmsDatabase database = DatabaseFactory.getEncryptingSmsDatabase(context); SmsDatabase database = DatabaseFactory.getSmsDatabase(context);
String body = message.getBody().isPresent() ? message.getBody().get() : ""; String body = message.getBody().isPresent() ? message.getBody().get() : "";
Recipient recipient = getMessageDestination(envelope, message); Recipient recipient = getMessageDestination(envelope, message);
if (message.getExpiresInSeconds() != recipient.getExpireMessages()) { if (message.getExpiresInSeconds() != recipient.getExpireMessages()) {
handleExpirationUpdate(masterSecret, envelope, message, Optional.<Long>absent()); handleExpirationUpdate(envelope, message, Optional.absent());
} }
Long threadId; Long threadId;
if (smsMessageId.isPresent() && !message.getGroupInfo().isPresent()) { if (smsMessageId.isPresent() && !message.getGroupInfo().isPresent()) {
threadId = database.updateBundleMessageBody(masterSecret, smsMessageId.get(), body).second; threadId = database.updateBundleMessageBody(smsMessageId.get(), body).second;
} else { } else {
IncomingTextMessage textMessage = new IncomingTextMessage(Address.fromExternal(context, envelope.getSource()), IncomingTextMessage textMessage = new IncomingTextMessage(Address.fromExternal(context, envelope.getSource()),
envelope.getSourceDevice(), envelope.getSourceDevice(),
@ -647,7 +613,7 @@ public class PushDecryptJob extends ContextJob {
message.getExpiresInSeconds() * 1000); message.getExpiresInSeconds() * 1000);
textMessage = new IncomingEncryptedMessage(textMessage, body); textMessage = new IncomingEncryptedMessage(textMessage, body);
Optional<InsertResult> insertResult = database.insertMessageInbox(masterSecret, textMessage); Optional<InsertResult> insertResult = database.insertMessageInbox(textMessage);
if (insertResult.isPresent()) threadId = insertResult.get().getThreadId(); if (insertResult.isPresent()) threadId = insertResult.get().getThreadId();
else threadId = null; else threadId = null;
@ -656,12 +622,11 @@ public class PushDecryptJob extends ContextJob {
} }
if (threadId != null) { if (threadId != null) {
MessageNotifier.updateNotification(context, masterSecret.getMasterSecret().orNull(), threadId); MessageNotifier.updateNotification(context, threadId);
} }
} }
private long handleSynchronizeSentTextMessage(@NonNull MasterSecretUnion masterSecret, private long handleSynchronizeSentTextMessage(@NonNull SentTranscriptMessage message)
@NonNull SentTranscriptMessage message)
throws MmsException throws MmsException
{ {
@ -670,7 +635,7 @@ public class PushDecryptJob extends ContextJob {
long expiresInMillis = message.getMessage().getExpiresInSeconds() * 1000; long expiresInMillis = message.getMessage().getExpiresInSeconds() * 1000;
if (recipient.getExpireMessages() != message.getMessage().getExpiresInSeconds()) { if (recipient.getExpireMessages() != message.getMessage().getExpiresInSeconds()) {
handleSynchronizeSentExpirationUpdate(masterSecret, message); handleSynchronizeSentExpirationUpdate(message);
} }
long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipient); long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipient);
@ -682,12 +647,12 @@ public class PushDecryptJob extends ContextJob {
OutgoingMediaMessage outgoingMediaMessage = new OutgoingMediaMessage(recipient, new SlideDeck(), body, message.getTimestamp(), -1, expiresInMillis, ThreadDatabase.DistributionTypes.DEFAULT); OutgoingMediaMessage outgoingMediaMessage = new OutgoingMediaMessage(recipient, new SlideDeck(), body, message.getTimestamp(), -1, expiresInMillis, ThreadDatabase.DistributionTypes.DEFAULT);
outgoingMediaMessage = new OutgoingSecureMediaMessage(outgoingMediaMessage); outgoingMediaMessage = new OutgoingSecureMediaMessage(outgoingMediaMessage);
messageId = DatabaseFactory.getMmsDatabase(context).insertMessageOutbox(masterSecret, outgoingMediaMessage, threadId, false, null); messageId = DatabaseFactory.getMmsDatabase(context).insertMessageOutbox(outgoingMediaMessage, threadId, false, null);
database = DatabaseFactory.getMmsDatabase(context); database = DatabaseFactory.getMmsDatabase(context);
} else { } else {
OutgoingTextMessage outgoingTextMessage = new OutgoingEncryptedMessage(recipient, body, expiresInMillis); OutgoingTextMessage outgoingTextMessage = new OutgoingEncryptedMessage(recipient, body, expiresInMillis);
messageId = DatabaseFactory.getEncryptingSmsDatabase(context).insertMessageOutbox(masterSecret, threadId, outgoingTextMessage, false, message.getTimestamp(), null); messageId = DatabaseFactory.getSmsDatabase(context).insertMessageOutbox(threadId, outgoingTextMessage, false, message.getTimestamp(), null);
database = DatabaseFactory.getSmsDatabase(context); database = DatabaseFactory.getSmsDatabase(context);
} }
@ -703,80 +668,76 @@ public class PushDecryptJob extends ContextJob {
return threadId; return threadId;
} }
private void handleInvalidVersionMessage(@NonNull MasterSecretUnion masterSecret, private void handleInvalidVersionMessage(@NonNull SignalServiceEnvelope envelope,
@NonNull SignalServiceEnvelope envelope,
@NonNull Optional<Long> smsMessageId) @NonNull Optional<Long> smsMessageId)
{ {
EncryptingSmsDatabase smsDatabase = DatabaseFactory.getEncryptingSmsDatabase(context); SmsDatabase smsDatabase = DatabaseFactory.getSmsDatabase(context);
if (!smsMessageId.isPresent()) { if (!smsMessageId.isPresent()) {
Optional<InsertResult> insertResult = insertPlaceholder(envelope); Optional<InsertResult> insertResult = insertPlaceholder(envelope);
if (insertResult.isPresent()) { if (insertResult.isPresent()) {
smsDatabase.markAsInvalidVersionKeyExchange(insertResult.get().getMessageId()); smsDatabase.markAsInvalidVersionKeyExchange(insertResult.get().getMessageId());
MessageNotifier.updateNotification(context, masterSecret.getMasterSecret().orNull(), insertResult.get().getThreadId()); MessageNotifier.updateNotification(context, insertResult.get().getThreadId());
} }
} else { } else {
smsDatabase.markAsInvalidVersionKeyExchange(smsMessageId.get()); smsDatabase.markAsInvalidVersionKeyExchange(smsMessageId.get());
} }
} }
private void handleCorruptMessage(@NonNull MasterSecretUnion masterSecret, private void handleCorruptMessage(@NonNull SignalServiceEnvelope envelope,
@NonNull SignalServiceEnvelope envelope,
@NonNull Optional<Long> smsMessageId) @NonNull Optional<Long> smsMessageId)
{ {
EncryptingSmsDatabase smsDatabase = DatabaseFactory.getEncryptingSmsDatabase(context); SmsDatabase smsDatabase = DatabaseFactory.getSmsDatabase(context);
if (!smsMessageId.isPresent()) { if (!smsMessageId.isPresent()) {
Optional<InsertResult> insertResult = insertPlaceholder(envelope); Optional<InsertResult> insertResult = insertPlaceholder(envelope);
if (insertResult.isPresent()) { if (insertResult.isPresent()) {
smsDatabase.markAsDecryptFailed(insertResult.get().getMessageId()); smsDatabase.markAsDecryptFailed(insertResult.get().getMessageId());
MessageNotifier.updateNotification(context, masterSecret.getMasterSecret().orNull(), insertResult.get().getThreadId()); MessageNotifier.updateNotification(context, insertResult.get().getThreadId());
} }
} else { } else {
smsDatabase.markAsDecryptFailed(smsMessageId.get()); smsDatabase.markAsDecryptFailed(smsMessageId.get());
} }
} }
private void handleNoSessionMessage(@NonNull MasterSecretUnion masterSecret, private void handleNoSessionMessage(@NonNull SignalServiceEnvelope envelope,
@NonNull SignalServiceEnvelope envelope,
@NonNull Optional<Long> smsMessageId) @NonNull Optional<Long> smsMessageId)
{ {
EncryptingSmsDatabase smsDatabase = DatabaseFactory.getEncryptingSmsDatabase(context); SmsDatabase smsDatabase = DatabaseFactory.getSmsDatabase(context);
if (!smsMessageId.isPresent()) { if (!smsMessageId.isPresent()) {
Optional<InsertResult> insertResult = insertPlaceholder(envelope); Optional<InsertResult> insertResult = insertPlaceholder(envelope);
if (insertResult.isPresent()) { if (insertResult.isPresent()) {
smsDatabase.markAsNoSession(insertResult.get().getMessageId()); smsDatabase.markAsNoSession(insertResult.get().getMessageId());
MessageNotifier.updateNotification(context, masterSecret.getMasterSecret().orNull(), insertResult.get().getThreadId()); MessageNotifier.updateNotification(context, insertResult.get().getThreadId());
} }
} else { } else {
smsDatabase.markAsNoSession(smsMessageId.get()); smsDatabase.markAsNoSession(smsMessageId.get());
} }
} }
private void handleLegacyMessage(@NonNull MasterSecretUnion masterSecret, private void handleLegacyMessage(@NonNull SignalServiceEnvelope envelope,
@NonNull SignalServiceEnvelope envelope,
@NonNull Optional<Long> smsMessageId) @NonNull Optional<Long> smsMessageId)
{ {
EncryptingSmsDatabase smsDatabase = DatabaseFactory.getEncryptingSmsDatabase(context); SmsDatabase smsDatabase = DatabaseFactory.getSmsDatabase(context);
if (!smsMessageId.isPresent()) { if (!smsMessageId.isPresent()) {
Optional<InsertResult> insertResult = insertPlaceholder(envelope); Optional<InsertResult> insertResult = insertPlaceholder(envelope);
if (insertResult.isPresent()) { if (insertResult.isPresent()) {
smsDatabase.markAsLegacyVersion(insertResult.get().getMessageId()); smsDatabase.markAsLegacyVersion(insertResult.get().getMessageId());
MessageNotifier.updateNotification(context, masterSecret.getMasterSecret().orNull(), insertResult.get().getThreadId()); MessageNotifier.updateNotification(context, insertResult.get().getThreadId());
} }
} else { } else {
smsDatabase.markAsLegacyVersion(smsMessageId.get()); smsDatabase.markAsLegacyVersion(smsMessageId.get());
} }
} }
private void handleDuplicateMessage(@NonNull MasterSecretUnion masterSecret, @SuppressWarnings("unused")
@NonNull SignalServiceEnvelope envelope, private void handleDuplicateMessage(@NonNull SignalServiceEnvelope envelope,
@NonNull Optional<Long> smsMessageId) @NonNull Optional<Long> smsMessageId)
{ {
// Let's start ignoring these now // Let's start ignoring these now
@ -791,12 +752,11 @@ public class PushDecryptJob extends ContextJob {
// } // }
} }
private void handleUntrustedIdentityMessage(@NonNull MasterSecretUnion masterSecret, private void handleUntrustedIdentityMessage(@NonNull SignalServiceEnvelope envelope,
@NonNull SignalServiceEnvelope envelope,
@NonNull Optional<Long> smsMessageId) @NonNull Optional<Long> smsMessageId)
{ {
try { try {
EncryptingSmsDatabase database = DatabaseFactory.getEncryptingSmsDatabase(context); SmsDatabase database = DatabaseFactory.getSmsDatabase(context);
Address sourceAddress = Address.fromExternal(context, envelope.getSource()); Address sourceAddress = Address.fromExternal(context, envelope.getSource());
byte[] serialized = envelope.hasLegacyMessage() ? envelope.getLegacyMessage() : envelope.getContent(); byte[] serialized = envelope.hasLegacyMessage() ? envelope.getLegacyMessage() : envelope.getContent();
PreKeySignalMessage whisperMessage = new PreKeySignalMessage(serialized); PreKeySignalMessage whisperMessage = new PreKeySignalMessage(serialized);
@ -806,18 +766,18 @@ public class PushDecryptJob extends ContextJob {
IncomingTextMessage textMessage = new IncomingTextMessage(sourceAddress, IncomingTextMessage textMessage = new IncomingTextMessage(sourceAddress,
envelope.getSourceDevice(), envelope.getSourceDevice(),
envelope.getTimestamp(), encoded, envelope.getTimestamp(), encoded,
Optional.<SignalServiceGroup>absent(), 0); Optional.absent(), 0);
if (!smsMessageId.isPresent()) { if (!smsMessageId.isPresent()) {
IncomingPreKeyBundleMessage bundleMessage = new IncomingPreKeyBundleMessage(textMessage, encoded, envelope.hasLegacyMessage()); IncomingPreKeyBundleMessage bundleMessage = new IncomingPreKeyBundleMessage(textMessage, encoded, envelope.hasLegacyMessage());
Optional<InsertResult> insertResult = database.insertMessageInbox(masterSecret, bundleMessage); Optional<InsertResult> insertResult = database.insertMessageInbox(bundleMessage);
if (insertResult.isPresent()) { if (insertResult.isPresent()) {
database.setMismatchedIdentity(insertResult.get().getMessageId(), sourceAddress, identityKey); database.setMismatchedIdentity(insertResult.get().getMessageId(), sourceAddress, identityKey);
MessageNotifier.updateNotification(context, masterSecret.getMasterSecret().orNull(), insertResult.get().getThreadId()); MessageNotifier.updateNotification(context, insertResult.get().getThreadId());
} }
} else { } else {
database.updateMessageBody(masterSecret, smsMessageId.get(), encoded); database.updateMessageBody(smsMessageId.get(), encoded);
database.markAsPreKeyBundle(smsMessageId.get()); database.markAsPreKeyBundle(smsMessageId.get());
database.setMismatchedIdentity(smsMessageId.get(), sourceAddress, identityKey); database.setMismatchedIdentity(smsMessageId.get(), sourceAddress, identityKey);
} }
@ -863,11 +823,11 @@ public class PushDecryptJob extends ContextJob {
} }
private Optional<InsertResult> insertPlaceholder(@NonNull SignalServiceEnvelope envelope) { private Optional<InsertResult> insertPlaceholder(@NonNull SignalServiceEnvelope envelope) {
EncryptingSmsDatabase database = DatabaseFactory.getEncryptingSmsDatabase(context); SmsDatabase database = DatabaseFactory.getSmsDatabase(context);
IncomingTextMessage textMessage = new IncomingTextMessage(Address.fromExternal(context, envelope.getSource()), IncomingTextMessage textMessage = new IncomingTextMessage(Address.fromExternal(context, envelope.getSource()),
envelope.getSourceDevice(), envelope.getSourceDevice(),
envelope.getTimestamp(), "", envelope.getTimestamp(), "",
Optional.<SignalServiceGroup>absent(), 0); Optional.absent(), 0);
textMessage = new IncomingEncryptedMessage(textMessage, ""); textMessage = new IncomingEncryptedMessage(textMessage, "");
return database.insertMessageInbox(textMessage); return database.insertMessageInbox(textMessage);

View File

@ -12,7 +12,6 @@ import org.thoughtcrime.securesms.attachments.Attachment;
import org.thoughtcrime.securesms.crypto.MasterSecret; import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.database.Address; import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.GroupReceiptDatabase;
import org.thoughtcrime.securesms.database.GroupReceiptDatabase.GroupReceiptInfo; import org.thoughtcrime.securesms.database.GroupReceiptDatabase.GroupReceiptInfo;
import org.thoughtcrime.securesms.database.MmsDatabase; import org.thoughtcrime.securesms.database.MmsDatabase;
import org.thoughtcrime.securesms.database.NoSuchMessageException; import org.thoughtcrime.securesms.database.NoSuchMessageException;
@ -82,10 +81,10 @@ public class PushGroupSendJob extends PushSendJob implements InjectableType {
throws MmsException, IOException, NoSuchMessageException throws MmsException, IOException, NoSuchMessageException
{ {
MmsDatabase database = DatabaseFactory.getMmsDatabase(context); MmsDatabase database = DatabaseFactory.getMmsDatabase(context);
OutgoingMediaMessage message = database.getOutgoingMessage(masterSecret, messageId); OutgoingMediaMessage message = database.getOutgoingMessage(messageId);
try { try {
deliver(masterSecret, message, filterAddress == null ? null : Address.fromSerialized(filterAddress)); deliver(message, filterAddress == null ? null : Address.fromSerialized(filterAddress));
database.markAsSent(messageId, true); database.markAsSent(messageId, true);
markAttachmentsUploaded(messageId, message.getAttachments()); markAttachmentsUploaded(messageId, message.getAttachments());
@ -135,7 +134,7 @@ public class PushGroupSendJob extends PushSendJob implements InjectableType {
DatabaseFactory.getMmsDatabase(context).markAsSentFailed(messageId); DatabaseFactory.getMmsDatabase(context).markAsSentFailed(messageId);
} }
private void deliver(MasterSecret masterSecret, OutgoingMediaMessage message, @Nullable Address filterAddress) private void deliver(OutgoingMediaMessage message, @Nullable Address filterAddress)
throws IOException, RecipientFormattingException, InvalidNumberException, throws IOException, RecipientFormattingException, InvalidNumberException,
EncapsulatedExceptions, UndeliverableMessageException EncapsulatedExceptions, UndeliverableMessageException
{ {
@ -143,8 +142,8 @@ public class PushGroupSendJob extends PushSendJob implements InjectableType {
Optional<byte[]> profileKey = getProfileKey(message.getRecipient()); Optional<byte[]> profileKey = getProfileKey(message.getRecipient());
List<Address> recipients = getGroupMessageRecipients(groupId, messageId); List<Address> recipients = getGroupMessageRecipients(groupId, messageId);
MediaConstraints mediaConstraints = MediaConstraints.getPushMediaConstraints(); MediaConstraints mediaConstraints = MediaConstraints.getPushMediaConstraints();
List<Attachment> scaledAttachments = scaleAttachments(masterSecret, mediaConstraints, message.getAttachments()); List<Attachment> scaledAttachments = scaleAttachments(mediaConstraints, message.getAttachments());
List<SignalServiceAttachment> attachmentStreams = getAttachmentsFor(masterSecret, scaledAttachments); List<SignalServiceAttachment> attachmentStreams = getAttachmentsFor(scaledAttachments);
List<SignalServiceAddress> addresses; List<SignalServiceAddress> addresses;

View File

@ -59,10 +59,10 @@ public class PushMediaSendJob extends PushSendJob implements InjectableType {
{ {
ExpiringMessageManager expirationManager = ApplicationContext.getInstance(context).getExpiringMessageManager(); ExpiringMessageManager expirationManager = ApplicationContext.getInstance(context).getExpiringMessageManager();
MmsDatabase database = DatabaseFactory.getMmsDatabase(context); MmsDatabase database = DatabaseFactory.getMmsDatabase(context);
OutgoingMediaMessage message = database.getOutgoingMessage(masterSecret, messageId); OutgoingMediaMessage message = database.getOutgoingMessage(messageId);
try { try {
deliver(masterSecret, message); deliver(message);
database.markAsSent(messageId, true); database.markAsSent(messageId, true);
markAttachmentsUploaded(messageId, message.getAttachments()); markAttachmentsUploaded(messageId, message.getAttachments());
@ -97,7 +97,7 @@ public class PushMediaSendJob extends PushSendJob implements InjectableType {
notifyMediaMessageDeliveryFailed(context, messageId); notifyMediaMessageDeliveryFailed(context, messageId);
} }
private void deliver(MasterSecret masterSecret, OutgoingMediaMessage message) private void deliver(OutgoingMediaMessage message)
throws RetryLaterException, InsecureFallbackApprovalException, UntrustedIdentityException, throws RetryLaterException, InsecureFallbackApprovalException, UntrustedIdentityException,
UndeliverableMessageException UndeliverableMessageException
{ {
@ -108,8 +108,8 @@ public class PushMediaSendJob extends PushSendJob implements InjectableType {
try { try {
SignalServiceAddress address = getPushAddress(message.getRecipient().getAddress()); SignalServiceAddress address = getPushAddress(message.getRecipient().getAddress());
MediaConstraints mediaConstraints = MediaConstraints.getPushMediaConstraints(); MediaConstraints mediaConstraints = MediaConstraints.getPushMediaConstraints();
List<Attachment> scaledAttachments = scaleAttachments(masterSecret, mediaConstraints, message.getAttachments()); List<Attachment> scaledAttachments = scaleAttachments(mediaConstraints, message.getAttachments());
List<SignalServiceAttachment> attachmentStreams = getAttachmentsFor(masterSecret, scaledAttachments); List<SignalServiceAttachment> attachmentStreams = getAttachmentsFor(scaledAttachments);
Optional<byte[]> profileKey = getProfileKey(message.getRecipient()); Optional<byte[]> profileKey = getProfileKey(message.getRecipient());
SignalServiceDataMessage mediaMessage = SignalServiceDataMessage.newBuilder() SignalServiceDataMessage mediaMessage = SignalServiceDataMessage.newBuilder()
.withBody(message.getBody()) .withBody(message.getBody())

View File

@ -10,7 +10,6 @@ import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.MessagingDatabase.SyncMessageId; import org.thoughtcrime.securesms.database.MessagingDatabase.SyncMessageId;
import org.thoughtcrime.securesms.database.RecipientDatabase; import org.thoughtcrime.securesms.database.RecipientDatabase;
import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.service.KeyCachingService;
import org.whispersystems.jobqueue.JobManager; import org.whispersystems.jobqueue.JobManager;
import org.whispersystems.jobqueue.JobParameters; import org.whispersystems.jobqueue.JobParameters;
import org.whispersystems.signalservice.api.messages.SignalServiceEnvelope; import org.whispersystems.signalservice.api.messages.SignalServiceEnvelope;
@ -29,7 +28,7 @@ public abstract class PushReceivedJob extends ContextJob {
if (!isActiveNumber(recipient)) { if (!isActiveNumber(recipient)) {
DatabaseFactory.getRecipientDatabase(context).setRegistered(recipient, RecipientDatabase.RegisteredState.REGISTERED); DatabaseFactory.getRecipientDatabase(context).setRegistered(recipient, RecipientDatabase.RegisteredState.REGISTERED);
ApplicationContext.getInstance(context).getJobManager().add(new DirectoryRefreshJob(context, KeyCachingService.getMasterSecret(context), recipient, false)); ApplicationContext.getInstance(context).getJobManager().add(new DirectoryRefreshJob(context, recipient, false));
} }
if (envelope.isReceipt()) { if (envelope.isReceipt()) {

View File

@ -80,13 +80,13 @@ public abstract class PushSendJob extends SendJob {
return new SignalServiceAddress(address.toPhoneString(), Optional.fromNullable(relay)); return new SignalServiceAddress(address.toPhoneString(), Optional.fromNullable(relay));
} }
protected List<SignalServiceAttachment> getAttachmentsFor(MasterSecret masterSecret, List<Attachment> parts) { protected List<SignalServiceAttachment> getAttachmentsFor(List<Attachment> parts) {
List<SignalServiceAttachment> attachments = new LinkedList<>(); List<SignalServiceAttachment> attachments = new LinkedList<>();
for (final Attachment attachment : parts) { for (final Attachment attachment : parts) {
try { try {
if (attachment.getDataUri() == null || attachment.getSize() == 0) throw new IOException("Assertion failed, outgoing attachment has no data!"); if (attachment.getDataUri() == null || attachment.getSize() == 0) throw new IOException("Assertion failed, outgoing attachment has no data!");
InputStream is = PartAuthority.getAttachmentStream(context, masterSecret, attachment.getDataUri()); InputStream is = PartAuthority.getAttachmentStream(context, attachment.getDataUri());
attachments.add(SignalServiceAttachment.newStreamBuilder() attachments.add(SignalServiceAttachment.newStreamBuilder()
.withStream(is) .withStream(is)
.withContentType(attachment.getContentType()) .withContentType(attachment.getContentType())

View File

@ -7,8 +7,8 @@ import org.thoughtcrime.securesms.ApplicationContext;
import org.thoughtcrime.securesms.crypto.MasterSecret; import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.database.Address; import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.EncryptingSmsDatabase;
import org.thoughtcrime.securesms.database.NoSuchMessageException; import org.thoughtcrime.securesms.database.NoSuchMessageException;
import org.thoughtcrime.securesms.database.SmsDatabase;
import org.thoughtcrime.securesms.database.model.SmsMessageRecord; import org.thoughtcrime.securesms.database.model.SmsMessageRecord;
import org.thoughtcrime.securesms.dependencies.InjectableType; import org.thoughtcrime.securesms.dependencies.InjectableType;
import org.thoughtcrime.securesms.notifications.MessageNotifier; import org.thoughtcrime.securesms.notifications.MessageNotifier;
@ -48,8 +48,8 @@ public class PushTextSendJob extends PushSendJob implements InjectableType {
@Override @Override
public void onPushSend(MasterSecret masterSecret) throws NoSuchMessageException, RetryLaterException { public void onPushSend(MasterSecret masterSecret) throws NoSuchMessageException, RetryLaterException {
ExpiringMessageManager expirationManager = ApplicationContext.getInstance(context).getExpiringMessageManager(); ExpiringMessageManager expirationManager = ApplicationContext.getInstance(context).getExpiringMessageManager();
EncryptingSmsDatabase database = DatabaseFactory.getEncryptingSmsDatabase(context); SmsDatabase database = DatabaseFactory.getSmsDatabase(context);
SmsMessageRecord record = database.getMessage(masterSecret, messageId); SmsMessageRecord record = database.getMessage(messageId);
try { try {
Log.w(TAG, "Sending message: " + messageId); Log.w(TAG, "Sending message: " + messageId);

View File

@ -22,6 +22,7 @@ import java.util.List;
public abstract class SendJob extends MasterSecretJob { public abstract class SendJob extends MasterSecretJob {
@SuppressWarnings("unused")
private final static String TAG = SendJob.class.getSimpleName(); private final static String TAG = SendJob.class.getSimpleName();
public SendJob(Context context, JobParameters parameters) { public SendJob(Context context, JobParameters parameters) {
@ -49,8 +50,7 @@ public abstract class SendJob extends MasterSecretJob {
} }
} }
protected List<Attachment> scaleAttachments(@NonNull MasterSecret masterSecret, protected List<Attachment> scaleAttachments(@NonNull MediaConstraints constraints,
@NonNull MediaConstraints constraints,
@NonNull List<Attachment> attachments) @NonNull List<Attachment> attachments)
throws UndeliverableMessageException throws UndeliverableMessageException
{ {
@ -59,11 +59,11 @@ public abstract class SendJob extends MasterSecretJob {
for (Attachment attachment : attachments) { for (Attachment attachment : attachments) {
try { try {
if (constraints.isSatisfied(context, masterSecret, attachment)) { if (constraints.isSatisfied(context, attachment)) {
results.add(attachment); results.add(attachment);
} else if (constraints.canResize(attachment)) { } else if (constraints.canResize(attachment)) {
MediaStream resized = constraints.getResizedMedia(context, masterSecret, attachment); MediaStream resized = constraints.getResizedMedia(context, attachment);
results.add(attachmentDatabase.updateAttachmentData(masterSecret, attachment, resized)); results.add(attachmentDatabase.updateAttachmentData(attachment, resized));
} else { } else {
throw new UndeliverableMessageException("Size constraints could not be met!"); throw new UndeliverableMessageException("Size constraints could not be met!");
} }

View File

@ -6,15 +6,11 @@ import android.support.annotation.Nullable;
import android.telephony.SmsMessage; import android.telephony.SmsMessage;
import android.util.Log; import android.util.Log;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.crypto.MasterSecretUnion;
import org.thoughtcrime.securesms.crypto.MasterSecretUtil;
import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.EncryptingSmsDatabase;
import org.thoughtcrime.securesms.database.MessagingDatabase.InsertResult; import org.thoughtcrime.securesms.database.MessagingDatabase.InsertResult;
import org.thoughtcrime.securesms.database.SmsDatabase;
import org.thoughtcrime.securesms.notifications.MessageNotifier; import org.thoughtcrime.securesms.notifications.MessageNotifier;
import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.service.KeyCachingService;
import org.thoughtcrime.securesms.sms.IncomingTextMessage; import org.thoughtcrime.securesms.sms.IncomingTextMessage;
import org.whispersystems.jobqueue.JobParameters; import org.whispersystems.jobqueue.JobParameters;
import org.whispersystems.libsignal.util.guava.Optional; import org.whispersystems.libsignal.util.guava.Optional;
@ -49,21 +45,12 @@ public class SmsReceiveJob extends ContextJob {
Log.w(TAG, "onRun()"); Log.w(TAG, "onRun()");
Optional<IncomingTextMessage> message = assembleMessageFragments(pdus, subscriptionId); Optional<IncomingTextMessage> message = assembleMessageFragments(pdus, subscriptionId);
MasterSecret masterSecret = KeyCachingService.getMasterSecret(context);
MasterSecretUnion masterSecretUnion;
if (masterSecret == null) {
masterSecretUnion = new MasterSecretUnion(MasterSecretUtil.getAsymmetricMasterSecret(context, null));
} else {
masterSecretUnion = new MasterSecretUnion(masterSecret);
}
if (message.isPresent() && !isBlocked(message.get())) { if (message.isPresent() && !isBlocked(message.get())) {
Optional<InsertResult> insertResult = storeMessage(masterSecretUnion, message.get()); Optional<InsertResult> insertResult = storeMessage(message.get());
if (insertResult.isPresent()) { if (insertResult.isPresent()) {
MessageNotifier.updateNotification(context, masterSecret, insertResult.get().getThreadId()); MessageNotifier.updateNotification(context, insertResult.get().getThreadId());
} }
} else if (message.isPresent()) { } else if (message.isPresent()) {
Log.w(TAG, "*** Received blocked SMS, ignoring..."); Log.w(TAG, "*** Received blocked SMS, ignoring...");
@ -91,8 +78,8 @@ public class SmsReceiveJob extends ContextJob {
return false; return false;
} }
private Optional<InsertResult> storeMessage(MasterSecretUnion masterSecret, IncomingTextMessage message) { private Optional<InsertResult> storeMessage(IncomingTextMessage message) {
EncryptingSmsDatabase database = DatabaseFactory.getEncryptingSmsDatabase(context); SmsDatabase database = DatabaseFactory.getSmsDatabase(context);
if (message.isSecureMessage()) { if (message.isSecureMessage()) {
IncomingTextMessage placeholder = new IncomingTextMessage(message, ""); IncomingTextMessage placeholder = new IncomingTextMessage(message, "");
@ -101,7 +88,7 @@ public class SmsReceiveJob extends ContextJob {
return insertResult; return insertResult;
} else { } else {
return database.insertMessageInbox(masterSecret, message); return database.insertMessageInbox(message);
} }
} }

View File

@ -11,8 +11,8 @@ import android.util.Log;
import org.thoughtcrime.securesms.crypto.MasterSecret; import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.EncryptingSmsDatabase;
import org.thoughtcrime.securesms.database.NoSuchMessageException; import org.thoughtcrime.securesms.database.NoSuchMessageException;
import org.thoughtcrime.securesms.database.SmsDatabase;
import org.thoughtcrime.securesms.database.model.SmsMessageRecord; import org.thoughtcrime.securesms.database.model.SmsMessageRecord;
import org.thoughtcrime.securesms.jobs.requirements.MasterSecretRequirement; import org.thoughtcrime.securesms.jobs.requirements.MasterSecretRequirement;
import org.thoughtcrime.securesms.jobs.requirements.NetworkOrServiceRequirement; import org.thoughtcrime.securesms.jobs.requirements.NetworkOrServiceRequirement;
@ -43,8 +43,8 @@ public class SmsSendJob extends SendJob {
@Override @Override
public void onSend(MasterSecret masterSecret) throws NoSuchMessageException { public void onSend(MasterSecret masterSecret) throws NoSuchMessageException {
EncryptingSmsDatabase database = DatabaseFactory.getEncryptingSmsDatabase(context); SmsDatabase database = DatabaseFactory.getSmsDatabase(context);
SmsMessageRecord record = database.getMessage(masterSecret, messageId); SmsMessageRecord record = database.getMessage(messageId);
try { try {
Log.w(TAG, "Sending message: " + messageId); Log.w(TAG, "Sending message: " + messageId);

View File

@ -8,8 +8,8 @@ import android.util.Log;
import org.thoughtcrime.securesms.ApplicationContext; import org.thoughtcrime.securesms.ApplicationContext;
import org.thoughtcrime.securesms.crypto.MasterSecret; import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.EncryptingSmsDatabase;
import org.thoughtcrime.securesms.database.NoSuchMessageException; import org.thoughtcrime.securesms.database.NoSuchMessageException;
import org.thoughtcrime.securesms.database.SmsDatabase;
import org.thoughtcrime.securesms.database.model.SmsMessageRecord; import org.thoughtcrime.securesms.database.model.SmsMessageRecord;
import org.thoughtcrime.securesms.jobs.requirements.MasterSecretRequirement; import org.thoughtcrime.securesms.jobs.requirements.MasterSecretRequirement;
import org.thoughtcrime.securesms.notifications.MessageNotifier; import org.thoughtcrime.securesms.notifications.MessageNotifier;
@ -46,7 +46,7 @@ public class SmsSentJob extends MasterSecretJob {
switch (action) { switch (action) {
case SmsDeliveryListener.SENT_SMS_ACTION: case SmsDeliveryListener.SENT_SMS_ACTION:
handleSentResult(masterSecret, messageId, result); handleSentResult(messageId, result);
break; break;
case SmsDeliveryListener.DELIVERED_SMS_ACTION: case SmsDeliveryListener.DELIVERED_SMS_ACTION:
handleDeliveredResult(messageId, result); handleDeliveredResult(messageId, result);
@ -65,13 +65,13 @@ public class SmsSentJob extends MasterSecretJob {
} }
private void handleDeliveredResult(long messageId, int result) { private void handleDeliveredResult(long messageId, int result) {
DatabaseFactory.getEncryptingSmsDatabase(context).markStatus(messageId, result); DatabaseFactory.getSmsDatabase(context).markStatus(messageId, result);
} }
private void handleSentResult(MasterSecret masterSecret, long messageId, int result) { private void handleSentResult(long messageId, int result) {
try { try {
EncryptingSmsDatabase database = DatabaseFactory.getEncryptingSmsDatabase(context); SmsDatabase database = DatabaseFactory.getSmsDatabase(context);
SmsMessageRecord record = database.getMessage(masterSecret, messageId); SmsMessageRecord record = database.getMessage(messageId);
switch (result) { switch (result) {
case Activity.RESULT_OK: case Activity.RESULT_OK:

Some files were not shown because too many files have changed in this diff Show More