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.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 'org.assertj:assertj-core:1.7.1'
@ -183,6 +184,7 @@ dependencyVerification {
'com.annimon:stream:5da6e2e3e0551d61a3ea7014f04312276549e3dd739cf637996e4cf43c5535b9',
'com.takisoft.fix:colorpicker:f5d0dbabe406a1800498ca9c1faf34db36e021d8488bf10360f29961fe3ab0d1',
'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-base:0ca636a8fc9a5af45e607cdcd61783bf5d561cbbb0f862021ce69606eee5ad49',
'com.google.android.gms:play-services-tasks:69ec265168e601d0203d04cd42e34bb019b2f029aa1e16fabd38a5153eea2086',

View File

@ -1,2 +1,5 @@
-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;
public interface BindableConversationItem extends Unbindable {
void bind(@NonNull MasterSecret masterSecret,
@NonNull MessageRecord messageRecord,
void bind(@NonNull MessageRecord messageRecord,
@NonNull GlideRequests glideRequests,
@NonNull Locale locale,
@NonNull Set<MessageRecord> batchSelected,

View File

@ -11,7 +11,7 @@ import java.util.Set;
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 Set<Long> selectedThreads, boolean batchMode);
}

View File

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

View File

@ -1,4 +1,4 @@
/**
/*
* Copyright (C) 2015 Open Whisper Systems
*
* 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 org.thoughtcrime.securesms.components.ContactFilterToolbar;
import org.thoughtcrime.securesms.components.ContactFilterToolbar.OnFilterChangedListener;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.util.DirectoryHelper;
import org.thoughtcrime.securesms.util.DynamicLanguage;
@ -53,8 +52,7 @@ public abstract class ContactSelectionActivity extends PassphraseRequiredActionB
protected ContactSelectionListFragment contactsFragment;
private MasterSecret masterSecret;
private ContactFilterToolbar toolbar;
private ContactFilterToolbar toolbar;
@Override
protected void onPreCreate() {
@ -64,7 +62,6 @@ public abstract class ContactSelectionActivity extends PassphraseRequiredActionB
@Override
protected void onCreate(Bundle icicle, @NonNull MasterSecret masterSecret) {
this.masterSecret = masterSecret;
if (!getIntent().hasExtra(ContactSelectionListFragment.DISPLAY_MODE)) {
getIntent().putExtra(ContactSelectionListFragment.DISPLAY_MODE,
TextSecurePreferences.isSmsEnabled(this)
@ -94,6 +91,7 @@ public abstract class ContactSelectionActivity extends PassphraseRequiredActionB
this.toolbar = ViewUtil.findById(this, R.id.toolbar);
setSupportActionBar(toolbar);
assert getSupportActionBar() != null;
getSupportActionBar().setDisplayHomeAsUpEnabled(false);
getSupportActionBar().setDisplayShowTitleEnabled(false);
getSupportActionBar().setIcon(null);
@ -107,11 +105,7 @@ public abstract class ContactSelectionActivity extends PassphraseRequiredActionB
}
private void initializeSearch() {
toolbar.setOnFilterChangedListener(new OnFilterChangedListener() {
@Override public void onFilterChanged(String filter) {
contactsFragment.setQueryFilter(filter);
}
});
toolbar.setOnFilterChangedListener(filter -> contactsFragment.setQueryFilter(filter));
}
@Override
@ -128,19 +122,16 @@ public abstract class ContactSelectionActivity extends PassphraseRequiredActionB
private static class RefreshDirectoryTask extends AsyncTask<Context, Void, Void> {
private final WeakReference<ContactSelectionActivity> activity;
private final MasterSecret masterSecret;
private RefreshDirectoryTask(ContactSelectionActivity activity) {
this.activity = new WeakReference<>(activity);
this.masterSecret = activity.masterSecret;
this.activity = new WeakReference<>(activity);
}
@Override
protected Void doInBackground(Context... params) {
try {
DirectoryHelper.refreshDirectory(params[0], masterSecret, true);
DirectoryHelper.refreshDirectory(params[0], true);
} catch (IOException 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.mms.GlideApp;
import org.thoughtcrime.securesms.permissions.Permissions;
import org.thoughtcrime.securesms.service.KeyCachingService;
import org.thoughtcrime.securesms.util.DirectoryHelper;
import org.thoughtcrime.securesms.util.StickyHeaderDecoration;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
@ -221,7 +220,7 @@ public class ContactSelectionListFragment extends Fragment
@Override
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),
cursorFilter, getActivity().getIntent().getBooleanExtra(RECENTS, false));
}
@ -263,7 +262,7 @@ public class ContactSelectionListFragment extends Fragment
@Override
protected Boolean doInBackground(Void... voids) {
try {
DirectoryHelper.refreshDirectory(getContext(), null, false);
DirectoryHelper.refreshDirectory(getContext(), false);
return true;
} catch (IOException 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.ContactData;
import org.thoughtcrime.securesms.crypto.IdentityKeyParcelable;
import org.thoughtcrime.securesms.crypto.MasterCipher;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.crypto.SecurityEvent;
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 SMS_DEFAULT = 10;
private MasterSecret masterSecret;
private GlideRequests glideRequests;
protected ComposeText composeText;
private AnimatingToggle buttonToggle;
@ -263,7 +261,6 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
@Override
protected void onCreate(Bundle state, @NonNull MasterSecret masterSecret) {
Log.w(TAG, "onCreate()");
this.masterSecret = masterSecret;
supportRequestWindowFeature(WindowCompat.FEATURE_ACTION_BAR_OVERLAY);
setContentView(R.layout.conversation_activity);
@ -440,7 +437,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
break;
case PICK_LOCATION:
SignalPlace place = new SignalPlace(PlacePicker.getPlace(data, this));
attachmentManager.setLocation(masterSecret, place, getCurrentMediaConstraints());
attachmentManager.setLocation(place, getCurrentMediaConstraints());
break;
case PICK_GIF:
setMedia(data.getData(), MediaType.GIF);
@ -595,7 +592,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
protected Void doInBackground(Void... params) {
DatabaseFactory.getRecipientDatabase(ConversationActivity.this).setExpireMessages(recipient, expirationTime);
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;
}
@ -681,7 +678,6 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
private void handleRegisterForSignal() {
Intent intent = new Intent(this, RegistrationActivity.class);
intent.putExtra("cancel_button", true);
intent.putExtra("master_secret", masterSecret);
startActivity(intent);
}
@ -723,7 +719,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
new AsyncTask<OutgoingEndSessionMessage, Void, Long>() {
@Override
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
@ -768,7 +764,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
.build();
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)));
initializeEnabledCheck();
} catch (IOException e) {
@ -1006,9 +1002,8 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
new AsyncTask<Void, Void, List<Draft>>() {
@Override
protected List<Draft> doInBackground(Void... params) {
MasterCipher masterCipher = new MasterCipher(masterSecret);
DraftDatabase draftDatabase = DatabaseFactory.getDraftDatabase(ConversationActivity.this);
List<Draft> results = draftDatabase.getDrafts(masterCipher, threadId);
List<Draft> results = draftDatabase.getDrafts(threadId);
draftDatabase.clearDrafts(threadId);
@ -1024,7 +1019,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
composeText.setText(draft.getValue());
break;
case Draft.LOCATION:
attachmentManager.setLocation(masterSecret, SignalPlace.deserialize(draft.getValue()), getCurrentMediaConstraints());
attachmentManager.setLocation(SignalPlace.deserialize(draft.getValue()), getCurrentMediaConstraints());
break;
case Draft.IMAGE:
setMedia(Uri.parse(draft.getValue()), MediaType.IMAGE);
@ -1078,7 +1073,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
if (registeredState == RegisteredState.UNKNOWN) {
try {
Log.w(TAG, "Refreshing directory for user: " + recipient.getAddress().serialize());
registeredState = DirectoryHelper.refreshDirectoryFor(context, masterSecret, recipient);
registeredState = DirectoryHelper.refreshDirectoryFor(context, recipient);
} catch (IOException e) {
Log.w(TAG, e);
}
@ -1246,7 +1241,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
attachmentTypeSelector = null;
attachmentManager = new AttachmentManager(this, this);
audioRecorder = new AudioRecorder(this, masterSecret);
audioRecorder = new AudioRecorder(this);
SendButtonListener sendButtonListener = new SendButtonListener();
ComposeKeyPressedListener composeKeyPressedListener = new ComposeKeyPressedListener();
@ -1385,7 +1380,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
private void setMedia(@Nullable Uri uri, @NonNull MediaType mediaType) {
if (uri == null) return;
attachmentManager.setMedia(masterSecret, glideRequests, uri, mediaType, getCurrentMediaConstraints());
attachmentManager.setMedia(glideRequests, uri, mediaType, getCurrentMediaConstraints());
}
private void addAttachmentContactInfo(Uri contactUri) {
@ -1440,7 +1435,6 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
final Drafts drafts = getDraftsForCurrentState();
final long thisThreadId = this.threadId;
final MasterSecret thisMasterSecret = this.masterSecret.parcelClone();
final int thisDistributionType = this.distributionType;
new AsyncTask<Long, Void, Long>() {
@ -1453,9 +1447,9 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
if (drafts.size() > 0) {
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),
drafts.getUriSnippet(ConversationActivity.this),
drafts.getUriSnippet(),
System.currentTimeMillis(), Types.BASE_DRAFT_TYPE, true);
} else if (threadId > 0) {
threadDatabase.update(threadId, false);
@ -1588,7 +1582,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
Context context = ConversationActivity.this;
List<MarkedMessageInfo> messageIds = DatabaseFactory.getThreadDatabase(context).setRead(params[0], false);
MessageNotifier.updateNotification(context, masterSecret);
MessageNotifier.updateNotification(context);
MarkReadReceiver.process(context, messageIds);
return null;
@ -1701,7 +1695,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
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
@ -1746,7 +1740,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
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
@ -1799,7 +1793,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
@Override
public void onImageCapture(@NonNull final byte[] imageBytes) {
setMedia(PersistentBlobProvider.getInstance(this)
.create(masterSecret, imageBytes, MediaUtil.IMAGE_JPEG, null),
.create(this, imageBytes, MediaUtil.IMAGE_JPEG, null),
MediaType.IMAGE);
quickAttachmentDrawer.hide(false);
}
@ -1862,7 +1856,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... params) {
PersistentBlobProvider.getInstance(ConversationActivity.this).delete(result.first);
PersistentBlobProvider.getInstance(ConversationActivity.this).delete(ConversationActivity.this, result.first);
return null;
}
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
@ -1891,7 +1885,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... params) {
PersistentBlobProvider.getInstance(ConversationActivity.this).delete(result.first);
PersistentBlobProvider.getInstance(ConversationActivity.this).delete(ConversationActivity.this, result.first);
return null;
}
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);

View File

@ -26,13 +26,10 @@ import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnLongClickListener;
import android.view.ViewGroup;
import android.widget.TextView;
import org.thoughtcrime.securesms.ConversationAdapter.HeaderViewHolder;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.database.AttachmentDatabase;
import org.thoughtcrime.securesms.database.DatabaseFactory;
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 @Nullable ItemClickListener clickListener;
private final @NonNull MasterSecret masterSecret;
private final @NonNull GlideRequests glideRequests;
private final @NonNull Locale locale;
private final @NonNull Recipient recipient;
@ -142,7 +138,6 @@ public class ConversationAdapter <V extends View & BindableConversationItem>
ConversationAdapter(Context context, Cursor cursor) {
super(context, cursor);
try {
this.masterSecret = null;
this.glideRequests = null;
this.locale = null;
this.clickListener = null;
@ -157,7 +152,6 @@ public class ConversationAdapter <V extends View & BindableConversationItem>
}
public ConversationAdapter(@NonNull Context context,
@NonNull MasterSecret masterSecret,
@NonNull GlideRequests glideRequests,
@NonNull Locale locale,
@Nullable ItemClickListener clickListener,
@ -167,7 +161,6 @@ public class ConversationAdapter <V extends View & BindableConversationItem>
super(context, cursor);
try {
this.masterSecret = masterSecret;
this.glideRequests = glideRequests;
this.locale = locale;
this.clickListener = clickListener;
@ -193,7 +186,7 @@ public class ConversationAdapter <V extends View & BindableConversationItem>
@Override
protected void onBindItemViewHolder(ViewHolder viewHolder, @NonNull MessageRecord messageRecord) {
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));
}
@ -302,7 +295,7 @@ public class ConversationAdapter <V extends View & BindableConversationItem>
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));
return messageRecord;

View File

@ -52,7 +52,6 @@ import android.widget.Toast;
import org.thoughtcrime.securesms.ConversationAdapter.HeaderViewHolder;
import org.thoughtcrime.securesms.ConversationAdapter.ItemClickListener;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.MmsSmsDatabase;
import org.thoughtcrime.securesms.database.RecipientDatabase;
@ -92,7 +91,6 @@ public class ConversationFragment extends Fragment
private ConversationFragmentListener listener;
private MasterSecret masterSecret;
private Recipient recipient;
private long threadId;
private long lastSeen;
@ -110,12 +108,11 @@ public class ConversationFragment extends Fragment
@Override
public void onCreate(Bundle 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
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);
list = ViewUtil.findById(view, android.R.id.list);
composeDivider = ViewUtil.findById(view, R.id.compose_divider);
@ -192,7 +189,7 @@ public class ConversationFragment extends Fragment
private void initializeListAdapter() {
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.addItemDecoration(new StickyHeaderDecoration(adapter, false, false));
@ -352,7 +349,6 @@ public class ConversationFragment extends Fragment
private void handleDisplayDetails(MessageRecord message) {
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.THREAD_ID_EXTRA, threadId);
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>() {
@Override
protected Void doInBackground(MessageRecord... messageRecords) {
MessageSender.resend(context, masterSecret, messageRecords[0]);
MessageSender.resend(context, messageRecords[0]);
return null;
}
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, message);
@ -391,7 +387,7 @@ public class ConversationFragment extends Fragment
public void onClick(DialogInterface dialog, int which) {
for (Slide slide : message.getSlideDeck().getSlides()) {
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()));
return;
}

View File

@ -184,8 +184,7 @@ public class ConversationItem extends LinearLayout
}
@Override
public void bind(@NonNull MasterSecret masterSecret,
@NonNull MessageRecord messageRecord,
public void bind(@NonNull MessageRecord messageRecord,
@NonNull GlideRequests glideRequests,
@NonNull Locale locale,
@NonNull Set<MessageRecord> batchSelected,
@ -366,7 +365,7 @@ public class ConversationItem extends LinearLayout
if (documentViewStub.resolved()) documentViewStub.get().setVisibility(View.GONE);
//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().setOnLongClickListener(passthroughClickListener);
@ -389,7 +388,7 @@ public class ConversationItem extends LinearLayout
if (documentViewStub.resolved()) documentViewStub.get().setVisibility(View.GONE);
//noinspection ConstantConditions
mediaThumbnailStub.get().setImageResource(masterSecret, glideRequests,
mediaThumbnailStub.get().setImageResource(glideRequests,
((MmsMessageRecord)messageRecord).getSlideDeck().getThumbnailSlide(),
showControls, false);
mediaThumbnailStub.get().setThumbnailClickListener(new ThumbnailClickListener());
@ -564,7 +563,7 @@ public class ConversationItem extends LinearLayout
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
@ -660,7 +659,6 @@ public class ConversationItem extends LinearLayout
parent.onClick(v);
} else if (messageRecord.isFailed()) {
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.THREAD_ID_EXTRA, messageRecord.getThreadId());
intent.putExtra(MessageDetailsActivity.TYPE_EXTRA, messageRecord.isMms() ? MmsSmsDatabase.MMS_TRANSPORT : MmsSmsDatabase.SMS_TRANSPORT);

View File

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

View File

@ -25,8 +25,6 @@ import android.view.LayoutInflater;
import android.view.View;
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.DatabaseFactory;
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 final @NonNull ThreadDatabase threadDatabase;
private final @NonNull MasterSecret masterSecret;
private final @NonNull MasterCipher masterCipher;
private final @NonNull GlideRequests glideRequests;
private final @NonNull Locale locale;
private final @NonNull LayoutInflater inflater;
@ -83,7 +79,6 @@ class ConversationListAdapter extends CursorRecyclerViewAdapter<ConversationList
}
ConversationListAdapter(@NonNull Context context,
@NonNull MasterSecret masterSecret,
@NonNull GlideRequests glideRequests,
@NonNull Locale locale,
@Nullable Cursor cursor,
@ -91,8 +86,6 @@ class ConversationListAdapter extends CursorRecyclerViewAdapter<ConversationList
{
super(context, cursor);
try {
this.masterSecret = masterSecret;
this.masterCipher = new MasterCipher(masterSecret);
this.glideRequests = glideRequests;
this.threadDatabase = DatabaseFactory.getThreadDatabase(context);
this.locale = locale;
@ -142,7 +135,7 @@ class ConversationListAdapter extends CursorRecyclerViewAdapter<ConversationList
@Override
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
@ -159,10 +152,10 @@ class ConversationListAdapter extends CursorRecyclerViewAdapter<ConversationList
}
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)) {
batchSet.remove(threadId);
} else if (threadId != -1) {
@ -170,21 +163,21 @@ class ConversationListAdapter extends CursorRecyclerViewAdapter<ConversationList
}
}
public Set<Long> getBatchSelections() {
Set<Long> getBatchSelections() {
return batchSet;
}
public void initializeBatchMode(boolean toggle) {
void initializeBatchMode(boolean toggle) {
this.batchMode = toggle;
unselectAllThreads();
}
public void unselectAllThreads() {
private void unselectAllThreads() {
this.batchSet.clear();
this.notifyDataSetChanged();
}
public void selectAllThreads() {
void selectAllThreads() {
for (int i = 0; i < getItemCount(); i++) {
long threadId = getThreadRecord(getCursorAtPositionOrThrow(i)).getThreadId();
if (threadId != -1) batchSet.add(threadId);

View File

@ -221,7 +221,7 @@ public class ConversationListFragment extends Fragment
}
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);
}
@ -302,7 +302,7 @@ public class ConversationListFragment extends Fragment
@Override
protected Void doInBackground(Void... params) {
DatabaseFactory.getThreadDatabase(getActivity()).deleteConversations(selectedConversations);
MessageNotifier.updateNotification(getActivity(), masterSecret);
MessageNotifier.updateNotification(getActivity());
return null;
}
@ -519,7 +519,7 @@ public class ConversationListFragment extends Fragment
if (unreadCount > 0) {
List<MarkedMessageInfo> messageIds = DatabaseFactory.getThreadDatabase(getActivity()).setRead(threadId, false);
MessageNotifier.updateNotification(getActivity(), masterSecret);
MessageNotifier.updateNotification(getActivity());
MarkReadReceiver.process(getActivity(), messageIds);
}
}
@ -530,7 +530,7 @@ public class ConversationListFragment extends Fragment
if (unreadCount > 0) {
DatabaseFactory.getThreadDatabase(getActivity()).incrementUnread(threadId, unreadCount);
MessageNotifier.updateNotification(getActivity(), masterSecret);
MessageNotifier.updateNotification(getActivity());
}
}
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, threadId);

View File

@ -16,7 +16,6 @@
*/
package org.thoughtcrime.securesms;
import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.ColorStateList;
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.FromTextView;
import org.thoughtcrime.securesms.components.ThumbnailView;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.database.model.ThreadRecord;
import org.thoughtcrime.securesms.mms.GlideRequests;
import org.thoughtcrime.securesms.recipients.Recipient;
@ -108,7 +106,7 @@ public class ConversationListItem extends RelativeLayout
}
@Override
public void bind(@NonNull MasterSecret masterSecret, @NonNull ThreadRecord thread,
public void bind(@NonNull ThreadRecord thread,
@NonNull GlideRequests glideRequests, @NonNull Locale locale,
@NonNull Set<Long> selectedThreads, boolean batchMode)
{
@ -139,7 +137,7 @@ public class ConversationListItem extends RelativeLayout
}
setStatusIcons(thread);
setThumbnailSnippet(masterSecret, thread);
setThumbnailSnippet(thread);
setBatchState(batchMode);
setRippleColor(recipient);
setUnreadIndicator(thread);
@ -175,10 +173,10 @@ public class ConversationListItem extends RelativeLayout
return lastSeen;
}
private void setThumbnailSnippet(MasterSecret masterSecret, ThreadRecord thread) {
private void setThumbnailSnippet(ThreadRecord thread) {
if (thread.getSnippetUri() != null) {
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();
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.TextView;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.database.model.ThreadRecord;
import org.thoughtcrime.securesms.mms.GlideRequests;
import org.thoughtcrime.securesms.util.ViewUtil;
@ -40,7 +39,7 @@ public class ConversationListItemAction extends LinearLayout implements Bindable
}
@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()));
}

View File

@ -9,7 +9,6 @@ import android.support.annotation.RequiresApi;
import android.util.AttributeSet;
import android.widget.LinearLayout;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.database.model.ThreadRecord;
import org.thoughtcrime.securesms.mms.GlideRequests;
@ -40,7 +39,7 @@ public class ConversationListItemInboxZero extends LinearLayout implements Binda
}
@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 org.thoughtcrime.securesms.crypto.IdentityKeyParcelable;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.database.IdentityDatabase;
import org.thoughtcrime.securesms.database.IdentityDatabase.IdentityRecord;
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 MasterSecret masterSecret;
private Set<MessageRecord> batchSelected;
private ImageView icon;
@ -60,22 +58,20 @@ public class ConversationUpdateItem extends LinearLayout
public void onFinishInflate() {
super.onFinishInflate();
this.icon = (ImageView)findViewById(R.id.conversation_update_icon);
this.body = (TextView)findViewById(R.id.conversation_update_body);
this.date = (TextView)findViewById(R.id.conversation_update_date);
this.icon = findViewById(R.id.conversation_update_icon);
this.body = findViewById(R.id.conversation_update_body);
this.date = findViewById(R.id.conversation_update_date);
this.setOnClickListener(new InternalClickListener(null));
}
@Override
public void bind(@NonNull MasterSecret masterSecret,
@NonNull MessageRecord messageRecord,
public void bind(@NonNull MessageRecord messageRecord,
@NonNull GlideRequests glideRequests,
@NonNull Locale locale,
@NonNull Set<MessageRecord> batchSelected,
@NonNull Recipient conversationRecipient)
{
this.masterSecret = masterSecret;
this.batchSelected = batchSelected;
bind(messageRecord, locale);
@ -172,12 +168,7 @@ public class ConversationUpdateItem extends LinearLayout
@Override
public void onModified(Recipient recipient) {
Util.runOnMain(new Runnable() {
@Override
public void run() {
bind(messageRecord, locale);
}
});
Util.runOnMain(() -> bind(messageRecord, locale));
}
@Override
@ -196,7 +187,7 @@ public class ConversationUpdateItem extends LinearLayout
@Nullable private final View.OnClickListener parent;
public InternalClickListener(@Nullable View.OnClickListener parent) {
InternalClickListener(@Nullable View.OnClickListener 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 PERSISTENT_BLOBS = 317;
public static final int INTERNALIZE_CONTACTS = 317;
public static final int SQLCIPHER = 334;
private static final SortedSet<Integer> UPGRADE_VERSIONS = new TreeSet<Integer>() {{
add(NO_MORE_KEY_EXCHANGE_PREFIX_VERSION);
@ -93,6 +94,7 @@ public class DatabaseUpgradeActivity extends BaseActivity {
add(SCREENSHOTS);
add(INTERNALIZE_CONTACTS);
add(PERSISTENT_BLOBS);
add(SQLCIPHER);
}};
private MasterSecret masterSecret;
@ -106,14 +108,14 @@ public class DatabaseUpgradeActivity extends BaseActivity {
Log.w("DatabaseUpgradeActivity", "Upgrading...");
setContentView(R.layout.database_upgrade_activity);
ProgressBar indeterminateProgress = (ProgressBar)findViewById(R.id.indeterminate_progress);
ProgressBar determinateProgress = (ProgressBar)findViewById(R.id.determinate_progress);
ProgressBar indeterminateProgress = findViewById(R.id.indeterminate_progress);
ProgressBar determinateProgress = findViewById(R.id.determinate_progress);
new DatabaseUpgradeTask(indeterminateProgress, determinateProgress)
.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, VersionTracker.getLastSeenVersion(this));
} else {
VersionTracker.updateLastSeenVersion(this);
updateNotifications(this, masterSecret);
updateNotifications(this);
startActivity((Intent)getIntent().getParcelableExtra("next_intent"));
finish();
}
@ -149,11 +151,11 @@ public class DatabaseUpgradeActivity extends BaseActivity {
}
@SuppressLint("StaticFieldLeak")
private void updateNotifications(final Context context, final MasterSecret masterSecret) {
private void updateNotifications(final Context context) {
new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... params) {
MessageNotifier.updateNotification(context, masterSecret);
MessageNotifier.updateNotification(context);
return null;
}
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
@ -171,7 +173,7 @@ public class DatabaseUpgradeActivity extends BaseActivity {
private final ProgressBar indeterminateProgress;
private final ProgressBar determinateProgress;
public DatabaseUpgradeTask(ProgressBar indeterminateProgress, ProgressBar determinateProgress) {
DatabaseUpgradeTask(ProgressBar indeterminateProgress, ProgressBar determinateProgress) {
this.indeterminateProgress = indeterminateProgress;
this.determinateProgress = determinateProgress;
}
@ -278,11 +280,11 @@ public class DatabaseUpgradeActivity extends BaseActivity {
private void schedulePendingIncomingParts(Context context) {
final AttachmentDatabase attachmentDb = DatabaseFactory.getAttachmentDatabase(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.");
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();
if (attachment.hasData()) {
@ -329,7 +331,7 @@ public class DatabaseUpgradeActivity extends BaseActivity {
@Override
protected void onPostExecute(Void result) {
VersionTracker.updateLastSeenVersion(DatabaseUpgradeActivity.this);
updateNotifications(DatabaseUpgradeActivity.this, masterSecret);
updateNotifications(DatabaseUpgradeActivity.this);
startActivity((Intent)getIntent().getParcelableExtra("next_intent"));
finish();

View File

@ -18,7 +18,6 @@
package org.thoughtcrime.securesms;
import android.app.Activity;
import android.content.AsyncTaskLoader;
import android.content.Intent;
import android.graphics.Bitmap;
import android.net.Uri;
@ -102,7 +101,6 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity
private ListView lv;
private ImageView avatar;
private TextView creatingText;
private MasterSecret masterSecret;
private Bitmap avatarBmp;
@NonNull private Optional<GroupData> groupToUpdate = Optional.absent();
@ -115,8 +113,6 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity
@Override
protected void onCreate(Bundle state, @NonNull MasterSecret masterSecret) {
this.masterSecret = masterSecret;
setContentView(R.layout.group_create_activity);
//noinspection ConstantConditions
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
@ -192,12 +188,7 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity
recipientsPanel.setPanelChangeListener(this);
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.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Crop.pickImage(GroupCreateActivity.this);
}
});
avatar.setOnClickListener(view -> Crop.pickImage(GroupCreateActivity.this));
}
private void initializeExistingGroup() {
@ -252,14 +243,14 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity
return;
}
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 {
new CreateMmsGroupTask(this, masterSecret, getAdapter().getRecipients()).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
new CreateMmsGroupTask(this, getAdapter().getRecipients()).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
}
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);
}
@ -313,7 +304,7 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity
.override(AVATAR_SIZE, AVATAR_SIZE)
.into(new SimpleTarget<Bitmap>() {
@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);
}
});
@ -332,12 +323,10 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity
private static class CreateMmsGroupTask extends AsyncTask<Void,Void,GroupActionResult> {
private final GroupCreateActivity activity;
private final MasterSecret masterSecret;
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.masterSecret = masterSecret;
this.members = members;
}
@ -368,20 +357,18 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity
}
private abstract static class SignalGroupTask extends AsyncTask<Void,Void,Optional<GroupActionResult>> {
protected GroupCreateActivity activity;
protected MasterSecret masterSecret;
protected Bitmap avatar;
protected Set<Recipient> members;
protected String name;
public SignalGroupTask(GroupCreateActivity activity,
MasterSecret masterSecret,
Bitmap avatar,
String name,
Set<Recipient> members)
{
this.activity = activity;
this.masterSecret = masterSecret;
this.avatar = avatar;
this.name = name;
this.members = members;
@ -408,13 +395,13 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity
}
private static class CreateSignalGroupTask extends SignalGroupTask {
public CreateSignalGroupTask(GroupCreateActivity activity, MasterSecret masterSecret, Bitmap avatar, String name, Set<Recipient> members) {
super(activity, masterSecret, avatar, name, members);
public CreateSignalGroupTask(GroupCreateActivity activity, Bitmap avatar, String name, Set<Recipient> members) {
super(activity, avatar, name, members);
}
@Override
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
@ -434,18 +421,17 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity
private static class UpdateSignalGroupTask extends SignalGroupTask {
private String groupId;
public UpdateSignalGroupTask(GroupCreateActivity activity,
MasterSecret masterSecret, String groupId, Bitmap avatar, String name,
Set<Recipient> members)
public UpdateSignalGroupTask(GroupCreateActivity activity, String groupId,
Bitmap avatar, String name, Set<Recipient> members)
{
super(activity, masterSecret, avatar, name, members);
super(activity, avatar, name, members);
this.groupId = groupId;
}
@Override
protected Optional<GroupActionResult> doInBackground(Void... aVoid) {
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) {
return Optional.absent();
}

View File

@ -16,7 +16,6 @@ import android.view.View;
import android.view.ViewGroup;
import android.widget.Toast;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.database.NoExternalStorageException;
import org.thoughtcrime.securesms.database.PlaintextBackupExporter;
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 ERROR_IO = 2;
private MasterSecret masterSecret;
private ProgressDialog progressDialog;
@Override
public void onCreate(Bundle bundle) {
super.onCreate(bundle);
this.masterSecret = getArguments().getParcelable("master_secret");
}
@Override
@ -87,7 +84,6 @@ public class ImportExportFragment extends Fragment {
.onAllGranted(() -> {
Intent intent = new Intent(getActivity(), ApplicationMigrationService.class);
intent.setAction(ApplicationMigrationService.MIGRATE_DATABASE);
intent.putExtra("master_secret", masterSecret);
getActivity().startService(intent);
Intent nextIntent = new Intent(getActivity(), ConversationListActivity.class);
@ -185,7 +181,7 @@ public class ImportExportFragment extends Fragment {
@Override
protected Integer doInBackground(Void... params) {
try {
PlaintextBackupImporter.importPlaintextFromSd(getActivity(), masterSecret);
PlaintextBackupImporter.importPlaintextFromSd(getActivity());
return SUCCESS;
} catch (NoExternalStorageException e) {
Log.w("ImportFragment", e);
@ -212,7 +208,7 @@ public class ImportExportFragment extends Fragment {
@Override
protected Integer doInBackground(Void... params) {
try {
PlaintextBackupExporter.exportPlaintextToSd(getActivity(), masterSecret);
PlaintextBackupExporter.exportPlaintextToSd(getActivity());
return SUCCESS;
} catch (NoExternalStorageException 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);
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) {
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.ViewHolder;
import org.thoughtcrime.securesms.components.DocumentView;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.database.CursorRecyclerViewAdapter;
import org.thoughtcrime.securesms.database.MediaDatabase;
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> {
private final MasterSecret masterSecret;
private final Calendar calendar;
private final Locale locale;
public MediaDocumentsAdapter(Context context, MasterSecret masterSecret, Cursor cursor, Locale locale) {
MediaDocumentsAdapter(Context context, Cursor cursor, Locale locale) {
super(context, cursor);
this.masterSecret = masterSecret;
this.calendar = Calendar.getInstance();
this.locale = locale;
}
@ -55,7 +52,7 @@ public class MediaDocumentsAdapter extends CursorRecyclerViewAdapter<ViewHolder>
@Override
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());
if (slide != null && slide.hasDocument()) {
@ -89,7 +86,7 @@ public class MediaDocumentsAdapter extends CursorRecyclerViewAdapter<ViewHolder>
if (position < 0) return -1;
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()));
return Util.hashCode(calendar.get(Calendar.YEAR), calendar.get(Calendar.DAY_OF_YEAR));
@ -103,7 +100,7 @@ public class MediaDocumentsAdapter extends CursorRecyclerViewAdapter<ViewHolder>
@Override
public void onBindHeaderViewHolder(HeaderViewHolder viewHolder, int 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()));
}
@ -114,8 +111,8 @@ public class MediaDocumentsAdapter extends CursorRecyclerViewAdapter<ViewHolder>
public ViewHolder(View itemView) {
super(itemView);
this.documentView = (DocumentView)itemView.findViewById(R.id.document_view);
this.date = (TextView)itemView.findViewById(R.id.date);
this.documentView = itemView.findViewById(R.id.document_view);
this.date = itemView.findViewById(R.id.date);
}
}
@ -125,7 +122,7 @@ public class MediaDocumentsAdapter extends CursorRecyclerViewAdapter<ViewHolder>
HeaderViewHolder(View 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 org.thoughtcrime.securesms.components.ThumbnailView;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.MediaDatabase.MediaRecord;
import org.thoughtcrime.securesms.database.loaders.BucketedThreadMediaLoader.BucketedThreadMedia;
@ -39,10 +38,10 @@ import java.util.Locale;
class MediaGalleryAdapter extends StickyHeaderGridAdapter {
@SuppressWarnings("unused")
private static final String TAG = MediaGalleryAdapter.class.getSimpleName();
private final Context context;
private final MasterSecret masterSecret;
private final GlideRequests glideRequests;
private final Locale locale;
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)
{
this.context = context;
this.masterSecret = masterSecret;
this.glideRequests = glideRequests;
this.locale = locale;
this.media = media;
@ -105,7 +103,7 @@ class MediaGalleryAdapter extends StickyHeaderGridAdapter {
Slide slide = MediaUtil.getSlideForAttachment(context, mediaRecord.getAttachment());
if (slide != null) {
thumbnailView.setImageResource(masterSecret, glideRequests, slide, false, false);
thumbnailView.setImageResource(glideRequests, slide, false, false);
}
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.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.setHasFixedSize(true);
@ -225,7 +225,7 @@ public class MediaOverviewActivity extends PassphraseRequiredActionBarActivity
@Override
public Loader<BucketedThreadMedia> onCreateLoader(int i, Bundle bundle) {
return new BucketedThreadMediaLoader(getContext(), masterSecret, recipient.getAddress());
return new BucketedThreadMediaLoader(getContext(), recipient.getAddress());
}
@Override
@ -248,7 +248,7 @@ public class MediaOverviewActivity extends PassphraseRequiredActionBarActivity
@Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
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.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 MasterSecret masterSecret;
private ViewPager mediaPager;
private Uri initialMediaUri;
private String initialMediaType;
@ -95,7 +93,6 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity im
@SuppressWarnings("ConstantConditions")
@Override
protected void onCreate(Bundle bundle, @NonNull MasterSecret masterSecret) {
this.masterSecret = masterSecret;
this.setTheme(R.style.TextSecure_DarkTheme);
dynamicLanguage.onCreate(this);
@ -203,7 +200,7 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity im
if (conversationRecipient != null) {
getSupportLoaderManager().restartLoader(0, null, this);
} 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))
.onAnyDenied(() -> Toast.makeText(this, R.string.MediaPreviewActivity_unable_to_write_to_external_storage_without_permission, Toast.LENGTH_LONG).show())
.onAllGranted(() -> {
SaveAttachmentTask saveTask = new SaveAttachmentTask(MediaPreviewActivity.this, masterSecret);
SaveAttachmentTask saveTask = new SaveAttachmentTask(MediaPreviewActivity.this);
long saveDate = (mediaItem.date > 0) ? mediaItem.date : System.currentTimeMillis();
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) {
if (data != null) {
@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);
adapter.setActive(true);
@ -350,7 +347,6 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity im
private static class SingleItemPagerAdapter extends PagerAdapter implements MediaItemAdapter {
private final MasterSecret masterSecret;
private final GlideRequests glideRequests;
private final Window window;
private final Uri uri;
@ -359,11 +355,10 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity im
private final LayoutInflater inflater;
SingleItemPagerAdapter(@NonNull Context context, @NonNull MasterSecret masterSecret,
@NonNull GlideRequests glideRequests, @NonNull Window window,
@NonNull Uri uri, @NonNull String mediaType, long size)
SingleItemPagerAdapter(@NonNull Context context, @NonNull GlideRequests glideRequests,
@NonNull Window window, @NonNull Uri uri, @NonNull String mediaType,
long size)
{
this.masterSecret = masterSecret;
this.glideRequests = glideRequests;
this.window = window;
this.uri = uri;
@ -388,7 +383,7 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity im
MediaView mediaView = itemView.findViewById(R.id.media_view);
try {
mediaView.set(masterSecret, glideRequests, window, uri, mediaType, size, true);
mediaView.set(glideRequests, window, uri, mediaType, size, true);
} catch (IOException 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 Context context;
private final MasterSecret masterSecret;
private final GlideRequests glideRequests;
private final Window window;
private final Cursor cursor;
@ -431,12 +425,11 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity im
private boolean active;
private int autoPlayPosition;
CursorPagerAdapter(@NonNull Context context, @NonNull MasterSecret masterSecret,
@NonNull GlideRequests glideRequests, @NonNull Window window,
@NonNull Cursor cursor, int autoPlayPosition, boolean leftIsRecent)
CursorPagerAdapter(@NonNull Context context, @NonNull GlideRequests glideRequests,
@NonNull Window window, @NonNull Cursor cursor, int autoPlayPosition,
boolean leftIsRecent)
{
this.context = context.getApplicationContext();
this.masterSecret = masterSecret;
this.glideRequests = glideRequests;
this.window = window;
this.cursor = cursor;
@ -471,11 +464,11 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity im
cursor.moveToPosition(cursorPosition);
MediaRecord mediaRecord = MediaRecord.from(context, masterSecret, cursor);
MediaRecord mediaRecord = MediaRecord.from(context, cursor);
try {
//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) {
Log.w(TAG, e);
}
@ -497,7 +490,7 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity im
public MediaItem getMediaItemFor(int position) {
cursor.moveToPosition(getCursorPosition(position));
MediaRecord mediaRecord = MediaRecord.from(context, masterSecret, cursor);
MediaRecord mediaRecord = MediaRecord.from(context, cursor);
Address address = mediaRecord.getAddress();
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.database.Address;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.EncryptingSmsDatabase;
import org.thoughtcrime.securesms.database.GroupReceiptDatabase;
import org.thoughtcrime.securesms.database.GroupReceiptDatabase.GroupReceiptInfo;
import org.thoughtcrime.securesms.database.MmsDatabase;
@ -72,14 +71,12 @@ import java.util.Locale;
public class MessageDetailsActivity extends PassphraseRequiredActionBarActivity implements LoaderCallbacks<Cursor>, RecipientModifiedListener {
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 THREAD_ID_EXTRA = "thread_id";
public final static String IS_PUSH_GROUP_EXTRA = "is_push_group";
public final static String TYPE_EXTRA = "type";
public final static String ADDRESS_EXTRA = "address";
private MasterSecret masterSecret;
private GlideRequests glideRequests;
private long threadId;
private boolean isPushGroup;
@ -166,21 +163,20 @@ public class MessageDetailsActivity extends PassphraseRequiredActionBarActivity
inflater = LayoutInflater.from(this);
View header = inflater.inflate(R.layout.message_details_header, recipientsList, false);
masterSecret = getIntent().getParcelableExtra(MASTER_SECRET_EXTRA);
threadId = getIntent().getLongExtra(THREAD_ID_EXTRA, -1);
isPushGroup = getIntent().getBooleanExtra(IS_PUSH_GROUP_EXTRA, false);
glideRequests = GlideApp.with(this);
itemParent = (ViewGroup) header.findViewById(R.id.item_container);
recipientsList = (ListView ) findViewById(R.id.recipients_list);
metadataContainer = header.findViewById(R.id.metadata_container);
errorText = (TextView ) header.findViewById(R.id.error_text);
sentDate = (TextView ) header.findViewById(R.id.sent_time);
receivedContainer = header.findViewById(R.id.received_container);
receivedDate = (TextView ) header.findViewById(R.id.received_time);
transport = (TextView ) header.findViewById(R.id.transport);
toFrom = (TextView ) header.findViewById(R.id.tofrom);
expiresContainer = header.findViewById(R.id.expires_container);
expiresInText = (TextView) header.findViewById(R.id.expires_in);
itemParent = header.findViewById(R.id.item_container);
recipientsList = findViewById(R.id.recipients_list);
metadataContainer = header.findViewById(R.id.metadata_container);
errorText = header.findViewById(R.id.error_text);
sentDate = header.findViewById(R.id.sent_time);
receivedContainer = header.findViewById(R.id.received_container);
receivedDate = header.findViewById(R.id.received_time);
transport = header.findViewById(R.id.transport);
toFrom = header.findViewById(R.id.tofrom);
expiresContainer = header.findViewById(R.id.expires_container);
expiresInText = header.findViewById(R.id.expires_in);
recipientsList.setHeaderDividersEnabled(false);
recipientsList.addHeaderView(header, null, false);
}
@ -253,8 +249,8 @@ public class MessageDetailsActivity extends PassphraseRequiredActionBarActivity
toFromRes = R.string.message_details_header__from;
}
toFrom.setText(toFromRes);
conversationItem.bind(masterSecret, messageRecord, glideRequests, dynamicLanguage.getCurrentLocale(), new HashSet<>(), recipient);
recipientsList.setAdapter(new MessageDetailsRecipientAdapter(this, masterSecret, glideRequests, messageRecord, recipients, isPushGroup));
conversationItem.bind(messageRecord, glideRequests, dynamicLanguage.getCurrentLocale(), new HashSet<>(), recipient);
recipientsList.setAdapter(new MessageDetailsRecipientAdapter(this, glideRequests, messageRecord, recipients, isPushGroup));
}
private void inflateMessageViewIfAbsent(MessageRecord messageRecord) {
@ -273,12 +269,12 @@ public class MessageDetailsActivity extends PassphraseRequiredActionBarActivity
private @Nullable MessageRecord getMessageRecord(Context context, Cursor cursor, String type) {
switch (type) {
case MmsSmsDatabase.SMS_TRANSPORT:
EncryptingSmsDatabase smsDatabase = DatabaseFactory.getEncryptingSmsDatabase(context);
SmsDatabase.Reader reader = smsDatabase.readerFor(masterSecret, cursor);
SmsDatabase smsDatabase = DatabaseFactory.getSmsDatabase(context);
SmsDatabase.Reader reader = smsDatabase.readerFor(cursor);
return reader.getNext();
case MmsSmsDatabase.MMS_TRANSPORT:
MmsDatabase mmsDatabase = DatabaseFactory.getMmsDatabase(context);
MmsDatabase.Reader mmsReader = mmsDatabase.readerFor(masterSecret, cursor);
MmsDatabase.Reader mmsReader = mmsDatabase.readerFor(cursor);
return mmsReader.getNext();
default:
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.BaseAdapter;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.database.model.MessageRecord;
import org.thoughtcrime.securesms.mms.GlideRequests;
import org.thoughtcrime.securesms.recipients.Recipient;
@ -21,18 +20,16 @@ import java.util.List;
class MessageDetailsRecipientAdapter extends BaseAdapter implements AbsListView.RecyclerListener {
private final Context context;
private final MasterSecret masterSecret;
private final GlideRequests glideRequests;
private final MessageRecord record;
private final List<RecipientDeliveryStatus> members;
private final boolean isPushGroup;
MessageDetailsRecipientAdapter(@NonNull Context context, @NonNull MasterSecret masterSecret,
@NonNull GlideRequests glideRequests, @NonNull MessageRecord record,
@NonNull List<RecipientDeliveryStatus> members, boolean isPushGroup)
MessageDetailsRecipientAdapter(@NonNull Context context, @NonNull GlideRequests glideRequests,
@NonNull MessageRecord record, @NonNull List<RecipientDeliveryStatus> members,
boolean isPushGroup)
{
this.context = context;
this.masterSecret = masterSecret;
this.glideRequests = glideRequests;
this.record = record;
this.isPushGroup = isPushGroup;
@ -66,7 +63,7 @@ class MessageDetailsRecipientAdapter extends BaseAdapter implements AbsListView.
RecipientDeliveryStatus member = members.get(position);
((MessageRecipientListItem)convertView).set(masterSecret, glideRequests, record, member, isPushGroup);
((MessageRecipientListItem)convertView).set(glideRequests, record, member, isPushGroup);
return convertView;
}

View File

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

View File

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

View File

@ -131,7 +131,7 @@ public class ShareActivity extends PassphraseRequiredActionBarActivity
public void onPause() {
super.onPause();
if (!isPassingAlongMedia && resolvedExtra != null) {
PersistentBlobProvider.getInstance(this).delete(resolvedExtra);
PersistentBlobProvider.getInstance(this).delete(this, resolvedExtra);
}
if (!isFinishing()) {
finish();
@ -334,7 +334,7 @@ public class ShareActivity extends PassphraseRequiredActionBarActivity
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) {
Log.w(TAG, ioe);
return null;

View File

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

View File

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

View File

@ -4,9 +4,8 @@ import android.net.Uri;
import android.support.annotation.NonNull;
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.util.Base64;
import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.api.messages.SignalServiceAttachment;
@ -15,7 +14,7 @@ import java.util.List;
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,
@NonNull String key, @NonNull String relay,
@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<>();
if (pointers.isPresent()) {
for (SignalServiceAttachment pointer : pointers.get()) {
if (pointer.isPointer()) {
String encryptedKey = MediaKey.getEncrypted(masterSecret, pointer.asPointer().getKey());
String encodedKey = Base64.encodeBytes(pointer.asPointer().getKey());
results.add(new PointerAttachment(pointer.getContentType(),
AttachmentDatabase.TRANSFER_PROGRESS_PENDING,
pointer.asPointer().getSize().or(0),
pointer.asPointer().getFileName().orNull(),
String.valueOf(pointer.asPointer().getId()),
encryptedKey, pointer.asPointer().getRelay().orNull(),
encodedKey, pointer.asPointer().getRelay().orNull(),
pointer.asPointer().getDigest().orNull(),
pointer.asPointer().getVoiceNote()));
}

View File

@ -9,7 +9,6 @@ import android.support.annotation.NonNull;
import android.util.Log;
import android.util.Pair;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.providers.PersistentBlobProvider;
import org.thoughtcrime.securesms.util.MediaUtil;
import org.thoughtcrime.securesms.util.ThreadUtil;
@ -28,41 +27,35 @@ public class AudioRecorder {
private static final ExecutorService executor = ThreadUtil.newDynamicSingleThreadedExecutor();
private final Context context;
private final MasterSecret masterSecret;
private final PersistentBlobProvider blobProvider;
private AudioCodec audioCodec;
private Uri captureUri;
public AudioRecorder(@NonNull Context context, @NonNull MasterSecret masterSecret) {
public AudioRecorder(@NonNull Context context) {
this.context = context;
this.masterSecret = masterSecret;
this.blobProvider = PersistentBlobProvider.getInstance(context.getApplicationContext());
}
public void startRecording() {
Log.w(TAG, "startRecording()");
executor.execute(new Runnable() {
@Override
public void run() {
Log.w(TAG, "Running startRecording() + " + Thread.currentThread().getId());
try {
if (audioCodec != null) {
throw new AssertionError("We can only record once at a time.");
}
ParcelFileDescriptor fds[] = ParcelFileDescriptor.createPipe();
captureUri = blobProvider.create(masterSecret,
new ParcelFileDescriptor.AutoCloseInputStream(fds[0]),
MediaUtil.AUDIO_AAC, null, null);
audioCodec = new AudioCodec();
audioCodec.start(new ParcelFileDescriptor.AutoCloseOutputStream(fds[1]));
} catch (IOException e) {
Log.w(TAG, e);
executor.execute(() -> {
Log.w(TAG, "Running startRecording() + " + Thread.currentThread().getId());
try {
if (audioCodec != null) {
throw new AssertionError("We can only record once at a time.");
}
ParcelFileDescriptor fds[] = ParcelFileDescriptor.createPipe();
captureUri = blobProvider.create(context, new ParcelFileDescriptor.AutoCloseInputStream(fds[0]),
MediaUtil.AUDIO_AAC, null, null);
audioCodec = new AudioCodec();
audioCodec.start(new ParcelFileDescriptor.AutoCloseOutputStream(fds[1]));
} catch (IOException e) {
Log.w(TAG, e);
}
});
}
@ -72,47 +65,34 @@ public class AudioRecorder {
final SettableFuture<Pair<Uri, Long>> future = new SettableFuture<>();
executor.execute(new Runnable() {
@Override
public void run() {
if (audioCodec == null) {
sendToFuture(future, new IOException("MediaRecorder was never initialized successfully!"));
return;
}
audioCodec.stop();
try {
long size = MediaUtil.getMediaSize(context, masterSecret, captureUri);
sendToFuture(future, new Pair<>(captureUri, size));
} catch (IOException ioe) {
Log.w(TAG, ioe);
sendToFuture(future, ioe);
}
audioCodec = null;
captureUri = null;
executor.execute(() -> {
if (audioCodec == null) {
sendToFuture(future, new IOException("MediaRecorder was never initialized successfully!"));
return;
}
audioCodec.stop();
try {
long size = MediaUtil.getMediaSize(context, captureUri);
sendToFuture(future, new Pair<>(captureUri, size));
} catch (IOException ioe) {
Log.w(TAG, ioe);
sendToFuture(future, ioe);
}
audioCodec = null;
captureUri = null;
});
return future;
}
private <T> void sendToFuture(final SettableFuture<T> future, final Exception exception) {
Util.runOnMain(new Runnable() {
@Override
public void run() {
future.setException(exception);
}
});
Util.runOnMain(() -> future.setException(exception));
}
private <T> void sendToFuture(final SettableFuture<T> future, final T result) {
Util.runOnMain(new Runnable() {
@Override
public void run() {
future.set(result);
}
});
Util.runOnMain(() -> future.set(result));
}
}

View File

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

View File

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

View File

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

View File

@ -91,11 +91,11 @@ public class ThreadPhotoRailView extends FrameLayout {
@Override
public void onBindItemViewHolder(ThreadPhotoViewHolder viewHolder, @NonNull Cursor cursor) {
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());
if (slide != null) {
imageView.setImageResource(masterSecret, glideRequests, slide, false, false);
imageView.setImageResource(glideRequests, slide, false, false);
}
imageView.setOnClickListener(v -> {

View File

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

View File

@ -22,7 +22,6 @@ import com.github.chrisbanes.photoview.PhotoView;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.components.subsampling.AttachmentBitmapDecoder;
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.GlideRequests;
import org.thoughtcrime.securesms.mms.PartAuthority;
@ -61,8 +60,7 @@ public class ZoomingImageView extends FrameLayout {
}
@SuppressLint("StaticFieldLeak")
public void setImageUri(@NonNull MasterSecret masterSecret, @NonNull GlideRequests glideRequests,
@NonNull Uri uri, @NonNull String contentType)
public void setImageUri(@NonNull GlideRequests glideRequests, @NonNull Uri uri, @NonNull String contentType)
{
final Context context = getContext();
final int maxTextureSize = BitmapUtil.getMaxTextureSize();
@ -75,7 +73,7 @@ public class ZoomingImageView extends FrameLayout {
if (MediaUtil.isGif(contentType)) return null;
try {
InputStream inputStream = PartAuthority.getAttachmentStream(context, masterSecret, uri);
InputStream inputStream = PartAuthority.getAttachmentStream(context, uri);
return BitmapUtil.getDimensions(inputStream);
} catch (IOException | BitmapDecodingException e) {
Log.w(TAG, e);
@ -88,29 +86,29 @@ public class ZoomingImageView extends FrameLayout {
if (dimensions == null || (dimensions.first <= maxTextureSize && dimensions.second <= maxTextureSize)) {
Log.w(TAG, "Loading in standard image view...");
setImageViewUri(masterSecret, glideRequests, uri);
setImageViewUri(glideRequests, uri);
} else {
Log.w(TAG, "Loading in subsampling image view...");
setSubsamplingImageViewUri(masterSecret, uri);
setSubsamplingImageViewUri(uri);
}
}
}.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);
subsamplingImageView.setVisibility(View.GONE);
glideRequests.load(new DecryptableUri(masterSecret, uri))
glideRequests.load(new DecryptableUri(uri))
.diskCacheStrategy(DiskCacheStrategy.NONE)
.dontTransform()
.override(Target.SIZE_ORIGINAL, Target.SIZE_ORIGINAL)
.into(photoView);
}
private void setSubsamplingImageViewUri(@NonNull MasterSecret masterSecret, @NonNull Uri uri) {
subsamplingImageView.setBitmapDecoderFactory(new AttachmentBitmapDecoderFactory(masterSecret));
subsamplingImageView.setRegionDecoderFactory(new AttachmentRegionDecoderFactory(masterSecret));
private void setSubsamplingImageViewUri(@NonNull Uri uri) {
subsamplingImageView.setBitmapDecoderFactory(new AttachmentBitmapDecoderFactory());
subsamplingImageView.setRegionDecoderFactory(new AttachmentRegionDecoderFactory());
subsamplingImageView.setVisibility(View.VISIBLE);
photoView.setVisibility(View.GONE);
@ -124,31 +122,16 @@ public class ZoomingImageView extends FrameLayout {
}
private static class AttachmentBitmapDecoderFactory implements DecoderFactory<AttachmentBitmapDecoder> {
private final MasterSecret masterSecret;
private AttachmentBitmapDecoderFactory(MasterSecret masterSecret) {
this.masterSecret = masterSecret;
}
@Override
public AttachmentBitmapDecoder make() throws IllegalAccessException, InstantiationException {
return new AttachmentBitmapDecoder(masterSecret);
return new AttachmentBitmapDecoder();
}
}
private static class AttachmentRegionDecoderFactory implements DecoderFactory<AttachmentRegionDecoder> {
private final MasterSecret masterSecret;
private AttachmentRegionDecoderFactory(@NonNull MasterSecret masterSecret) {
this.masterSecret = masterSecret;
}
@Override
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) {
Intent intent = new Intent(context, ApplicationMigrationService.class);
intent.setAction(ApplicationMigrationService.MIGRATE_DATABASE);
intent.putExtra("master_secret", masterSecret);
context.startService(intent);
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.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Rect;
import android.net.Uri;
import android.support.annotation.NonNull;
import com.davemorrissey.labs.subscaleview.decoder.ImageDecoder;
import com.davemorrissey.labs.subscaleview.decoder.SkiaImageDecoder;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.mms.PartAuthority;
import org.thoughtcrime.securesms.service.KeyCachingService;
import java.io.InputStream;
public class AttachmentBitmapDecoder implements ImageDecoder{
private final MasterSecret masterSecret;
public AttachmentBitmapDecoder(@NonNull MasterSecret masterSecret) {
this.masterSecret = masterSecret;
}
public AttachmentBitmapDecoder() {}
@Override
public Bitmap decode(Context context, Uri uri) throws Exception {
@ -31,7 +23,7 @@ public class AttachmentBitmapDecoder implements ImageDecoder{
return new SkiaImageDecoder().decode(context, uri);
}
InputStream inputStream = PartAuthority.getAttachmentStream(context, masterSecret, uri);
InputStream inputStream = PartAuthority.getAttachmentStream(context, uri);
try {
BitmapFactory.Options options = new BitmapFactory.Options();

View File

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

View File

@ -28,16 +28,14 @@ import android.text.TextUtils;
import android.util.Log;
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.DatabaseFactory;
import org.thoughtcrime.securesms.database.RecipientDatabase;
import org.thoughtcrime.securesms.database.ThreadDatabase;
import org.thoughtcrime.securesms.database.model.ThreadRecord;
import org.thoughtcrime.securesms.permissions.Permissions;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.util.NumberUtil;
import org.thoughtcrime.securesms.permissions.Permissions;
import java.util.ArrayList;
@ -61,17 +59,14 @@ public class ContactsCursorLoader extends CursorLoader {
ContactsDatabase.CONTACT_TYPE_COLUMN};
private final MasterSecret masterSecret;
private final String filter;
private final int mode;
private final boolean recents;
public ContactsCursorLoader(@NonNull Context context, @NonNull MasterSecret masterSecret,
int mode, String filter, boolean recents)
public ContactsCursorLoader(@NonNull Context context, int mode, String filter, boolean recents)
{
super(context);
this.masterSecret = masterSecret;
this.filter = filter;
this.mode = mode;
this.recents = recents;
@ -88,7 +83,7 @@ public class ContactsCursorLoader extends CursorLoader {
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});
ThreadDatabase.Reader reader = threadDatabase.readerFor(recentConversations, new MasterCipher(masterSecret));
ThreadDatabase.Reader reader = threadDatabase.readerFor(recentConversations);
ThreadRecord threadRecord;

View File

@ -8,7 +8,6 @@ import android.content.SyncResult;
import android.os.Bundle;
import android.util.Log;
import org.thoughtcrime.securesms.service.KeyCachingService;
import org.thoughtcrime.securesms.util.DirectoryHelper;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
@ -30,7 +29,7 @@ public class ContactsSyncAdapter extends AbstractThreadedSyncAdapter {
if (TextSecurePreferences.isPushRegistered(getContext())) {
try {
DirectoryHelper.refreshDirectory(getContext(), KeyCachingService.getMasterSecret(getContext()), true);
DirectoryHelper.refreshDirectory(getContext(), true);
} catch (IOException 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;
import android.support.annotation.NonNull;
import android.util.Log;
import org.thoughtcrime.securesms.util.LimitedInputStream;
@ -37,14 +38,14 @@ import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
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 MAC_LENGTH = 20;
public static InputStream createFor(MasterSecret masterSecret, File file)
public static InputStream createFor(@NonNull AttachmentSecret attachmentSecret, @NonNull File file)
throws IOException
{
try {
@ -52,7 +53,7 @@ public class DecryptingPartInputStream {
throw new IOException("File too short");
}
verifyMac(masterSecret, file);
verifyMac(attachmentSecret, file);
FileInputStream fileStream = new FileInputStream(file);
byte[] ivBytes = new byte[IV_LENGTH];
@ -60,7 +61,7 @@ public class DecryptingPartInputStream {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
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);
} catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | InvalidAlgorithmParameterException e) {
@ -68,8 +69,8 @@ public class DecryptingPartInputStream {
}
}
private static void verifyMac(MasterSecret masterSecret, File file) throws IOException {
Mac mac = initializeMac(masterSecret.getMacKey());
private static void verifyMac(AttachmentSecret attachmentSecret, File file) throws IOException {
Mac mac = initializeMac(new SecretKeySpec(attachmentSecret.getClassicMacKey(), "HmacSHA1"));
FileInputStream macStream = new FileInputStream(file);
InputStream dataStream = new LimitedInputStream(new FileInputStream(file), file.length() - 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 static boolean hasSession(Context context, MasterSecret masterSecret, Recipient recipient) {
return hasSession(context, masterSecret, recipient.getAddress());
public static boolean hasSession(Context context, Recipient recipient) {
return hasSession(context, recipient.getAddress());
}
public static boolean hasSession(Context context, MasterSecret masterSecret, @NonNull Address address) {
SessionStore sessionStore = new TextSecureSessionStore(context, masterSecret);
public static boolean hasSession(Context context, @NonNull Address address) {
SessionStore sessionStore = new TextSecureSessionStore(context, null);
SignalProtocolAddress axolotlAddress = new SignalProtocolAddress(address.serialize(), SignalServiceAddress.DEFAULT_DEVICE_ID);
return sessionStore.containsSession(axolotlAddress);

View File

@ -1,4 +1,4 @@
/**
/*
* Copyright (C) 2011 Whisper Systems
*
* This program is free software: you can redistribute it and/or modify
@ -16,11 +16,10 @@
*/
package org.thoughtcrime.securesms.database;
import android.annotation.SuppressLint;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.graphics.Bitmap;
import android.media.MediaMetadataRetriever;
import android.net.Uri;
@ -30,17 +29,17 @@ import android.support.annotation.Nullable;
import android.support.annotation.VisibleForTesting;
import android.text.TextUtils;
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.AttachmentId;
import org.thoughtcrime.securesms.attachments.DatabaseAttachment;
import org.thoughtcrime.securesms.crypto.DecryptingPartInputStream;
import org.thoughtcrime.securesms.crypto.EncryptingPartOutputStream;
import org.thoughtcrime.securesms.crypto.MasterCipher;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.crypto.MasterSecretUnion;
import org.thoughtcrime.securesms.crypto.AttachmentSecret;
import org.thoughtcrime.securesms.crypto.ClassicDecryptingPartInputStream;
import org.thoughtcrime.securesms.crypto.ModernDecryptingPartInputStream;
import org.thoughtcrime.securesms.crypto.ModernEncryptingPartOutputStream;
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper;
import org.thoughtcrime.securesms.mms.MediaStream;
import org.thoughtcrime.securesms.mms.MmsException;
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.Util;
import org.thoughtcrime.securesms.video.EncryptedMediaDataSource;
import org.whispersystems.libsignal.InvalidMessageException;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.SecureRandom;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.Callable;
@ -83,6 +82,8 @@ public class AttachmentDatabase extends Database {
static final String DIGEST = "digest";
static final String VOICE_NOTE = "voice_note";
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_STARTED = 1;
@ -95,7 +96,8 @@ public class AttachmentDatabase extends Database {
MMS_ID, CONTENT_TYPE, NAME, CONTENT_DISPOSITION,
CONTENT_LOCATION, DATA, THUMBNAIL, TRANSFER_STATE,
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, " +
MMS_ID + " INTEGER, " + "seq" + " INTEGER DEFAULT 0, " +
@ -106,7 +108,7 @@ public class AttachmentDatabase extends Database {
TRANSFER_STATE + " INTEGER, "+ DATA + " TEXT, " + SIZE + " INTEGER, " +
FILE_NAME + " TEXT, " + THUMBNAIL + " TEXT, " + THUMBNAIL_ASPECT_RATIO + " REAL, " +
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 = {
"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();
public AttachmentDatabase(Context context, SQLiteOpenHelper databaseHelper) {
private final AttachmentSecret attachmentSecret;
public AttachmentDatabase(Context context, SQLCipherOpenHelper databaseHelper, AttachmentSecret attachmentSecret) {
super(context, databaseHelper);
this.attachmentSecret = attachmentSecret;
}
public @NonNull InputStream getAttachmentStream(MasterSecret masterSecret, AttachmentId attachmentId)
public @NonNull InputStream getAttachmentStream(AttachmentId attachmentId)
throws IOException
{
InputStream dataStream = getDataStream(masterSecret, attachmentId, DATA);
InputStream dataStream = getDataStream(attachmentId, DATA);
if (dataStream == null) throw new IOException("No stream for: " + attachmentId);
else return dataStream;
}
public @NonNull InputStream getThumbnailStream(@NonNull MasterSecret masterSecret, @NonNull AttachmentId attachmentId)
public @NonNull InputStream getThumbnailStream(@NonNull AttachmentId attachmentId)
throws IOException
{
Log.w(TAG, "getThumbnailStream(" + attachmentId + ")");
InputStream dataStream = getDataStream(masterSecret, attachmentId, THUMBNAIL);
InputStream dataStream = getDataStream(attachmentId, THUMBNAIL);
if (dataStream != null) {
return dataStream;
}
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);
else return generatedStream;
@ -162,7 +167,7 @@ public class AttachmentDatabase extends Database {
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();
Cursor cursor = null;
@ -170,7 +175,7 @@ public class AttachmentDatabase extends Database {
try {
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;
} 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();
List<DatabaseAttachment> results = new LinkedList<>();
Cursor cursor = null;
@ -189,7 +194,7 @@ public class AttachmentDatabase extends Database {
null, null, null);
while (cursor != null && cursor.moveToNext()) {
results.add(getAttachment(masterSecret, cursor));
results.add(getAttachment(cursor));
}
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 List<DatabaseAttachment> attachments = new LinkedList<>();
@ -207,7 +212,7 @@ public class AttachmentDatabase extends Database {
try {
cursor = database.query(TABLE_NAME, PROJECTION, TRANSFER_STATE + " = ?", new String[] {String.valueOf(TRANSFER_PROGRESS_STARTED)}, null, null, null);
while (cursor != null && cursor.moveToNext()) {
attachments.add(getAttachment(masterSecret, cursor));
attachments.add(getAttachment(cursor));
}
} finally {
if (cursor != null) cursor.close();
@ -217,7 +222,7 @@ public class AttachmentDatabase extends Database {
}
@SuppressWarnings("ResultOfMethodCallIgnored")
public void deleteAttachmentsForMessage(long mmsId) {
void deleteAttachmentsForMessage(long mmsId) {
SQLiteDatabase database = databaseHelper.getWritableDatabase();
Cursor cursor = null;
@ -246,7 +251,7 @@ public class AttachmentDatabase extends Database {
}
@SuppressWarnings("ResultOfMethodCallIgnored")
public void deleteAllAttachments() {
void deleteAllAttachments() {
SQLiteDatabase database = databaseHelper.getWritableDatabase();
database.delete(TABLE_NAME, null, null);
@ -258,17 +263,16 @@ public class AttachmentDatabase extends Database {
}
}
public long insertAttachmentsForPlaceholder(@NonNull MasterSecret masterSecret, long mmsId,
@NonNull AttachmentId attachmentId,
@NonNull InputStream inputStream)
public void insertAttachmentsForPlaceholder(long mmsId, @NonNull AttachmentId attachmentId, @NonNull InputStream inputStream)
throws MmsException
{
SQLiteDatabase database = databaseHelper.getWritableDatabase();
Pair<File, Long> partData = setAttachmentData(masterSecret, inputStream);
ContentValues values = new ContentValues();
SQLiteDatabase database = databaseHelper.getWritableDatabase();
DataInfo dataInfo = setAttachmentData(inputStream);
ContentValues values = new ContentValues();
values.put(DATA, partData.first.getAbsolutePath());
values.put(SIZE, partData.second);
values.put(DATA, dataInfo.file.getAbsolutePath());
values.put(SIZE, dataInfo.length);
values.put(DATA_RANDOM, dataInfo.random);
values.put(TRANSFER_STATE, TRANSFER_PROGRESS_DONE);
values.put(CONTENT_LOCATION, (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) {
//noinspection ResultOfMethodCallIgnored
partData.first.delete();
dataInfo.file.delete();
} else {
notifyConversationListeners(DatabaseFactory.getMmsDatabase(context).getThreadIdForMessage(mmsId));
notifyConversationListListeners();
}
thumbnailExecutor.submit(new ThumbnailFetchCallable(masterSecret, attachmentId));
return partData.second;
thumbnailExecutor.submit(new ThumbnailFetchCallable(attachmentId));
}
void insertAttachmentsForMessage(@NonNull MasterSecretUnion masterSecret,
long mmsId,
@NonNull List<Attachment> attachments)
void insertAttachmentsForMessage(long mmsId, @NonNull List<Attachment> attachments)
throws MmsException
{
Log.w(TAG, "insertParts(" + attachments.size() + ")");
for (Attachment attachment : attachments) {
AttachmentId attachmentId = insertAttachment(masterSecret, mmsId, attachment);
AttachmentId attachmentId = insertAttachment(mmsId, attachment);
Log.w(TAG, "Inserted attachment at ID: " + attachmentId);
}
}
public @NonNull Attachment updateAttachmentData(@NonNull MasterSecret masterSecret,
@NonNull Attachment attachment,
public @NonNull Attachment updateAttachmentData(@NonNull Attachment attachment,
@NonNull MediaStream mediaStream)
throws MmsException
{
SQLiteDatabase database = databaseHelper.getWritableDatabase();
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!");
}
long dataSize = setAttachmentData(masterSecret, dataFile, mediaStream.getStream());
dataInfo = setAttachmentData(dataInfo.file, mediaStream.getStream());
ContentValues contentValues = new ContentValues();
contentValues.put(SIZE, dataSize);
contentValues.put(SIZE, dataInfo.length);
contentValues.put(CONTENT_TYPE, mediaStream.getMimeType());
contentValues.put(DATA_RANDOM, dataInfo.random);
database.update(TABLE_NAME, contentValues, PART_ID_WHERE, databaseAttachment.getAttachmentId().toStrings());
@ -328,7 +329,7 @@ public class AttachmentDatabase extends Database {
databaseAttachment.hasThumbnail(),
mediaStream.getMimeType(),
databaseAttachment.getTransferState(),
dataSize,
dataInfo.length,
databaseAttachment.getFileName(),
databaseAttachment.getLocation(),
databaseAttachment.getKey(),
@ -339,16 +340,11 @@ public class AttachmentDatabase extends Database {
}
public void updateAttachmentFileName(@NonNull MasterSecret masterSecret,
@NonNull AttachmentId attachmentId,
public void updateAttachmentFileName(@NonNull AttachmentId attachmentId,
@Nullable String fileName)
{
SQLiteDatabase database = databaseHelper.getWritableDatabase();
if (fileName != null) {
fileName = new MasterCipher(masterSecret).encryptBody(fileName);
}
ContentValues contentValues = new ContentValues(1);
contentValues.put(FILE_NAME, fileName);
@ -382,28 +378,43 @@ public class AttachmentDatabase extends Database {
notifyConversationListeners(DatabaseFactory.getMmsDatabase(context).getThreadIdForMessage(messageId));
}
@SuppressWarnings("WeakerAccess")
@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 {
if (dataFile != null) return DecryptingPartInputStream.createFor(masterSecret, dataFile);
else return null;
if (dataInfo.random != null && dataInfo.random.length == 32) {
return ModernDecryptingPartInputStream.createFor(attachmentSecret, dataInfo.random, dataInfo.file);
} else {
return ClassicDecryptingPartInputStream.createFor(attachmentSecret, dataInfo.file);
}
} catch (IOException e) {
Log.w(TAG, e);
return null;
}
}
private @Nullable File getAttachmentDataFile(@NonNull AttachmentId attachmentId,
@NonNull String dataType)
private @Nullable DataInfo getAttachmentDataFileInfo(@NonNull AttachmentId attachmentId, @NonNull String dataType)
{
SQLiteDatabase database = databaseHelper.getReadableDatabase();
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 {
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);
if (cursor != null && cursor.moveToFirst()) {
@ -411,7 +422,9 @@ public class AttachmentDatabase extends Database {
return null;
}
return new File(cursor.getString(0));
return new DataInfo(new File(cursor.getString(0)),
cursor.getLong(1),
cursor.getBlob(2));
} else {
return null;
}
@ -422,57 +435,46 @@ public class AttachmentDatabase extends Database {
}
private @NonNull Pair<File, Long> setAttachmentData(@NonNull MasterSecret masterSecret,
@NonNull Uri uri)
private @NonNull DataInfo setAttachmentData(@NonNull Uri uri)
throws MmsException
{
try {
InputStream inputStream = PartAuthority.getAttachmentStream(context, masterSecret, uri);
return setAttachmentData(masterSecret, inputStream);
InputStream inputStream = PartAuthority.getAttachmentStream(context, uri);
return setAttachmentData(inputStream);
} catch (IOException e) {
throw new MmsException(e);
}
}
private @NonNull Pair<File, Long> setAttachmentData(@NonNull MasterSecret masterSecret,
@NonNull InputStream in)
private @NonNull DataInfo setAttachmentData(@NonNull InputStream in)
throws MmsException
{
try {
File partsDirectory = context.getDir("parts", Context.MODE_PRIVATE);
File dataFile = File.createTempFile("part", ".mms", partsDirectory);
return new Pair<>(dataFile, setAttachmentData(masterSecret, dataFile, in));
return setAttachmentData(dataFile, in);
} catch (IOException e) {
throw new MmsException(e);
}
}
private long setAttachmentData(@NonNull MasterSecret masterSecret,
@NonNull File destination,
@NonNull InputStream in)
private @NonNull DataInfo setAttachmentData(@NonNull File destination, @NonNull InputStream in)
throws MmsException
{
try {
OutputStream out = new EncryptingPartOutputStream(destination, masterSecret);
return Util.copy(in, out);
byte[] random = new byte[32];
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) {
throw new MmsException(e);
}
}
DatabaseAttachment getAttachment(@Nullable MasterSecret masterSecret, 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);
}
}
DatabaseAttachment getAttachment(@NonNull Cursor cursor) {
return new DatabaseAttachment(new AttachmentId(cursor.getLong(cursor.getColumnIndexOrThrow(ATTACHMENT_ID_ALIAS)),
cursor.getLong(cursor.getColumnIndexOrThrow(UNIQUE_ID))),
cursor.getLong(cursor.getColumnIndexOrThrow(MMS_ID)),
@ -481,7 +483,7 @@ public class AttachmentDatabase extends Database {
cursor.getString(cursor.getColumnIndexOrThrow(CONTENT_TYPE)),
cursor.getInt(cursor.getColumnIndexOrThrow(TRANSFER_STATE)),
cursor.getLong(cursor.getColumnIndexOrThrow(SIZE)),
fileName,
cursor.getString(cursor.getColumnIndexOrThrow(FILE_NAME)),
cursor.getString(cursor.getColumnIndexOrThrow(CONTENT_LOCATION)),
cursor.getString(cursor.getColumnIndexOrThrow(CONTENT_DISPOSITION)),
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
{
Log.w(TAG, "Inserting attachment for mms id: " + mmsId);
SQLiteDatabase database = databaseHelper.getWritableDatabase();
Pair<File, Long> partData = null;
long uniqueId = System.currentTimeMillis();
String fileName = null;
SQLiteDatabase database = databaseHelper.getWritableDatabase();
DataInfo dataInfo = null;
long uniqueId = System.currentTimeMillis();
if (masterSecret.getMasterSecret().isPresent() && attachment.getDataUri() != null) {
partData = setAttachmentData(masterSecret.getMasterSecret().get(), attachment.getDataUri());
Log.w(TAG, "Wrote part to file: " + partData.first.getAbsolutePath());
}
if (masterSecret.getMasterSecret().isPresent() && !TextUtils.isEmpty(attachment.getFileName())) {
fileName = new MasterCipher(masterSecret.getMasterSecret().get()).encryptBody(attachment.getFileName());
if (attachment.getDataUri() != null) {
dataInfo = setAttachmentData(attachment.getDataUri());
Log.w(TAG, "Wrote part to file: " + dataInfo.file.getAbsolutePath());
}
ContentValues contentValues = new ContentValues();
@ -519,33 +516,34 @@ public class AttachmentDatabase extends Database {
contentValues.put(DIGEST, attachment.getDigest());
contentValues.put(CONTENT_DISPOSITION, attachment.getKey());
contentValues.put(NAME, attachment.getRelay());
contentValues.put(FILE_NAME, fileName);
contentValues.put(FILE_NAME, attachment.getFileName());
contentValues.put(SIZE, attachment.getSize());
contentValues.put(FAST_PREFLIGHT_ID, attachment.getFastPreflightId());
contentValues.put(VOICE_NOTE, attachment.isVoiceNote() ? 1 : 0);
if (partData != null) {
contentValues.put(DATA, partData.first.getAbsolutePath());
contentValues.put(SIZE, partData.second);
if (dataInfo != null) {
contentValues.put(DATA, dataInfo.file.getAbsolutePath());
contentValues.put(SIZE, dataInfo.length);
contentValues.put(DATA_RANDOM, dataInfo.random);
}
long rowId = database.insert(TABLE_NAME, null, contentValues);
AttachmentId attachmentId = new AttachmentId(rowId, uniqueId);
if (partData != null) {
if (dataInfo != null) {
if (MediaUtil.hasVideoThumbnail(attachment.getDataUri())) {
Bitmap bitmap = MediaUtil.getVideoThumbnail(context, attachment.getDataUri());
if (bitmap != null) {
ThumbnailData thumbnailData = new ThumbnailData(bitmap);
updateAttachmentThumbnail(masterSecret.getMasterSecret().get(), attachmentId, thumbnailData.toDataStream(), thumbnailData.getAspectRatio());
updateAttachmentThumbnail(attachmentId, thumbnailData.toDataStream(), thumbnailData.getAspectRatio());
} else {
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 {
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
protected void updateAttachmentThumbnail(MasterSecret masterSecret, AttachmentId attachmentId, InputStream in, float aspectRatio)
protected void updateAttachmentThumbnail(AttachmentId attachmentId, InputStream in, float aspectRatio)
throws MmsException
{
Log.w(TAG, "updating part thumbnail for #" + attachmentId);
Pair<File, Long> thumbnailFile = setAttachmentData(masterSecret, in);
DataInfo thumbnailFile = setAttachmentData(in);
SQLiteDatabase database = databaseHelper.getWritableDatabase();
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_RANDOM, thumbnailFile.random);
database.update(TABLE_NAME, values, PART_ID_WHERE, attachmentId.toStrings());
@ -584,24 +584,22 @@ public class AttachmentDatabase extends Database {
@VisibleForTesting
class ThumbnailFetchCallable implements Callable<InputStream> {
private final MasterSecret masterSecret;
private final AttachmentId attachmentId;
ThumbnailFetchCallable(MasterSecret masterSecret, AttachmentId attachmentId) {
this.masterSecret = masterSecret;
ThumbnailFetchCallable(AttachmentId attachmentId) {
this.attachmentId = attachmentId;
}
@Override
public @Nullable InputStream call() throws Exception {
Log.w(TAG, "Executing thumbnail job...");
final InputStream stream = getDataStream(masterSecret, attachmentId, THUMBNAIL);
final InputStream stream = getDataStream(attachmentId, THUMBNAIL);
if (stream != null) {
return stream;
}
DatabaseAttachment attachment = getAttachment(masterSecret, attachmentId);
DatabaseAttachment attachment = getAttachment(attachmentId);
if (attachment == null || !attachment.hasData()) {
return null;
@ -610,34 +608,35 @@ public class AttachmentDatabase extends Database {
ThumbnailData data;
if (MediaUtil.isVideoType(attachment.getContentType())) {
data = generateVideoThumbnail(masterSecret, attachmentId);
data = generateVideoThumbnail(attachmentId);
} else{
data = MediaUtil.generateThumbnail(context, masterSecret, attachment.getContentType(), attachment.getDataUri());
data = MediaUtil.generateThumbnail(context, attachment.getContentType(), attachment.getDataUri());
}
if (data == 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) {
Log.w(TAG, "Video thumbnails not supported...");
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...");
return null;
}
EncryptedMediaDataSource dataSource = new EncryptedMediaDataSource(masterSecret, mediaFile);
EncryptedMediaDataSource dataSource = new EncryptedMediaDataSource(attachmentSecret, dataInfo.file, dataInfo.random, dataInfo.length);
MediaMetadataRetriever retriever = new MediaMetadataRetriever();
retriever.setDataSource(dataSource);
@ -647,4 +646,16 @@ public class AttachmentDatabase extends Database {
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.database.Cursor;
import android.database.sqlite.SQLiteOpenHelper;
import android.net.Uri;
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper;
import java.util.Set;
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_LIST_URI = "content://textsecure/conversation-list";
protected SQLiteOpenHelper databaseHelper;
protected final Context context;
protected SQLCipherOpenHelper databaseHelper;
protected final Context context;
public Database(Context context, SQLiteOpenHelper databaseHelper) {
public Database(Context context, SQLCipherOpenHelper databaseHelper) {
this.context = context;
this.databaseHelper = databaseHelper;
}
@ -58,7 +59,7 @@ public abstract class Database {
cursor.setNotificationUri(context.getContentResolver(), Uri.parse(CONVERSATION_LIST_URI));
}
public void reset(SQLiteOpenHelper databaseHelper) {
public void reset(SQLCipherOpenHelper 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.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.net.Uri;
import android.support.annotation.Nullable;
import android.util.Log;
import net.sqlcipher.database.SQLiteDatabase;
import org.thoughtcrime.securesms.R;
import org.whispersystems.libsignal.InvalidMessageException;
import org.thoughtcrime.securesms.crypto.MasterCipher;
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper;
import java.util.LinkedList;
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 + ");",
};
public DraftDatabase(Context context, SQLiteOpenHelper databaseHelper) {
public DraftDatabase(Context context, SQLCipherOpenHelper 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();
for (Draft draft : drafts) {
ContentValues values = new ContentValues(3);
values.put(THREAD_ID, threadId);
values.put(DRAFT_TYPE, masterCipher.encryptBody(draft.getType()));
values.put(DRAFT_VALUE, masterCipher.encryptBody(draft.getValue()));
values.put(DRAFT_TYPE, draft.getType());
values.put(DRAFT_VALUE, draft.getValue());
db.insert(TABLE_NAME, null, values);
}
@ -54,7 +52,7 @@ public class DraftDatabase extends Database {
db.delete(TABLE_NAME, THREAD_ID + " = ?", new String[] {threadId+""});
}
public void clearDrafts(Set<Long> threadIds) {
void clearDrafts(Set<Long> threadIds) {
SQLiteDatabase db = databaseHelper.getWritableDatabase();
StringBuilder where = new StringBuilder();
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]));
}
public void clearAllDrafts() {
void clearAllDrafts() {
SQLiteDatabase db = databaseHelper.getWritableDatabase();
db.delete(TABLE_NAME, null, null);
}
public List<Draft> getDrafts(MasterCipher masterCipher, long threadId) {
public List<Draft> getDrafts(long threadId) {
SQLiteDatabase db = databaseHelper.getReadableDatabase();
List<Draft> results = new LinkedList<Draft>();
List<Draft> results = new LinkedList<>();
Cursor cursor = null;
try {
cursor = db.query(TABLE_NAME, null, THREAD_ID + " = ?", new String[] {threadId+""}, null, null, null);
while (cursor != null && cursor.moveToNext()) {
try {
String encryptedType = cursor.getString(cursor.getColumnIndexOrThrow(DRAFT_TYPE));
String encryptedValue = cursor.getString(cursor.getColumnIndexOrThrow(DRAFT_VALUE));
String type = cursor.getString(cursor.getColumnIndexOrThrow(DRAFT_TYPE));
String value = cursor.getString(cursor.getColumnIndexOrThrow(DRAFT_VALUE));
results.add(new Draft(masterCipher.decryptBody(encryptedType),
masterCipher.decryptBody(encryptedValue)));
} catch (InvalidMessageException ime) {
Log.w("DraftDatabase", ime);
}
results.add(new Draft(type, value));
}
return results;
@ -125,7 +118,7 @@ public class DraftDatabase extends Database {
return value;
}
public String getSnippet(Context context) {
String getSnippet(Context context) {
switch (type) {
case TEXT: return value;
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);
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.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.graphics.Bitmap;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
@ -14,6 +12,9 @@ import android.text.TextUtils;
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.util.BitmapUtil;
import org.thoughtcrime.securesms.util.GroupUtil;
@ -64,7 +65,7 @@ public class GroupDatabase extends Database {
AVATAR_DIGEST + " BLOB, " +
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 + ");",
};
@ -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();
public GroupDatabase(Context context, SQLiteOpenHelper databaseHelper) {
public GroupDatabase(Context context, SQLCipherOpenHelper databaseHelper) {
super(context, databaseHelper);
}

View File

@ -4,10 +4,12 @@ package org.thoughtcrime.securesms.database;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.support.annotation.NonNull;
import net.sqlcipher.database.SQLiteDatabase;
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper;
import java.util.LinkedList;
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 + ");",
};
public GroupReceiptDatabase(Context context, SQLiteOpenHelper databaseHelper) {
public GroupReceiptDatabase(Context context, SQLCipherOpenHelper databaseHelper) {
super(context, databaseHelper);
}

View File

@ -19,12 +19,13 @@ package org.thoughtcrime.securesms.database;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import net.sqlcipher.database.SQLiteDatabase;
import org.greenrobot.eventbus.EventBus;
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper;
import org.thoughtcrime.securesms.util.Base64;
import org.whispersystems.libsignal.IdentityKey;
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);
}

View File

@ -2,14 +2,14 @@ package org.thoughtcrime.securesms.database;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import net.sqlcipher.database.SQLiteDatabase;
import org.thoughtcrime.securesms.attachments.Attachment;
import org.thoughtcrime.securesms.attachments.DatabaseAttachment;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper;
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 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);
}
@ -76,9 +76,9 @@ public class MediaDatabase extends Database {
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);
DatabaseAttachment attachment = attachmentDatabase.getAttachment(masterSecret, cursor);
DatabaseAttachment attachment = attachmentDatabase.getAttachment(cursor);
String serializedAddress = cursor.getString(cursor.getColumnIndexOrThrow(MmsDatabase.ADDRESS));
boolean outgoing = MessagingDatabase.Types.isOutgoingMessageType(cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.MESSAGE_BOX)));
Address address = null;

View File

@ -3,14 +3,15 @@ package org.thoughtcrime.securesms.database;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.text.TextUtils;
import android.util.Log;
import net.sqlcipher.database.SQLiteDatabase;
import org.thoughtcrime.securesms.database.documents.Document;
import org.thoughtcrime.securesms.database.documents.IdentityKeyMismatch;
import org.thoughtcrime.securesms.database.documents.IdentityKeyMismatchList;
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper;
import org.thoughtcrime.securesms.util.JsonUtils;
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();
public MessagingDatabase(Context context, SQLiteOpenHelper databaseHelper) {
public MessagingDatabase(Context context, SQLCipherOpenHelper databaseHelper) {
super(context, databaseHelper);
}

View File

@ -19,8 +19,6 @@ package org.thoughtcrime.securesms.database;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.net.Uri;
import android.support.annotation.NonNull;
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.PduHeaders;
import net.sqlcipher.database.SQLiteDatabase;
import org.thoughtcrime.securesms.ApplicationContext;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.attachments.Attachment;
import org.thoughtcrime.securesms.attachments.DatabaseAttachment;
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.IdentityKeyMismatchList;
import org.thoughtcrime.securesms.database.documents.NetworkFailure;
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.MediaMmsMessageRecord;
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.Util;
import org.whispersystems.jobqueue.JobManager;
import org.whispersystems.libsignal.InvalidMessageException;
import org.whispersystems.libsignal.util.guava.Optional;
import java.io.IOException;
@ -153,7 +148,7 @@ public class MmsDatabase extends MessagingDatabase {
private final JobManager jobManager;
public MmsDatabase(Context context, SQLiteOpenHelper databaseHelper) {
public MmsDatabase(Context context, SQLCipherOpenHelper databaseHelper) {
super(context, databaseHelper);
this.jobManager = ApplicationContext.getInstance(context).getJobManager();
}
@ -289,14 +284,14 @@ public class MmsDatabase extends MessagingDatabase {
return cursor;
}
public Reader getExpireStartedMessages(@Nullable MasterSecret masterSecret) {
public Reader getExpireStartedMessages() {
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";
return readerFor(masterSecret, rawQuery(where, null));
return readerFor(rawQuery(where, null));
}
private void updateMailboxBitmask(long id, long maskOff, long maskOn, Optional<Long> threadId) {
@ -494,16 +489,8 @@ public class MmsDatabase extends MessagingDatabase {
return expiring;
}
public void updateMessageBody(MasterSecretUnion masterSecret, long messageId, String body) {
body = getEncryptedBody(masterSecret, body);
long type;
if (masterSecret.getMasterSecret().isPresent()) {
type = Types.ENCRYPTION_SYMMETRIC_BIT;
} else {
type = Types.ENCRYPTION_ASYMMETRIC_BIT;
}
public void updateMessageBody(long messageId, String body) {
long type = 0;
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
{
AttachmentDatabase attachmentDatabase = DatabaseFactory.getAttachmentDatabase(context);
@ -555,13 +542,12 @@ public class MmsDatabase extends MessagingDatabase {
if (cursor != null && cursor.moveToNext()) {
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));
int subscriptionId = cursor.getInt(cursor.getColumnIndexOrThrow(SUBSCRIPTION_ID));
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 body = getDecryptedBody(masterSecret, messageText, outboxType);
long threadId = cursor.getLong(cursor.getColumnIndexOrThrow(THREAD_ID));
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 {
OutgoingMediaMessage request = getOutgoingMessage(masterSecret, messageId);
OutgoingMediaMessage request = getOutgoingMessage(messageId);
ContentValues contentValues = new ContentValues();
contentValues.put(ADDRESS, request.getRecipient().getAddress().serialize());
contentValues.put(DATE_SENT, request.getSentTimeMillis());
@ -623,8 +609,7 @@ public class MmsDatabase extends MessagingDatabase {
databaseAttachment.isVoiceNote()));
}
return insertMediaMessage(new MasterSecretUnion(masterSecret),
request.getBody(),
return insertMediaMessage(request.getBody(),
attachments,
contentValues,
null);
@ -633,8 +618,7 @@ public class MmsDatabase extends MessagingDatabase {
}
}
private Optional<InsertResult> insertMessageInbox(MasterSecretUnion masterSecret,
IncomingMediaMessage retrieved,
private Optional<InsertResult> insertMessageInbox(IncomingMediaMessage retrieved,
String contentLocation,
long threadId, long mailbox)
throws MmsException
@ -674,7 +658,7 @@ public class MmsDatabase extends MessagingDatabase {
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)) {
DatabaseFactory.getThreadDatabase(context).incrementUnread(threadId, 1);
@ -687,19 +671,12 @@ public class MmsDatabase extends MessagingDatabase {
return Optional.of(new InsertResult(messageId, threadId));
}
public Optional<InsertResult> insertMessageInbox(MasterSecretUnion masterSecret,
IncomingMediaMessage retrieved,
public Optional<InsertResult> insertMessageInbox(IncomingMediaMessage retrieved,
String contentLocation, long threadId)
throws MmsException
{
long type = Types.BASE_INBOX_TYPE;
if (masterSecret.getMasterSecret().isPresent()) {
type |= Types.ENCRYPTION_SYMMETRIC_BIT;
} else {
type |= Types.ENCRYPTION_ASYMMETRIC_BIT;
}
if (retrieved.isPushMessage()) {
type |= Types.PUSH_MESSAGE_BIT;
}
@ -708,22 +685,14 @@ public class MmsDatabase extends MessagingDatabase {
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,
IncomingMediaMessage retrieved,
long threadId)
public Optional<InsertResult> insertSecureDecryptedMessageInbox(IncomingMediaMessage retrieved, long threadId)
throws MmsException
{
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()) {
type |= Types.PUSH_MESSAGE_BIT;
}
@ -732,7 +701,7 @@ public class MmsDatabase extends MessagingDatabase {
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) {
@ -781,17 +750,13 @@ public class MmsDatabase extends MessagingDatabase {
jobManager.add(new TrimThreadJob(context, threadId));
}
public long insertMessageOutbox(@NonNull MasterSecretUnion masterSecret,
@NonNull OutgoingMediaMessage message,
public long insertMessageOutbox(@NonNull OutgoingMediaMessage message,
long threadId, boolean forceSms,
@Nullable SmsDatabase.InsertListener insertListener)
throws MmsException
{
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 (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(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()) {
List<Recipient> members = DatabaseFactory.getGroupDatabase(context).getGroupMembers(message.getRecipient().getAddress().toGroupString(), false);
@ -841,56 +806,23 @@ public class MmsDatabase extends MessagingDatabase {
return messageId;
}
private String getEncryptedBody(MasterSecretUnion masterSecret, 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,
private long insertMediaMessage(@Nullable String body,
@NonNull List<Attachment> attachments,
@NonNull ContentValues contentValues,
@Nullable SmsDatabase.InsertListener insertListener)
throws MmsException
{
SQLiteDatabase db = databaseHelper.getWritableDatabase();
AttachmentDatabase partsDatabase = DatabaseFactory.getAttachmentDatabase(context);
if (Types.isSymmetricEncryption(contentValues.getAsLong(MESSAGE_BOX)) ||
Types.isAsymmetricEncryption(contentValues.getAsLong(MESSAGE_BOX)))
{
if (!TextUtils.isEmpty(body)) {
contentValues.put(BODY, getEncryptedBody(masterSecret, body));
}
}
SQLiteDatabase db = databaseHelper.getWritableDatabase();
AttachmentDatabase partsDatabase = DatabaseFactory.getAttachmentDatabase(context);
contentValues.put(BODY, body);
contentValues.put(PART_COUNT, attachments.size());
db.beginTransaction();
try {
long messageId = db.insert(TABLE_NAME, null, contentValues);
partsDatabase.insertAttachmentsForMessage(masterSecret, messageId, attachments);
partsDatabase.insertAttachmentsForMessage(messageId, attachments);
db.setTransactionSuccessful();
return messageId;
@ -1016,8 +948,8 @@ public class MmsDatabase extends MessagingDatabase {
}
}
public Reader readerFor(MasterSecret masterSecret, Cursor cursor) {
return new Reader(masterSecret, cursor);
public Reader readerFor(Cursor cursor) {
return new Reader(cursor);
}
public OutgoingMessageReader readerFor(OutgoingMediaMessage message, long threadId) {
@ -1097,16 +1029,10 @@ public class MmsDatabase extends MessagingDatabase {
public class Reader {
private final Cursor cursor;
private final MasterSecret masterSecret;
private final MasterCipher masterCipher;
private final Cursor cursor;
public Reader(MasterSecret masterSecret, Cursor cursor) {
this.cursor = cursor;
this.masterSecret = masterSecret;
if (masterSecret != null) masterCipher = new MasterCipher(masterSecret);
else masterCipher = null;
public Reader(Cursor cursor) {
this.cursor = cursor;
}
public MessageRecord getNext() {
@ -1239,27 +1165,12 @@ public class MmsDatabase extends MessagingDatabase {
}
private DisplayRecord.Body getBody(Cursor cursor) {
try {
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);
}
} catch (InvalidMessageException e) {
Log.w("MmsDatabase", e);
return new DisplayRecord.Body(context.getString(R.string.MmsDatabase_error_decrypting_message), true);
}
String body = cursor.getString(cursor.getColumnIndexOrThrow(MmsDatabase.BODY));
return new DisplayRecord.Body(body == null ? "" : body, true);
}
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);
}

View File

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

View File

@ -1,4 +1,4 @@
/**
/*
* Copyright (C) 2011 Whisper Systems
*
* 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.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.Nullable;
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.helpers.SQLCipherOpenHelper;
import org.thoughtcrime.securesms.database.model.MessageRecord;
import org.whispersystems.libsignal.util.guava.Optional;
import java.util.HashSet;
import java.util.Set;
public class MmsSmsDatabase extends Database {
@SuppressWarnings("unused")
private static final String TAG = MmsSmsDatabase.class.getSimpleName();
public static final String TRANSPORT = "transport_type";
@ -77,7 +76,7 @@ public class MmsSmsDatabase extends Database {
AttachmentDatabase.NAME,
AttachmentDatabase.TRANSFER_STATE};
public MmsSmsDatabase(Context context, SQLiteOpenHelper databaseHelper) {
public MmsSmsDatabase(Context context, SQLCipherOpenHelper databaseHelper) {
super(context, databaseHelper);
}
@ -309,34 +308,23 @@ public class MmsSmsDatabase extends Database {
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) {
return new Reader(cursor);
}
public class Reader {
private final Cursor cursor;
private final Optional<MasterSecret> masterSecret;
private EncryptingSmsDatabase.Reader smsReader;
private MmsDatabase.Reader mmsReader;
public Reader(Cursor cursor, @Nullable MasterSecret masterSecret) {
this.cursor = cursor;
this.masterSecret = Optional.fromNullable(masterSecret);
}
private final Cursor cursor;
private SmsDatabase.Reader smsReader;
private MmsDatabase.Reader mmsReader;
public Reader(Cursor cursor) {
this(cursor, null);
this.cursor = cursor;
}
private EncryptingSmsDatabase.Reader getSmsReader() {
private SmsDatabase.Reader getSmsReader() {
if (smsReader == null) {
if (masterSecret.isPresent()) smsReader = DatabaseFactory.getEncryptingSmsDatabase(context).readerFor(masterSecret.get(), cursor);
else smsReader = DatabaseFactory.getSmsDatabase(context).readerFor(cursor);
smsReader = DatabaseFactory.getSmsDatabase(context).readerFor(cursor);
}
return smsReader;
@ -344,7 +332,7 @@ public class MmsSmsDatabase extends Database {
private MmsDatabase.Reader getMmsReader() {
if (mmsReader == null) {
mmsReader = DatabaseFactory.getMmsDatabase(context).readerFor(masterSecret.orNull(), cursor);
mmsReader = DatabaseFactory.getMmsDatabase(context).readerFor(cursor);
}
return mmsReader;

View File

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

View File

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

View File

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

View File

@ -20,9 +20,6 @@ package org.thoughtcrime.securesms.database;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.database.sqlite.SQLiteStatement;
import android.support.annotation.NonNull;
import android.text.TextUtils;
import android.util.Log;
@ -30,9 +27,13 @@ import android.util.Pair;
import com.annimon.stream.Stream;
import net.sqlcipher.database.SQLiteDatabase;
import net.sqlcipher.database.SQLiteStatement;
import org.thoughtcrime.securesms.ApplicationContext;
import org.thoughtcrime.securesms.database.documents.IdentityKeyMismatch;
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.MessageRecord;
import org.thoughtcrime.securesms.database.model.SmsMessageRecord;
@ -107,7 +108,7 @@ public class SmsDatabase extends MessagingDatabase {
private final JobManager jobManager;
public SmsDatabase(Context context, SQLiteOpenHelper databaseHelper) {
public SmsDatabase(Context context, SQLCipherOpenHelper databaseHelper) {
super(context, databaseHelper);
this.jobManager = ApplicationContext.getInstance(context).getJobManager();
}
@ -410,7 +411,17 @@ public class SmsDatabase extends MessagingDatabase {
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();
db.execSQL("UPDATE " + TABLE_NAME + " SET " + BODY + " = ?, " +
TYPE + " = (" + TYPE + " & " + (Types.TOTAL_MASK - maskOff) + " | " + maskOn + ") " +
@ -427,31 +438,33 @@ public class SmsDatabase extends MessagingDatabase {
}
public Pair<Long, Long> copyMessageInbox(long messageId) {
Reader reader = readerFor(getMessage(messageId));
SmsMessageRecord record = reader.getNext();
try {
SmsMessageRecord record = getMessage(messageId);
ContentValues contentValues = new ContentValues();
contentValues.put(TYPE, (record.getType() & ~Types.BASE_TYPE_MASK) | Types.BASE_INBOX_TYPE);
contentValues.put(ADDRESS, record.getIndividualRecipient().getAddress().serialize());
contentValues.put(ADDRESS_DEVICE_ID, record.getRecipientDeviceId());
contentValues.put(DATE_RECEIVED, System.currentTimeMillis());
contentValues.put(DATE_SENT, record.getDateSent());
contentValues.put(PROTOCOL, 31337);
contentValues.put(READ, 0);
contentValues.put(BODY, record.getBody().getBody());
contentValues.put(THREAD_ID, record.getThreadId());
contentValues.put(EXPIRES_IN, record.getExpiresIn());
ContentValues contentValues = new ContentValues();
contentValues.put(TYPE, (record.getType() & ~Types.BASE_TYPE_MASK) | Types.BASE_INBOX_TYPE);
contentValues.put(ADDRESS, record.getIndividualRecipient().getAddress().serialize());
contentValues.put(ADDRESS_DEVICE_ID, record.getRecipientDeviceId());
contentValues.put(DATE_RECEIVED, System.currentTimeMillis());
contentValues.put(DATE_SENT, record.getDateSent());
contentValues.put(PROTOCOL, 31337);
contentValues.put(READ, 0);
contentValues.put(BODY, record.getBody().getBody());
contentValues.put(THREAD_ID, record.getThreadId());
contentValues.put(EXPIRES_IN, record.getExpiresIn());
SQLiteDatabase db = databaseHelper.getWritableDatabase();
long newMessageId = db.insert(TABLE_NAME, null, contentValues);
SQLiteDatabase db = databaseHelper.getWritableDatabase();
long newMessageId = db.insert(TABLE_NAME, null, contentValues);
DatabaseFactory.getThreadDatabase(context).update(record.getThreadId(), true);
notifyConversationListeners(record.getThreadId());
DatabaseFactory.getThreadDatabase(context).update(record.getThreadId(), true);
notifyConversationListeners(record.getThreadId());
jobManager.add(new TrimThreadJob(context, record.getThreadId()));
reader.close();
return new Pair<>(newMessageId, record.getThreadId());
jobManager.add(new TrimThreadJob(context, record.getThreadId()));
return new Pair<>(newMessageId, record.getThreadId());
} catch (NoSuchMessageException e) {
throw new AssertionError(e);
}
}
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);
}
protected long insertMessageOutbox(long threadId, OutgoingTextMessage message,
long type, boolean forceSms, long date,
InsertListener insertListener)
public long insertMessageOutbox(long threadId, OutgoingTextMessage message,
boolean forceSms, long date, InsertListener insertListener)
{
long type = Types.BASE_SENDING_TYPE;
if (message.isKeyExchange()) type |= Types.KEY_EXCHANGE_BIT;
else if (message.isSecureMessage()) type |= (Types.SECURE_MESSAGE_BIT | Types.PUSH_MESSAGE_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);
}
public Cursor getMessage(long messageId) {
public SmsMessageRecord getMessage(long messageId) throws NoSuchMessageException {
SQLiteDatabase db = databaseHelper.getReadableDatabase();
Cursor cursor = db.query(TABLE_NAME, MESSAGE_PROJECTION, ID_WHERE, new String[]{messageId + ""},
null, null, null);
setNotifyConverationListeners(cursor, getThreadIdForMessage(messageId));
return cursor;
Cursor cursor = db.query(TABLE_NAME, MESSAGE_PROJECTION, ID_WHERE, new String[]{messageId + ""}, null, null, null);
Reader reader = new Reader(cursor);
SmsMessageRecord record = reader.getNext();
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) {

View File

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

View File

@ -21,21 +21,19 @@ import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.MergeCursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.net.Uri;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.text.TextUtils;
import android.util.Log;
import com.annimon.stream.Stream;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.crypto.MasterCipher;
import net.sqlcipher.database.SQLiteDatabase;
import org.thoughtcrime.securesms.database.GroupDatabase.GroupRecord;
import org.thoughtcrime.securesms.database.MessagingDatabase.MarkedMessageInfo;
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.MediaMmsMessageRecord;
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.TextSecurePreferences;
import org.thoughtcrime.securesms.util.Util;
import org.whispersystems.libsignal.InvalidMessageException;
import org.whispersystems.libsignal.util.Pair;
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, " +
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 archived_count_index ON " + TABLE_NAME + " (" + ARCHIVED + ", " + MESSAGE_COUNT + ");",
};
@ -109,7 +106,7 @@ public class ThreadDatabase extends Database {
Stream.of(GroupDatabase.TYPED_GROUP_PROJECTION))
.toList();
public ThreadDatabase(Context context, SQLiteOpenHelper databaseHelper) {
public ThreadDatabase(Context context, SQLCipherOpenHelper databaseHelper) {
super(context, databaseHelper);
}
@ -617,8 +614,8 @@ public class ThreadDatabase extends Database {
void onProgress(int complete, int total);
}
public Reader readerFor(Cursor cursor, MasterCipher masterCipher) {
return new Reader(cursor, masterCipher);
public Reader readerFor(Cursor cursor) {
return new Reader(cursor);
}
public static class DistributionTypes {
@ -631,12 +628,10 @@ public class ThreadDatabase extends Database {
public class Reader {
private final Cursor cursor;
private final MasterCipher masterCipher;
private final Cursor cursor;
public Reader(Cursor cursor, MasterCipher masterCipher) {
this.cursor = cursor;
this.masterCipher = masterCipher;
public Reader(Cursor cursor) {
this.cursor = cursor;
}
public ThreadRecord getNext() {
@ -686,21 +681,7 @@ public class ThreadDatabase extends Database {
}
private DisplayRecord.Body getPlaintextBody(Cursor cursor) {
try {
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);
}
return new DisplayRecord.Body(cursor.getString(cursor.getColumnIndexOrThrow(SNIPPET)), true);
}
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 org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.MediaDatabase;
@ -28,15 +27,14 @@ import java.util.Map;
public class BucketedThreadMediaLoader extends AsyncTaskLoader<BucketedThreadMediaLoader.BucketedThreadMedia> {
@SuppressWarnings("unused")
private static final String TAG = BucketedThreadMediaLoader.class.getSimpleName();
private final MasterSecret masterSecret;
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);
this.masterSecret = masterSecret;
this.address = address;
this.address = address;
onContentChanged();
}
@ -60,7 +58,7 @@ public class BucketedThreadMediaLoader extends AsyncTaskLoader<BucketedThreadMed
try (Cursor cursor = DatabaseFactory.getMediaDatabase(getContext()).getGalleryMediaForThread(threadId)) {
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() {
switch (type) {
case MmsSmsDatabase.SMS_TRANSPORT:
return DatabaseFactory.getEncryptingSmsDatabase(context).getMessage(messageId);
return DatabaseFactory.getSmsDatabase(context).getMessageCursor(messageId);
case MmsSmsDatabase.MMS_TRANSPORT:
return DatabaseFactory.getMmsDatabase(context).getMessage(messageId);
default:

View File

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

View File

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

View File

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

View File

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

View File

@ -6,9 +6,7 @@ import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.Log;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.service.KeyCachingService;
import org.thoughtcrime.securesms.util.DirectoryHelper;
import org.whispersystems.jobqueue.JobParameters;
import org.whispersystems.jobqueue.requirements.NetworkRequirement;
@ -19,15 +17,13 @@ import java.io.IOException;
public class DirectoryRefreshJob extends ContextJob {
@Nullable private transient Recipient recipient;
@Nullable private transient MasterSecret masterSecret;
private transient boolean notifyOfNewUsers;
public DirectoryRefreshJob(@NonNull Context context, boolean notifyOfNewUsers) {
this(context, null, null, notifyOfNewUsers);
this(context, null, notifyOfNewUsers);
}
public DirectoryRefreshJob(@NonNull Context context,
@Nullable MasterSecret masterSecret,
@Nullable Recipient recipient,
boolean notifyOfNewUsers)
{
@ -37,7 +33,6 @@ public class DirectoryRefreshJob extends ContextJob {
.create());
this.recipient = recipient;
this.masterSecret = masterSecret;
this.notifyOfNewUsers = notifyOfNewUsers;
}
@ -53,9 +48,9 @@ public class DirectoryRefreshJob extends ContextJob {
try {
wakeLock.acquire();
if (recipient == null) {
DirectoryHelper.refreshDirectory(context, KeyCachingService.getMasterSecret(context), notifyOfNewUsers);
DirectoryHelper.refreshDirectory(context, notifyOfNewUsers);
} else {
DirectoryHelper.refreshDirectoryFor(context, masterSecret, recipient);
DirectoryHelper.refreshDirectoryFor(context, recipient);
}
} finally {
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.UriAttachment;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.crypto.MasterSecretUnion;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.AttachmentDatabase;
import org.thoughtcrime.securesms.database.DatabaseFactory;
@ -50,6 +49,8 @@ import java.util.concurrent.TimeUnit;
public class MmsDownloadJob extends MasterSecretJob {
private static final long serialVersionUID = 1L;
private static final String TAG = MmsDownloadJob.class.getSimpleName();
private final long messageId;
@ -74,7 +75,7 @@ public class MmsDownloadJob extends MasterSecretJob {
public void onAdded() {
if (automatic && KeyCachingService.getMasterSecret(context) == null) {
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");
}
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) {
Log.w(TAG, e);
handleDownloadError(masterSecret, messageId, threadId, MmsDatabase.Status.DOWNLOAD_APN_UNAVAILABLE,
handleDownloadError(messageId, threadId, MmsDatabase.Status.DOWNLOAD_APN_UNAVAILABLE,
automatic);
} catch (MmsException e) {
Log.w(TAG, e);
handleDownloadError(masterSecret, messageId, threadId,
handleDownloadError(messageId, threadId,
MmsDatabase.Status.DOWNLOAD_HARD_FAILURE,
automatic);
} catch (MmsRadioException | IOException e) {
Log.w(TAG, e);
handleDownloadError(masterSecret, messageId, threadId,
handleDownloadError(messageId, threadId,
MmsDatabase.Status.DOWNLOAD_SOFT_FAILURE,
automatic);
} catch (DuplicateMessageException e) {
@ -157,7 +158,7 @@ public class MmsDownloadJob extends MasterSecretJob {
if (automatic) {
database.markIncomingNotificationReceived(threadId);
MessageNotifier.updateNotification(context, null, threadId);
MessageNotifier.updateNotification(context, threadId);
}
}
@ -166,7 +167,7 @@ public class MmsDownloadJob extends MasterSecretJob {
return false;
}
private void storeRetrievedMms(MasterSecret masterSecret, String contentLocation,
private void storeRetrievedMms(String contentLocation,
long messageId, long threadId, RetrieveConf retrieved,
int subscriptionId, @Nullable Address notificationFrom)
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);
Optional<InsertResult> insertResult = database.insertMessageInbox(new MasterSecretUnion(masterSecret),
message, contentLocation, threadId);
Optional<InsertResult> insertResult = database.insertMessageInbox(message, contentLocation, threadId);
if (insertResult.isPresent()) {
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,
int downloadStatus, boolean automatic)
private void handleDownloadError(long messageId, long threadId, int downloadStatus, boolean automatic)
{
MmsDatabase db = DatabaseFactory.getMmsDatabase(context);
@ -247,7 +246,7 @@ public class MmsDownloadJob extends MasterSecretJob {
if (automatic) {
db.markIncomingNotificationReceived(threadId);
MessageNotifier.updateNotification(context, masterSecret, threadId);
MessageNotifier.updateNotification(context, threadId);
}
}
}

View File

@ -76,10 +76,10 @@ public class MmsSendJob extends SendJob {
@Override
public void onSend(MasterSecret masterSecret) throws MmsException, NoSuchMessageException, IOException {
MmsDatabase database = DatabaseFactory.getMmsDatabase(context);
OutgoingMediaMessage message = database.getOutgoingMessage(masterSecret, messageId);
OutgoingMediaMessage message = database.getOutgoingMessage(messageId);
try {
SendReq pdu = constructSendPdu(masterSecret, message);
SendReq pdu = constructSendPdu(message);
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
{
SendReq req = new SendReq();
String lineNumber = getMyNumber(context);
Address destination = message.getRecipient().getAddress();
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)) {
req.setFrom(new EncodedStringValue(lineNumber));
@ -241,7 +241,7 @@ public class MmsSendJob extends SendJob {
int index = fileName.lastIndexOf(".");
String contentId = (index == -1) ? fileName : fileName.substring(0, index);
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);
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.PointerAttachment;
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.storage.SignalProtocolStoreImpl;
import org.thoughtcrime.securesms.crypto.storage.TextSecureSessionStore;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.EncryptingSmsDatabase;
import org.thoughtcrime.securesms.database.GroupDatabase;
import org.thoughtcrime.securesms.database.MessagingDatabase;
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.notifications.MessageNotifier;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.service.KeyCachingService;
import org.thoughtcrime.securesms.service.WebRtcCallService;
import org.thoughtcrime.securesms.sms.IncomingEncryptedMessage;
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.util.guava.Optional;
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.SignalServiceDataMessage;
import org.whispersystems.signalservice.api.messages.SignalServiceEnvelope;
@ -124,18 +118,11 @@ public class PushDecryptJob extends ContextJob {
return;
}
MasterSecret masterSecret = KeyCachingService.getMasterSecret(context);
PushDatabase database = DatabaseFactory.getPushDatabase(context);
SignalServiceEnvelope envelope = database.get(messageId);
Optional<Long> optionalSmsMessageId = smsMessageId > 0 ? Optional.of(smsMessageId) :
Optional.<Long>absent();
Optional<Long> optionalSmsMessageId = smsMessageId > 0 ? Optional.of(smsMessageId) : Optional.absent();
MasterSecretUnion masterSecretUnion;
if (masterSecret == null) masterSecretUnion = new MasterSecretUnion(MasterSecretUtil.getAsymmetricMasterSecret(context, null));
else masterSecretUnion = new MasterSecretUnion(masterSecret);
handleMessage(masterSecretUnion, envelope, optionalSmsMessageId);
handleMessage(envelope, optionalSmsMessageId);
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 {
GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context);
SignalProtocolStore axolotlStore = new SignalProtocolStoreImpl(context);
@ -161,11 +148,11 @@ public class PushDecryptJob extends ContextJob {
if (content.getDataMessage().isPresent()) {
SignalServiceDataMessage message = content.getDataMessage().get();
if (message.isEndSession()) handleEndSessionMessage(masterSecret, envelope, message, smsMessageId);
else if (message.isGroupUpdate()) handleGroupMessage(masterSecret, envelope, message, smsMessageId);
else if (message.isExpirationUpdate()) handleExpirationUpdate(masterSecret, envelope, message, smsMessageId);
else if (message.getAttachments().isPresent()) handleMediaMessage(masterSecret, envelope, message, smsMessageId);
else if (message.getBody().isPresent()) handleTextMessage(masterSecret, envelope, message, smsMessageId);
if (message.isEndSession()) handleEndSessionMessage(envelope, message, smsMessageId);
else if (message.isGroupUpdate()) handleGroupMessage(envelope, message, smsMessageId);
else if (message.isExpirationUpdate()) handleExpirationUpdate(envelope, message, smsMessageId);
else if (message.getAttachments().isPresent()) handleMediaMessage(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))) {
handleUnknownGroupMessage(envelope, message.getGroupInfo().get());
@ -177,10 +164,10 @@ public class PushDecryptJob extends ContextJob {
} else if (content.getSyncMessage().isPresent()) {
SignalServiceSyncMessage syncMessage = content.getSyncMessage().get();
if (syncMessage.getSent().isPresent()) handleSynchronizeSentMessage(masterSecret, envelope, syncMessage.getSent().get());
else if (syncMessage.getRequest().isPresent()) handleSynchronizeRequestMessage(masterSecret, syncMessage.getRequest().get());
else if (syncMessage.getRead().isPresent()) handleSynchronizeReadMessage(masterSecret, syncMessage.getRead().get(), envelope.getTimestamp());
else if (syncMessage.getVerified().isPresent()) handleSynchronizeVerifiedMessage(masterSecret, syncMessage.getVerified().get());
if (syncMessage.getSent().isPresent()) handleSynchronizeSentMessage(envelope, syncMessage.getSent().get());
else if (syncMessage.getRequest().isPresent()) handleSynchronizeRequestMessage(syncMessage.getRequest().get());
else if (syncMessage.getRead().isPresent()) handleSynchronizeReadMessage(syncMessage.getRead().get(), envelope.getTimestamp());
else if (syncMessage.getVerified().isPresent()) handleSynchronizeVerifiedMessage(syncMessage.getVerified().get());
else Log.w(TAG, "Contains no known sync types...");
} else if (content.getCallMessage().isPresent()) {
Log.w(TAG, "Got call message...");
@ -205,22 +192,22 @@ public class PushDecryptJob extends ContextJob {
}
} catch (InvalidVersionException e) {
Log.w(TAG, e);
handleInvalidVersionMessage(masterSecret, envelope, smsMessageId);
handleInvalidVersionMessage(envelope, smsMessageId);
} catch (InvalidMessageException | InvalidKeyIdException | InvalidKeyException | MmsException e) {
Log.w(TAG, e);
handleCorruptMessage(masterSecret, envelope, smsMessageId);
handleCorruptMessage(envelope, smsMessageId);
} catch (NoSessionException e) {
Log.w(TAG, e);
handleNoSessionMessage(masterSecret, envelope, smsMessageId);
handleNoSessionMessage(envelope, smsMessageId);
} catch (LegacyMessageException e) {
Log.w(TAG, e);
handleLegacyMessage(masterSecret, envelope, smsMessageId);
handleLegacyMessage(envelope, smsMessageId);
} catch (DuplicateMessageException e) {
Log.w(TAG, e);
handleDuplicateMessage(masterSecret, envelope, smsMessageId);
handleDuplicateMessage(envelope, smsMessageId);
} catch (UntrustedIdentityException e) {
Log.w(TAG, e);
handleUntrustedIdentityMessage(masterSecret, envelope, smsMessageId);
handleUntrustedIdentityMessage(envelope, smsMessageId);
}
}
@ -304,22 +291,21 @@ public class PushDecryptJob extends ContextJob {
context.startService(intent);
}
private void handleEndSessionMessage(@NonNull MasterSecretUnion masterSecret,
@NonNull SignalServiceEnvelope envelope,
private void handleEndSessionMessage(@NonNull SignalServiceEnvelope envelope,
@NonNull SignalServiceDataMessage message,
@NonNull Optional<Long> smsMessageId)
{
EncryptingSmsDatabase smsDatabase = DatabaseFactory.getEncryptingSmsDatabase(context);
IncomingTextMessage incomingTextMessage = new IncomingTextMessage(Address.fromExternal(context, envelope.getSource()),
envelope.getSourceDevice(),
message.getTimestamp(),
"", Optional.<SignalServiceGroup>absent(), 0);
SmsDatabase smsDatabase = DatabaseFactory.getSmsDatabase(context);
IncomingTextMessage incomingTextMessage = new IncomingTextMessage(Address.fromExternal(context, envelope.getSource()),
envelope.getSourceDevice(),
message.getTimestamp(),
"", Optional.absent(), 0);
Long threadId;
if (!smsMessageId.isPresent()) {
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();
else threadId = null;
@ -333,14 +319,13 @@ public class PushDecryptJob extends ContextJob {
sessionStore.deleteAllSessions(envelope.getSource());
SecurityEvent.broadcastSecurityUpdateEvent(context);
MessageNotifier.updateNotification(context, masterSecret.getMasterSecret().orNull(), threadId);
MessageNotifier.updateNotification(context, threadId);
}
}
private long handleSynchronizeSentEndSessionMessage(@NonNull MasterSecretUnion masterSecret,
@NonNull SentTranscriptMessage message)
private long handleSynchronizeSentEndSessionMessage(@NonNull SentTranscriptMessage message)
{
EncryptingSmsDatabase database = DatabaseFactory.getEncryptingSmsDatabase(context);
SmsDatabase database = DatabaseFactory.getSmsDatabase(context);
Recipient recipient = getSyncMessageDestination(message);
OutgoingTextMessage outgoingTextMessage = new OutgoingTextMessage(recipient, "", -1);
OutgoingEndSessionMessage outgoingEndSessionMessage = new OutgoingEndSessionMessage(outgoingTextMessage);
@ -353,20 +338,20 @@ public class PushDecryptJob extends ContextJob {
SecurityEvent.broadcastSecurityUpdateEvent(context);
long messageId = database.insertMessageOutbox(masterSecret, threadId, outgoingEndSessionMessage,
false, message.getTimestamp(), null);
long messageId = database.insertMessageOutbox(threadId, outgoingEndSessionMessage,
false, message.getTimestamp(),
null);
database.markAsSent(messageId, true);
}
return threadId;
}
private void handleGroupMessage(@NonNull MasterSecretUnion masterSecret,
@NonNull SignalServiceEnvelope envelope,
private void handleGroupMessage(@NonNull SignalServiceEnvelope envelope,
@NonNull SignalServiceDataMessage message,
@NonNull Optional<Long> smsMessageId)
{
GroupMessageProcessor.process(context, masterSecret, envelope, message, false);
GroupMessageProcessor.process(context, envelope, message, false);
if (smsMessageId.isPresent()) {
DatabaseFactory.getSmsDatabase(context).deleteMessage(smsMessageId.get());
@ -381,25 +366,23 @@ public class PushDecryptJob extends ContextJob {
.add(new RequestGroupInfoJob(context, envelope.getSource(), group.getGroupId()));
}
private void handleExpirationUpdate(@NonNull MasterSecretUnion masterSecret,
@NonNull SignalServiceEnvelope envelope,
private void handleExpirationUpdate(@NonNull SignalServiceEnvelope envelope,
@NonNull SignalServiceDataMessage message,
@NonNull Optional<Long> smsMessageId)
throws MmsException
{
MmsDatabase database = DatabaseFactory.getMmsDatabase(context);
Recipient recipient = getMessageDestination(envelope, message);
IncomingMediaMessage mediaMessage = new IncomingMediaMessage(masterSecret,
Address.fromExternal(context, envelope.getSource()),
IncomingMediaMessage mediaMessage = new IncomingMediaMessage(Address.fromExternal(context, envelope.getSource()),
message.getTimestamp(), -1,
message.getExpiresInSeconds() * 1000, true,
Optional.fromNullable(envelope.getRelay()),
Optional.<String>absent(), message.getGroupInfo(),
Optional.<List<SignalServiceAttachment>>absent());
Optional.absent(), message.getGroupInfo(),
Optional.absent());
database.insertSecureDecryptedMessageInbox(masterSecret, mediaMessage, -1);
database.insertSecureDecryptedMessageInbox(mediaMessage, -1);
DatabaseFactory.getRecipientDatabase(context).setExpireMessages(recipient, message.getExpiresInSeconds());
@ -408,14 +391,11 @@ public class PushDecryptJob extends ContextJob {
}
}
private void handleSynchronizeVerifiedMessage(@NonNull MasterSecretUnion masterSecret,
@NonNull VerifiedMessage verifiedMessage)
{
IdentityUtil.processVerifiedMessage(context, masterSecret, verifiedMessage);
private void handleSynchronizeVerifiedMessage(@NonNull VerifiedMessage verifiedMessage) {
IdentityUtil.processVerifiedMessage(context, verifiedMessage);
}
private void handleSynchronizeSentMessage(@NonNull MasterSecretUnion masterSecret,
@NonNull SignalServiceEnvelope envelope,
private void handleSynchronizeSentMessage(@NonNull SignalServiceEnvelope envelope,
@NonNull SentTranscriptMessage message)
throws MmsException
{
@ -424,15 +404,15 @@ public class PushDecryptJob extends ContextJob {
Long threadId;
if (message.getMessage().isEndSession()) {
threadId = handleSynchronizeSentEndSessionMessage(masterSecret, message);
threadId = handleSynchronizeSentEndSessionMessage(message);
} 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()) {
threadId = handleSynchronizeSentExpirationUpdate(masterSecret, message);
threadId = handleSynchronizeSentExpirationUpdate(message);
} else if (message.getMessage().getAttachments().isPresent()) {
threadId = handleSynchronizeSentMediaMessage(masterSecret, message);
threadId = handleSynchronizeSentMediaMessage(message);
} else {
threadId = handleSynchronizeSentTextMessage(masterSecret, message);
threadId = handleSynchronizeSentTextMessage(message);
}
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) {
DatabaseFactory.getThreadDatabase(getContext()).setRead(threadId, true);
MessageNotifier.updateNotification(getContext(), masterSecret.getMasterSecret().orNull());
MessageNotifier.updateNotification(getContext());
}
MessageNotifier.setLastDesktopActivityTimestamp(message.getTimestamp());
}
private void handleSynchronizeRequestMessage(@NonNull MasterSecretUnion masterSecret,
@NonNull RequestMessage message)
private void handleSynchronizeRequestMessage(@NonNull RequestMessage message)
{
if (message.isContactsRequest()) {
ApplicationContext.getInstance(context)
@ -487,9 +466,7 @@ public class PushDecryptJob extends ContextJob {
}
}
private void handleSynchronizeReadMessage(@NonNull MasterSecretUnion masterSecret,
@NonNull List<ReadMessage> readMessages,
long envelopeTimestamp)
private void handleSynchronizeReadMessage(@NonNull List<ReadMessage> readMessages, long envelopeTimestamp)
{
for (ReadMessage readMessage : readMessages) {
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.cancelDelayedNotifications();
MessageNotifier.updateNotification(context, masterSecret.getMasterSecret().orNull());
MessageNotifier.updateNotification(context);
}
private void handleMediaMessage(@NonNull MasterSecretUnion masterSecret,
@NonNull SignalServiceEnvelope envelope,
private void handleMediaMessage(@NonNull SignalServiceEnvelope envelope,
@NonNull SignalServiceDataMessage message,
@NonNull Optional<Long> smsMessageId)
throws MmsException
{
MmsDatabase database = DatabaseFactory.getMmsDatabase(context);
Recipient recipient = getMessageDestination(envelope, message);
IncomingMediaMessage mediaMessage = new IncomingMediaMessage(masterSecret,
Address.fromExternal(context, envelope.getSource()),
IncomingMediaMessage mediaMessage = new IncomingMediaMessage(Address.fromExternal(context, envelope.getSource()),
message.getTimestamp(), -1,
message.getExpiresInSeconds() * 1000, false,
Optional.fromNullable(envelope.getRelay()),
@ -531,36 +506,29 @@ public class PushDecryptJob extends ContextJob {
message.getAttachments());
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()) {
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) {
ApplicationContext.getInstance(context)
.getJobManager()
.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()) {
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,
@NonNull SentTranscriptMessage message)
private long handleSynchronizeSentExpirationUpdate(@NonNull SentTranscriptMessage message)
throws MmsException
{
MmsDatabase database = DatabaseFactory.getMmsDatabase(context);
@ -571,7 +539,7 @@ public class PushDecryptJob extends ContextJob {
message.getMessage().getExpiresInSeconds() * 1000);
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);
@ -580,14 +548,13 @@ public class PushDecryptJob extends ContextJob {
return threadId;
}
private long handleSynchronizeSentMediaMessage(@NonNull MasterSecretUnion masterSecret,
@NonNull SentTranscriptMessage message)
private long handleSynchronizeSentMediaMessage(@NonNull SentTranscriptMessage message)
throws MmsException
{
MmsDatabase database = DatabaseFactory.getMmsDatabase(context);
Recipient recipients = getSyncMessageDestination(message);
OutgoingMediaMessage mediaMessage = new OutgoingMediaMessage(recipients, message.getMessage().getBody().orNull(),
PointerAttachment.forPointers(masterSecret, message.getMessage().getAttachments()),
PointerAttachment.forPointers(message.getMessage().getAttachments()),
message.getTimestamp(), -1,
message.getMessage().getExpiresInSeconds() * 1000,
ThreadDatabase.DistributionTypes.DEFAULT);
@ -595,15 +562,15 @@ public class PushDecryptJob extends ContextJob {
mediaMessage = new OutgoingSecureMediaMessage(mediaMessage);
if (recipients.getExpireMessages() != message.getMessage().getExpiresInSeconds()) {
handleSynchronizeSentExpirationUpdate(masterSecret, message);
handleSynchronizeSentExpirationUpdate(message);
}
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);
for (DatabaseAttachment attachment : DatabaseFactory.getAttachmentDatabase(context).getAttachmentsForMessage(null, messageId)) {
for (DatabaseAttachment attachment : DatabaseFactory.getAttachmentDatabase(context).getAttachmentsForMessage(messageId)) {
ApplicationContext.getInstance(context)
.getJobManager()
.add(new AttachmentDownloadJob(context, messageId, attachment.getAttachmentId(), false));
@ -621,24 +588,23 @@ public class PushDecryptJob extends ContextJob {
return threadId;
}
private void handleTextMessage(@NonNull MasterSecretUnion masterSecret,
@NonNull SignalServiceEnvelope envelope,
private void handleTextMessage(@NonNull SignalServiceEnvelope envelope,
@NonNull SignalServiceDataMessage message,
@NonNull Optional<Long> smsMessageId)
throws MmsException
{
EncryptingSmsDatabase database = DatabaseFactory.getEncryptingSmsDatabase(context);
String body = message.getBody().isPresent() ? message.getBody().get() : "";
Recipient recipient = getMessageDestination(envelope, message);
SmsDatabase database = DatabaseFactory.getSmsDatabase(context);
String body = message.getBody().isPresent() ? message.getBody().get() : "";
Recipient recipient = getMessageDestination(envelope, message);
if (message.getExpiresInSeconds() != recipient.getExpireMessages()) {
handleExpirationUpdate(masterSecret, envelope, message, Optional.<Long>absent());
handleExpirationUpdate(envelope, message, Optional.absent());
}
Long threadId;
if (smsMessageId.isPresent() && !message.getGroupInfo().isPresent()) {
threadId = database.updateBundleMessageBody(masterSecret, smsMessageId.get(), body).second;
threadId = database.updateBundleMessageBody(smsMessageId.get(), body).second;
} else {
IncomingTextMessage textMessage = new IncomingTextMessage(Address.fromExternal(context, envelope.getSource()),
envelope.getSourceDevice(),
@ -647,7 +613,7 @@ public class PushDecryptJob extends ContextJob {
message.getExpiresInSeconds() * 1000);
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();
else threadId = null;
@ -656,12 +622,11 @@ public class PushDecryptJob extends ContextJob {
}
if (threadId != null) {
MessageNotifier.updateNotification(context, masterSecret.getMasterSecret().orNull(), threadId);
MessageNotifier.updateNotification(context, threadId);
}
}
private long handleSynchronizeSentTextMessage(@NonNull MasterSecretUnion masterSecret,
@NonNull SentTranscriptMessage message)
private long handleSynchronizeSentTextMessage(@NonNull SentTranscriptMessage message)
throws MmsException
{
@ -670,7 +635,7 @@ public class PushDecryptJob extends ContextJob {
long expiresInMillis = message.getMessage().getExpiresInSeconds() * 1000;
if (recipient.getExpireMessages() != message.getMessage().getExpiresInSeconds()) {
handleSynchronizeSentExpirationUpdate(masterSecret, message);
handleSynchronizeSentExpirationUpdate(message);
}
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 = 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);
} else {
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);
}
@ -703,80 +668,76 @@ public class PushDecryptJob extends ContextJob {
return threadId;
}
private void handleInvalidVersionMessage(@NonNull MasterSecretUnion masterSecret,
@NonNull SignalServiceEnvelope envelope,
private void handleInvalidVersionMessage(@NonNull SignalServiceEnvelope envelope,
@NonNull Optional<Long> smsMessageId)
{
EncryptingSmsDatabase smsDatabase = DatabaseFactory.getEncryptingSmsDatabase(context);
SmsDatabase smsDatabase = DatabaseFactory.getSmsDatabase(context);
if (!smsMessageId.isPresent()) {
Optional<InsertResult> insertResult = insertPlaceholder(envelope);
if (insertResult.isPresent()) {
smsDatabase.markAsInvalidVersionKeyExchange(insertResult.get().getMessageId());
MessageNotifier.updateNotification(context, masterSecret.getMasterSecret().orNull(), insertResult.get().getThreadId());
MessageNotifier.updateNotification(context, insertResult.get().getThreadId());
}
} else {
smsDatabase.markAsInvalidVersionKeyExchange(smsMessageId.get());
}
}
private void handleCorruptMessage(@NonNull MasterSecretUnion masterSecret,
@NonNull SignalServiceEnvelope envelope,
private void handleCorruptMessage(@NonNull SignalServiceEnvelope envelope,
@NonNull Optional<Long> smsMessageId)
{
EncryptingSmsDatabase smsDatabase = DatabaseFactory.getEncryptingSmsDatabase(context);
SmsDatabase smsDatabase = DatabaseFactory.getSmsDatabase(context);
if (!smsMessageId.isPresent()) {
Optional<InsertResult> insertResult = insertPlaceholder(envelope);
if (insertResult.isPresent()) {
smsDatabase.markAsDecryptFailed(insertResult.get().getMessageId());
MessageNotifier.updateNotification(context, masterSecret.getMasterSecret().orNull(), insertResult.get().getThreadId());
MessageNotifier.updateNotification(context, insertResult.get().getThreadId());
}
} else {
smsDatabase.markAsDecryptFailed(smsMessageId.get());
}
}
private void handleNoSessionMessage(@NonNull MasterSecretUnion masterSecret,
@NonNull SignalServiceEnvelope envelope,
private void handleNoSessionMessage(@NonNull SignalServiceEnvelope envelope,
@NonNull Optional<Long> smsMessageId)
{
EncryptingSmsDatabase smsDatabase = DatabaseFactory.getEncryptingSmsDatabase(context);
SmsDatabase smsDatabase = DatabaseFactory.getSmsDatabase(context);
if (!smsMessageId.isPresent()) {
Optional<InsertResult> insertResult = insertPlaceholder(envelope);
if (insertResult.isPresent()) {
smsDatabase.markAsNoSession(insertResult.get().getMessageId());
MessageNotifier.updateNotification(context, masterSecret.getMasterSecret().orNull(), insertResult.get().getThreadId());
MessageNotifier.updateNotification(context, insertResult.get().getThreadId());
}
} else {
smsDatabase.markAsNoSession(smsMessageId.get());
}
}
private void handleLegacyMessage(@NonNull MasterSecretUnion masterSecret,
@NonNull SignalServiceEnvelope envelope,
private void handleLegacyMessage(@NonNull SignalServiceEnvelope envelope,
@NonNull Optional<Long> smsMessageId)
{
EncryptingSmsDatabase smsDatabase = DatabaseFactory.getEncryptingSmsDatabase(context);
SmsDatabase smsDatabase = DatabaseFactory.getSmsDatabase(context);
if (!smsMessageId.isPresent()) {
Optional<InsertResult> insertResult = insertPlaceholder(envelope);
if (insertResult.isPresent()) {
smsDatabase.markAsLegacyVersion(insertResult.get().getMessageId());
MessageNotifier.updateNotification(context, masterSecret.getMasterSecret().orNull(), insertResult.get().getThreadId());
MessageNotifier.updateNotification(context, insertResult.get().getThreadId());
}
} else {
smsDatabase.markAsLegacyVersion(smsMessageId.get());
}
}
private void handleDuplicateMessage(@NonNull MasterSecretUnion masterSecret,
@NonNull SignalServiceEnvelope envelope,
@SuppressWarnings("unused")
private void handleDuplicateMessage(@NonNull SignalServiceEnvelope envelope,
@NonNull Optional<Long> smsMessageId)
{
// Let's start ignoring these now
@ -791,33 +752,32 @@ public class PushDecryptJob extends ContextJob {
// }
}
private void handleUntrustedIdentityMessage(@NonNull MasterSecretUnion masterSecret,
@NonNull SignalServiceEnvelope envelope,
private void handleUntrustedIdentityMessage(@NonNull SignalServiceEnvelope envelope,
@NonNull Optional<Long> smsMessageId)
{
try {
EncryptingSmsDatabase database = DatabaseFactory.getEncryptingSmsDatabase(context);
Address sourceAddress = Address.fromExternal(context, envelope.getSource());
byte[] serialized = envelope.hasLegacyMessage() ? envelope.getLegacyMessage() : envelope.getContent();
PreKeySignalMessage whisperMessage = new PreKeySignalMessage(serialized);
IdentityKey identityKey = whisperMessage.getIdentityKey();
String encoded = Base64.encodeBytes(serialized);
SmsDatabase database = DatabaseFactory.getSmsDatabase(context);
Address sourceAddress = Address.fromExternal(context, envelope.getSource());
byte[] serialized = envelope.hasLegacyMessage() ? envelope.getLegacyMessage() : envelope.getContent();
PreKeySignalMessage whisperMessage = new PreKeySignalMessage(serialized);
IdentityKey identityKey = whisperMessage.getIdentityKey();
String encoded = Base64.encodeBytes(serialized);
IncomingTextMessage textMessage = new IncomingTextMessage(sourceAddress,
envelope.getSourceDevice(),
envelope.getTimestamp(), encoded,
Optional.<SignalServiceGroup>absent(), 0);
Optional.absent(), 0);
if (!smsMessageId.isPresent()) {
IncomingPreKeyBundleMessage bundleMessage = new IncomingPreKeyBundleMessage(textMessage, encoded, envelope.hasLegacyMessage());
Optional<InsertResult> insertResult = database.insertMessageInbox(masterSecret, bundleMessage);
Optional<InsertResult> insertResult = database.insertMessageInbox(bundleMessage);
if (insertResult.isPresent()) {
database.setMismatchedIdentity(insertResult.get().getMessageId(), sourceAddress, identityKey);
MessageNotifier.updateNotification(context, masterSecret.getMasterSecret().orNull(), insertResult.get().getThreadId());
MessageNotifier.updateNotification(context, insertResult.get().getThreadId());
}
} else {
database.updateMessageBody(masterSecret, smsMessageId.get(), encoded);
database.updateMessageBody(smsMessageId.get(), encoded);
database.markAsPreKeyBundle(smsMessageId.get());
database.setMismatchedIdentity(smsMessageId.get(), sourceAddress, identityKey);
}
@ -863,11 +823,11 @@ public class PushDecryptJob extends ContextJob {
}
private Optional<InsertResult> insertPlaceholder(@NonNull SignalServiceEnvelope envelope) {
EncryptingSmsDatabase database = DatabaseFactory.getEncryptingSmsDatabase(context);
IncomingTextMessage textMessage = new IncomingTextMessage(Address.fromExternal(context, envelope.getSource()),
envelope.getSourceDevice(),
envelope.getTimestamp(), "",
Optional.<SignalServiceGroup>absent(), 0);
SmsDatabase database = DatabaseFactory.getSmsDatabase(context);
IncomingTextMessage textMessage = new IncomingTextMessage(Address.fromExternal(context, envelope.getSource()),
envelope.getSourceDevice(),
envelope.getTimestamp(), "",
Optional.absent(), 0);
textMessage = new IncomingEncryptedMessage(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.database.Address;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.GroupReceiptDatabase;
import org.thoughtcrime.securesms.database.GroupReceiptDatabase.GroupReceiptInfo;
import org.thoughtcrime.securesms.database.MmsDatabase;
import org.thoughtcrime.securesms.database.NoSuchMessageException;
@ -82,10 +81,10 @@ public class PushGroupSendJob extends PushSendJob implements InjectableType {
throws MmsException, IOException, NoSuchMessageException
{
MmsDatabase database = DatabaseFactory.getMmsDatabase(context);
OutgoingMediaMessage message = database.getOutgoingMessage(masterSecret, messageId);
OutgoingMediaMessage message = database.getOutgoingMessage(messageId);
try {
deliver(masterSecret, message, filterAddress == null ? null : Address.fromSerialized(filterAddress));
deliver(message, filterAddress == null ? null : Address.fromSerialized(filterAddress));
database.markAsSent(messageId, true);
markAttachmentsUploaded(messageId, message.getAttachments());
@ -135,7 +134,7 @@ public class PushGroupSendJob extends PushSendJob implements InjectableType {
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,
EncapsulatedExceptions, UndeliverableMessageException
{
@ -143,8 +142,8 @@ public class PushGroupSendJob extends PushSendJob implements InjectableType {
Optional<byte[]> profileKey = getProfileKey(message.getRecipient());
List<Address> recipients = getGroupMessageRecipients(groupId, messageId);
MediaConstraints mediaConstraints = MediaConstraints.getPushMediaConstraints();
List<Attachment> scaledAttachments = scaleAttachments(masterSecret, mediaConstraints, message.getAttachments());
List<SignalServiceAttachment> attachmentStreams = getAttachmentsFor(masterSecret, scaledAttachments);
List<Attachment> scaledAttachments = scaleAttachments(mediaConstraints, message.getAttachments());
List<SignalServiceAttachment> attachmentStreams = getAttachmentsFor(scaledAttachments);
List<SignalServiceAddress> addresses;

View File

@ -59,10 +59,10 @@ public class PushMediaSendJob extends PushSendJob implements InjectableType {
{
ExpiringMessageManager expirationManager = ApplicationContext.getInstance(context).getExpiringMessageManager();
MmsDatabase database = DatabaseFactory.getMmsDatabase(context);
OutgoingMediaMessage message = database.getOutgoingMessage(masterSecret, messageId);
OutgoingMediaMessage message = database.getOutgoingMessage(messageId);
try {
deliver(masterSecret, message);
deliver(message);
database.markAsSent(messageId, true);
markAttachmentsUploaded(messageId, message.getAttachments());
@ -97,7 +97,7 @@ public class PushMediaSendJob extends PushSendJob implements InjectableType {
notifyMediaMessageDeliveryFailed(context, messageId);
}
private void deliver(MasterSecret masterSecret, OutgoingMediaMessage message)
private void deliver(OutgoingMediaMessage message)
throws RetryLaterException, InsecureFallbackApprovalException, UntrustedIdentityException,
UndeliverableMessageException
{
@ -108,8 +108,8 @@ public class PushMediaSendJob extends PushSendJob implements InjectableType {
try {
SignalServiceAddress address = getPushAddress(message.getRecipient().getAddress());
MediaConstraints mediaConstraints = MediaConstraints.getPushMediaConstraints();
List<Attachment> scaledAttachments = scaleAttachments(masterSecret, mediaConstraints, message.getAttachments());
List<SignalServiceAttachment> attachmentStreams = getAttachmentsFor(masterSecret, scaledAttachments);
List<Attachment> scaledAttachments = scaleAttachments(mediaConstraints, message.getAttachments());
List<SignalServiceAttachment> attachmentStreams = getAttachmentsFor(scaledAttachments);
Optional<byte[]> profileKey = getProfileKey(message.getRecipient());
SignalServiceDataMessage mediaMessage = SignalServiceDataMessage.newBuilder()
.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.RecipientDatabase;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.service.KeyCachingService;
import org.whispersystems.jobqueue.JobManager;
import org.whispersystems.jobqueue.JobParameters;
import org.whispersystems.signalservice.api.messages.SignalServiceEnvelope;
@ -29,7 +28,7 @@ public abstract class PushReceivedJob extends ContextJob {
if (!isActiveNumber(recipient)) {
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()) {

View File

@ -80,13 +80,13 @@ public abstract class PushSendJob extends SendJob {
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<>();
for (final Attachment attachment : parts) {
try {
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()
.withStream(is)
.withContentType(attachment.getContentType())

View File

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

View File

@ -22,6 +22,7 @@ import java.util.List;
public abstract class SendJob extends MasterSecretJob {
@SuppressWarnings("unused")
private final static String TAG = SendJob.class.getSimpleName();
public SendJob(Context context, JobParameters parameters) {
@ -49,8 +50,7 @@ public abstract class SendJob extends MasterSecretJob {
}
}
protected List<Attachment> scaleAttachments(@NonNull MasterSecret masterSecret,
@NonNull MediaConstraints constraints,
protected List<Attachment> scaleAttachments(@NonNull MediaConstraints constraints,
@NonNull List<Attachment> attachments)
throws UndeliverableMessageException
{
@ -59,11 +59,11 @@ public abstract class SendJob extends MasterSecretJob {
for (Attachment attachment : attachments) {
try {
if (constraints.isSatisfied(context, masterSecret, attachment)) {
if (constraints.isSatisfied(context, attachment)) {
results.add(attachment);
} else if (constraints.canResize(attachment)) {
MediaStream resized = constraints.getResizedMedia(context, masterSecret, attachment);
results.add(attachmentDatabase.updateAttachmentData(masterSecret, attachment, resized));
MediaStream resized = constraints.getResizedMedia(context, attachment);
results.add(attachmentDatabase.updateAttachmentData(attachment, resized));
} else {
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.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.EncryptingSmsDatabase;
import org.thoughtcrime.securesms.database.MessagingDatabase.InsertResult;
import org.thoughtcrime.securesms.database.SmsDatabase;
import org.thoughtcrime.securesms.notifications.MessageNotifier;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.service.KeyCachingService;
import org.thoughtcrime.securesms.sms.IncomingTextMessage;
import org.whispersystems.jobqueue.JobParameters;
import org.whispersystems.libsignal.util.guava.Optional;
@ -48,22 +44,13 @@ public class SmsReceiveJob extends ContextJob {
public void onRun() {
Log.w(TAG, "onRun()");
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);
}
Optional<IncomingTextMessage> message = assembleMessageFragments(pdus, subscriptionId);
if (message.isPresent() && !isBlocked(message.get())) {
Optional<InsertResult> insertResult = storeMessage(masterSecretUnion, message.get());
Optional<InsertResult> insertResult = storeMessage(message.get());
if (insertResult.isPresent()) {
MessageNotifier.updateNotification(context, masterSecret, insertResult.get().getThreadId());
MessageNotifier.updateNotification(context, insertResult.get().getThreadId());
}
} else if (message.isPresent()) {
Log.w(TAG, "*** Received blocked SMS, ignoring...");
@ -91,8 +78,8 @@ public class SmsReceiveJob extends ContextJob {
return false;
}
private Optional<InsertResult> storeMessage(MasterSecretUnion masterSecret, IncomingTextMessage message) {
EncryptingSmsDatabase database = DatabaseFactory.getEncryptingSmsDatabase(context);
private Optional<InsertResult> storeMessage(IncomingTextMessage message) {
SmsDatabase database = DatabaseFactory.getSmsDatabase(context);
if (message.isSecureMessage()) {
IncomingTextMessage placeholder = new IncomingTextMessage(message, "");
@ -101,7 +88,7 @@ public class SmsReceiveJob extends ContextJob {
return insertResult;
} 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.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.EncryptingSmsDatabase;
import org.thoughtcrime.securesms.database.NoSuchMessageException;
import org.thoughtcrime.securesms.database.SmsDatabase;
import org.thoughtcrime.securesms.database.model.SmsMessageRecord;
import org.thoughtcrime.securesms.jobs.requirements.MasterSecretRequirement;
import org.thoughtcrime.securesms.jobs.requirements.NetworkOrServiceRequirement;
@ -43,8 +43,8 @@ public class SmsSendJob extends SendJob {
@Override
public void onSend(MasterSecret masterSecret) throws NoSuchMessageException {
EncryptingSmsDatabase database = DatabaseFactory.getEncryptingSmsDatabase(context);
SmsMessageRecord record = database.getMessage(masterSecret, messageId);
SmsDatabase database = DatabaseFactory.getSmsDatabase(context);
SmsMessageRecord record = database.getMessage(messageId);
try {
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.crypto.MasterSecret;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.EncryptingSmsDatabase;
import org.thoughtcrime.securesms.database.NoSuchMessageException;
import org.thoughtcrime.securesms.database.SmsDatabase;
import org.thoughtcrime.securesms.database.model.SmsMessageRecord;
import org.thoughtcrime.securesms.jobs.requirements.MasterSecretRequirement;
import org.thoughtcrime.securesms.notifications.MessageNotifier;
@ -46,7 +46,7 @@ public class SmsSentJob extends MasterSecretJob {
switch (action) {
case SmsDeliveryListener.SENT_SMS_ACTION:
handleSentResult(masterSecret, messageId, result);
handleSentResult(messageId, result);
break;
case SmsDeliveryListener.DELIVERED_SMS_ACTION:
handleDeliveredResult(messageId, result);
@ -65,13 +65,13 @@ public class SmsSentJob extends MasterSecretJob {
}
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 {
EncryptingSmsDatabase database = DatabaseFactory.getEncryptingSmsDatabase(context);
SmsMessageRecord record = database.getMessage(masterSecret, messageId);
SmsDatabase database = DatabaseFactory.getSmsDatabase(context);
SmsMessageRecord record = database.getMessage(messageId);
switch (result) {
case Activity.RESULT_OK:

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