mirror of
https://github.com/oxen-io/session-android.git
synced 2025-10-24 15:31:46 +00:00
Refactor "parts" to contain MMS/PDU madness to MMS code paths.
Closes #4248 // FREEBIE
This commit is contained in:
@@ -1,20 +1,21 @@
|
||||
package org.thoughtcrime.securesms.jobs;
|
||||
|
||||
import android.content.Context;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
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.DatabaseFactory;
|
||||
import org.thoughtcrime.securesms.database.PartDatabase;
|
||||
import org.thoughtcrime.securesms.database.PartDatabase.PartId;
|
||||
import org.thoughtcrime.securesms.database.AttachmentDatabase;
|
||||
import org.thoughtcrime.securesms.dependencies.InjectableType;
|
||||
import org.thoughtcrime.securesms.jobs.requirements.MasterSecretRequirement;
|
||||
import org.thoughtcrime.securesms.jobs.requirements.MediaNetworkRequirement;
|
||||
import org.thoughtcrime.securesms.notifications.MessageNotifier;
|
||||
import org.thoughtcrime.securesms.util.Util;
|
||||
import org.thoughtcrime.securesms.util.VisibleForTesting;
|
||||
import org.whispersystems.jobqueue.JobParameters;
|
||||
import org.whispersystems.jobqueue.requirements.NetworkRequirement;
|
||||
@@ -33,7 +34,6 @@ import javax.inject.Inject;
|
||||
|
||||
import de.greenrobot.event.EventBus;
|
||||
import ws.com.google.android.mms.MmsException;
|
||||
import ws.com.google.android.mms.pdu.PduPart;
|
||||
|
||||
public class AttachmentDownloadJob extends MasterSecretJob implements InjectableType {
|
||||
private static final long serialVersionUID = 1L;
|
||||
@@ -45,18 +45,18 @@ public class AttachmentDownloadJob extends MasterSecretJob implements Injectable
|
||||
private final long partRowId;
|
||||
private final long partUniqueId;
|
||||
|
||||
public AttachmentDownloadJob(Context context, long messageId, PartId partId) {
|
||||
public AttachmentDownloadJob(Context context, long messageId, AttachmentId attachmentId) {
|
||||
super(context, JobParameters.newBuilder()
|
||||
.withGroupId(AttachmentDownloadJob.class.getCanonicalName())
|
||||
.withRequirement(new MasterSecretRequirement(context))
|
||||
.withRequirement(new NetworkRequirement(context))
|
||||
.withRequirement(new MediaNetworkRequirement(context, messageId, partId))
|
||||
.withRequirement(new MediaNetworkRequirement(context, messageId, attachmentId))
|
||||
.withPersistence()
|
||||
.create());
|
||||
|
||||
this.messageId = messageId;
|
||||
this.partRowId = partId.getRowId();
|
||||
this.partUniqueId = partId.getUniqueId();
|
||||
this.partRowId = attachmentId.getRowId();
|
||||
this.partUniqueId = attachmentId.getUniqueId();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -65,29 +65,29 @@ public class AttachmentDownloadJob extends MasterSecretJob implements Injectable
|
||||
|
||||
@Override
|
||||
public void onRun(MasterSecret masterSecret) throws IOException {
|
||||
final PartId partId = new PartId(partRowId, partUniqueId);
|
||||
final PduPart part = DatabaseFactory.getPartDatabase(context).getPart(partId);
|
||||
final AttachmentId attachmentId = new AttachmentId(partRowId, partUniqueId);
|
||||
final Attachment attachment = DatabaseFactory.getAttachmentDatabase(context).getAttachment(attachmentId);
|
||||
|
||||
if (part == null) {
|
||||
Log.w(TAG, "part no longer exists.");
|
||||
return;
|
||||
}
|
||||
if (part.getDataUri() != null) {
|
||||
Log.w(TAG, "part was already downloaded.");
|
||||
if (attachment == null) {
|
||||
Log.w(TAG, "attachment no longer exists.");
|
||||
return;
|
||||
}
|
||||
|
||||
Log.w(TAG, "Downloading push part " + partId);
|
||||
if (!attachment.isInProgress()) {
|
||||
Log.w(TAG, "Attachment was already downloaded.");
|
||||
return;
|
||||
}
|
||||
|
||||
retrievePart(masterSecret, part, messageId);
|
||||
Log.w(TAG, "Downloading push part " + attachmentId);
|
||||
|
||||
retrieveAttachment(masterSecret, messageId, attachmentId, attachment);
|
||||
MessageNotifier.updateNotification(context, masterSecret);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCanceled() {
|
||||
final PartId partId = new PartId(partRowId, partUniqueId);
|
||||
final PduPart part = DatabaseFactory.getPartDatabase(context).getPart(partId);
|
||||
markFailed(messageId, part, part.getPartId());
|
||||
final AttachmentId attachmentId = new AttachmentId(partRowId, partUniqueId);
|
||||
markFailed(messageId, attachmentId);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -95,28 +95,31 @@ public class AttachmentDownloadJob extends MasterSecretJob implements Injectable
|
||||
return (exception instanceof PushNetworkException);
|
||||
}
|
||||
|
||||
private void retrievePart(MasterSecret masterSecret, PduPart part, long messageId)
|
||||
private void retrieveAttachment(MasterSecret masterSecret,
|
||||
long messageId,
|
||||
final AttachmentId attachmentId,
|
||||
final Attachment attachment)
|
||||
throws IOException
|
||||
{
|
||||
|
||||
PartDatabase database = DatabaseFactory.getPartDatabase(context);
|
||||
File attachmentFile = null;
|
||||
AttachmentDatabase database = DatabaseFactory.getAttachmentDatabase(context);
|
||||
File attachmentFile = null;
|
||||
|
||||
final PartId partId = part.getPartId();
|
||||
try {
|
||||
attachmentFile = createTempFile();
|
||||
|
||||
TextSecureAttachmentPointer pointer = createAttachmentPointer(masterSecret, part);
|
||||
InputStream attachment = messageReceiver.retrieveAttachment(pointer, attachmentFile, new ProgressListener() {
|
||||
@Override public void onAttachmentProgress(long total, long progress) {
|
||||
EventBus.getDefault().postSticky(new PartProgressEvent(partId, total, progress));
|
||||
TextSecureAttachmentPointer pointer = createAttachmentPointer(masterSecret, attachment);
|
||||
InputStream stream = messageReceiver.retrieveAttachment(pointer, attachmentFile, new ProgressListener() {
|
||||
@Override
|
||||
public void onAttachmentProgress(long total, long progress) {
|
||||
EventBus.getDefault().postSticky(new PartProgressEvent(attachment, total, progress));
|
||||
}
|
||||
});
|
||||
|
||||
database.updateDownloadedPart(masterSecret, messageId, partId, part, attachment);
|
||||
database.insertAttachmentsForPlaceholder(masterSecret, messageId, attachmentId, stream);
|
||||
} catch (InvalidPartException | NonSuccessfulResponseCodeException | InvalidMessageException | MmsException e) {
|
||||
Log.w(TAG, e);
|
||||
markFailed(messageId, part, partId);
|
||||
markFailed(messageId, attachmentId);
|
||||
} finally {
|
||||
if (attachmentFile != null)
|
||||
attachmentFile.delete();
|
||||
@@ -124,24 +127,25 @@ public class AttachmentDownloadJob extends MasterSecretJob implements Injectable
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
TextSecureAttachmentPointer createAttachmentPointer(MasterSecret masterSecret, PduPart part)
|
||||
TextSecureAttachmentPointer createAttachmentPointer(MasterSecret masterSecret, Attachment attachment)
|
||||
throws InvalidPartException
|
||||
{
|
||||
if (part.getContentLocation() == null || part.getContentLocation().length == 0) {
|
||||
if (TextUtils.isEmpty(attachment.getLocation())) {
|
||||
throw new InvalidPartException("empty content id");
|
||||
}
|
||||
if (part.getContentDisposition() == null || part.getContentDisposition().length == 0) {
|
||||
|
||||
if (TextUtils.isEmpty(attachment.getKey())) {
|
||||
throw new InvalidPartException("empty encrypted key");
|
||||
}
|
||||
|
||||
try {
|
||||
AsymmetricMasterSecret asymmetricMasterSecret = MasterSecretUtil.getAsymmetricMasterSecret(context, masterSecret);
|
||||
long id = Long.parseLong(Util.toIsoString(part.getContentLocation()));
|
||||
byte[] key = MediaKey.getDecrypted(masterSecret, asymmetricMasterSecret, Util.toIsoString(part.getContentDisposition()));
|
||||
long id = Long.parseLong(attachment.getLocation());
|
||||
byte[] key = MediaKey.getDecrypted(masterSecret, asymmetricMasterSecret, attachment.getKey());
|
||||
String relay = null;
|
||||
|
||||
if (part.getName() != null) {
|
||||
relay = Util.toIsoString(part.getName());
|
||||
if (TextUtils.isEmpty(attachment.getRelay())) {
|
||||
relay = attachment.getRelay();
|
||||
}
|
||||
|
||||
return new TextSecureAttachmentPointer(id, null, key, relay);
|
||||
@@ -162,10 +166,10 @@ public class AttachmentDownloadJob extends MasterSecretJob implements Injectable
|
||||
}
|
||||
}
|
||||
|
||||
private void markFailed(long messageId, PduPart part, PartDatabase.PartId partId) {
|
||||
private void markFailed(long messageId, AttachmentId attachmentId) {
|
||||
try {
|
||||
PartDatabase database = DatabaseFactory.getPartDatabase(context);
|
||||
database.updateFailedDownloadedPart(messageId, partId, part);
|
||||
AttachmentDatabase database = DatabaseFactory.getAttachmentDatabase(context);
|
||||
database.setTransferProgressFailed(attachmentId, messageId);
|
||||
} catch (MmsException e) {
|
||||
Log.w(TAG, e);
|
||||
}
|
||||
|
||||
@@ -5,17 +5,22 @@ import android.net.Uri;
|
||||
import android.util.Log;
|
||||
import android.util.Pair;
|
||||
|
||||
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.DatabaseFactory;
|
||||
import org.thoughtcrime.securesms.database.MmsDatabase;
|
||||
import org.thoughtcrime.securesms.database.AttachmentDatabase;
|
||||
import org.thoughtcrime.securesms.jobs.requirements.MasterSecretRequirement;
|
||||
import org.thoughtcrime.securesms.mms.ApnUnavailableException;
|
||||
import org.thoughtcrime.securesms.mms.CompatMmsConnection;
|
||||
import org.thoughtcrime.securesms.mms.IncomingMediaMessage;
|
||||
import org.thoughtcrime.securesms.mms.MmsRadioException;
|
||||
import org.thoughtcrime.securesms.notifications.MessageNotifier;
|
||||
import org.thoughtcrime.securesms.providers.SingleUseBlobProvider;
|
||||
import org.thoughtcrime.securesms.service.KeyCachingService;
|
||||
import org.thoughtcrime.securesms.util.Util;
|
||||
import org.whispersystems.jobqueue.JobParameters;
|
||||
import org.whispersystems.jobqueue.requirements.NetworkRequirement;
|
||||
import org.whispersystems.libaxolotl.DuplicateMessageException;
|
||||
@@ -25,10 +30,15 @@ import org.whispersystems.libaxolotl.NoSessionException;
|
||||
import org.whispersystems.libaxolotl.util.guava.Optional;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import ws.com.google.android.mms.ContentType;
|
||||
import ws.com.google.android.mms.MmsException;
|
||||
import ws.com.google.android.mms.pdu.EncodedStringValue;
|
||||
import ws.com.google.android.mms.pdu.NotificationInd;
|
||||
import ws.com.google.android.mms.pdu.PduPart;
|
||||
import ws.com.google.android.mms.pdu.RetrieveConf;
|
||||
|
||||
public class MmsDownloadJob extends MasterSecretJob {
|
||||
@@ -63,8 +73,6 @@ public class MmsDownloadJob extends MasterSecretJob {
|
||||
|
||||
@Override
|
||||
public void onRun(MasterSecret masterSecret) {
|
||||
Log.w(TAG, "onRun()");
|
||||
|
||||
MmsDatabase database = DatabaseFactory.getMmsDatabase(context);
|
||||
Optional<NotificationInd> notification = database.getNotification(messageId);
|
||||
|
||||
@@ -140,13 +148,52 @@ public class MmsDownloadJob extends MasterSecretJob {
|
||||
throws MmsException, NoSessionException, DuplicateMessageException, InvalidMessageException,
|
||||
LegacyMessageException
|
||||
{
|
||||
MmsDatabase database = DatabaseFactory.getMmsDatabase(context);
|
||||
IncomingMediaMessage message = new IncomingMediaMessage(retrieved);
|
||||
MmsDatabase database = DatabaseFactory.getMmsDatabase(context);
|
||||
SingleUseBlobProvider provider = SingleUseBlobProvider.getInstance();
|
||||
String from = null;
|
||||
List<String> to = new LinkedList<>();
|
||||
List<String> cc = new LinkedList<>();
|
||||
String body = null;
|
||||
List<Attachment> attachments = new LinkedList<>();
|
||||
|
||||
if (retrieved.getFrom() != null) {
|
||||
from = Util.toIsoString(retrieved.getFrom().getTextString());
|
||||
}
|
||||
|
||||
if (retrieved.getTo() != null) {
|
||||
for (EncodedStringValue toValue : retrieved.getTo()) {
|
||||
to.add(Util.toIsoString(toValue.getTextString()));
|
||||
}
|
||||
}
|
||||
|
||||
if (retrieved.getCc() != null) {
|
||||
for (EncodedStringValue ccValue : retrieved.getCc()) {
|
||||
cc.add(Util.toIsoString(ccValue.getTextString()));
|
||||
}
|
||||
}
|
||||
|
||||
if (retrieved.getBody() != null) {
|
||||
for (int i=0;i<retrieved.getBody().getPartsNum();i++) {
|
||||
PduPart part = retrieved.getBody().getPart(i);
|
||||
|
||||
if (Util.toIsoString(part.getContentType()).equals(ContentType.TEXT_PLAIN)) {
|
||||
body = Util.toIsoString(part.getData());
|
||||
} else if (part.getData() != null) {
|
||||
Uri uri = provider.createUri(part.getData());
|
||||
attachments.add(new UriAttachment(uri, Util.toIsoString(part.getContentType()),
|
||||
AttachmentDatabase.TRANSFER_PROGRESS_DONE,
|
||||
part.getData().length));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
IncomingMediaMessage message = new IncomingMediaMessage(from, to, cc, body, retrieved.getDate() * 1000L, attachments);
|
||||
|
||||
Pair<Long, Long> messageAndThreadId = database.insertMessageInbox(new MasterSecretUnion(masterSecret),
|
||||
message, contentLocation, threadId);
|
||||
database.delete(messageId);
|
||||
|
||||
MessageNotifier.updateNotification(context, masterSecret, messageAndThreadId.second);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,23 +1,23 @@
|
||||
package org.thoughtcrime.securesms.jobs;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Build.VERSION;
|
||||
import android.os.Build.VERSION_CODES;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import org.thoughtcrime.securesms.attachments.Attachment;
|
||||
import org.thoughtcrime.securesms.crypto.MasterSecret;
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||
import org.thoughtcrime.securesms.database.MmsDatabase;
|
||||
import org.thoughtcrime.securesms.database.NoSuchMessageException;
|
||||
import org.thoughtcrime.securesms.database.ThreadDatabase.DistributionTypes;
|
||||
import org.thoughtcrime.securesms.jobs.requirements.MasterSecretRequirement;
|
||||
import org.thoughtcrime.securesms.mms.ApnUnavailableException;
|
||||
import org.thoughtcrime.securesms.mms.CompatMmsConnection;
|
||||
import org.thoughtcrime.securesms.mms.MediaConstraints;
|
||||
import org.thoughtcrime.securesms.mms.MmsSendResult;
|
||||
import org.thoughtcrime.securesms.mms.OutgoingLegacyMmsConnection;
|
||||
import org.thoughtcrime.securesms.mms.OutgoingLollipopMmsConnection;
|
||||
import org.thoughtcrime.securesms.mms.OutgoingMmsConnection;
|
||||
import org.thoughtcrime.securesms.mms.OutgoingMediaMessage;
|
||||
import org.thoughtcrime.securesms.mms.PartAuthority;
|
||||
import org.thoughtcrime.securesms.notifications.MessageNotifier;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.recipients.Recipients;
|
||||
import org.thoughtcrime.securesms.transport.InsecureFallbackApprovalException;
|
||||
import org.thoughtcrime.securesms.transport.UndeliverableMessageException;
|
||||
@@ -25,20 +25,29 @@ import org.thoughtcrime.securesms.util.Hex;
|
||||
import org.thoughtcrime.securesms.util.NumberUtil;
|
||||
import org.thoughtcrime.securesms.util.SmilUtil;
|
||||
import org.thoughtcrime.securesms.util.TelephonyUtil;
|
||||
import org.thoughtcrime.securesms.util.Util;
|
||||
import org.whispersystems.jobqueue.JobParameters;
|
||||
import org.whispersystems.jobqueue.requirements.NetworkRequirement;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import ws.com.google.android.mms.ContentType;
|
||||
import ws.com.google.android.mms.MmsException;
|
||||
import ws.com.google.android.mms.pdu.CharacterSets;
|
||||
import ws.com.google.android.mms.pdu.EncodedStringValue;
|
||||
import ws.com.google.android.mms.pdu.PduBody;
|
||||
import ws.com.google.android.mms.pdu.PduComposer;
|
||||
import ws.com.google.android.mms.pdu.PduHeaders;
|
||||
import ws.com.google.android.mms.pdu.PduPart;
|
||||
import ws.com.google.android.mms.pdu.SendConf;
|
||||
import ws.com.google.android.mms.pdu.SendReq;
|
||||
|
||||
public class MmsSendJob extends SendJob {
|
||||
|
||||
private static final long serialVersionUID = 0L;
|
||||
|
||||
private static final String TAG = MmsSendJob.class.getSimpleName();
|
||||
|
||||
private final long messageId;
|
||||
@@ -62,18 +71,20 @@ public class MmsSendJob extends SendJob {
|
||||
|
||||
@Override
|
||||
public void onSend(MasterSecret masterSecret) throws MmsException, NoSuchMessageException, IOException {
|
||||
MmsDatabase database = DatabaseFactory.getMmsDatabase(context);
|
||||
SendReq message = database.getOutgoingMessage(masterSecret, messageId);
|
||||
MmsDatabase database = DatabaseFactory.getMmsDatabase(context);
|
||||
OutgoingMediaMessage message = database.getOutgoingMessage(masterSecret, messageId);
|
||||
|
||||
try {
|
||||
validateDestinations(message);
|
||||
SendReq pdu = constructSendPdu(masterSecret, message);
|
||||
|
||||
final byte[] pduBytes = getPduBytes(masterSecret, message);
|
||||
validateDestinations(message, pdu);
|
||||
|
||||
final byte[] pduBytes = getPduBytes(pdu);
|
||||
final SendConf sendConf = new CompatMmsConnection(context).send(pduBytes);
|
||||
final MmsSendResult result = getSendResult(sendConf, message);
|
||||
final MmsSendResult result = getSendResult(sendConf, pdu);
|
||||
|
||||
database.markAsSent(messageId, result.getMessageId(), result.getResponseStatus());
|
||||
markPartsUploaded(messageId, message.getBody());
|
||||
database.markAsSent(messageId);
|
||||
markAttachmentsUploaded(messageId, message.getAttachments());
|
||||
} catch (UndeliverableMessageException | IOException e) {
|
||||
Log.w(TAG, e);
|
||||
database.markAsSentFailed(messageId);
|
||||
@@ -96,21 +107,19 @@ public class MmsSendJob extends SendJob {
|
||||
notifyMediaMessageDeliveryFailed(context, messageId);
|
||||
}
|
||||
|
||||
private byte[] getPduBytes(MasterSecret masterSecret, SendReq message)
|
||||
private byte[] getPduBytes(SendReq message)
|
||||
throws IOException, UndeliverableMessageException, InsecureFallbackApprovalException
|
||||
{
|
||||
String number = TelephonyUtil.getManager(context).getLine1Number();
|
||||
|
||||
message = getResolvedMessage(masterSecret, message, MediaConstraints.MMS_CONSTRAINTS, true);
|
||||
message.setBody(SmilUtil.getSmilBody(message.getBody()));
|
||||
if (MmsDatabase.Types.isSecureType(message.getDatabaseMessageBox())) {
|
||||
throw new UndeliverableMessageException("Attempt to send encrypted MMS?");
|
||||
}
|
||||
|
||||
if (number != null && number.trim().length() != 0) {
|
||||
if (!TextUtils.isEmpty(number)) {
|
||||
message.setFrom(new EncodedStringValue(number));
|
||||
}
|
||||
|
||||
byte[] pduBytes = new PduComposer(context, message).make();
|
||||
|
||||
if (pduBytes == null) {
|
||||
throw new UndeliverableMessageException("PDU composition failed, null payload");
|
||||
}
|
||||
@@ -149,7 +158,7 @@ public class MmsSendJob extends SendJob {
|
||||
}
|
||||
}
|
||||
|
||||
private void validateDestinations(SendReq message) throws UndeliverableMessageException {
|
||||
private void validateDestinations(OutgoingMediaMessage media, SendReq message) throws UndeliverableMessageException {
|
||||
validateDestinations(message.getTo());
|
||||
validateDestinations(message.getCc());
|
||||
validateDestinations(message.getBcc());
|
||||
@@ -157,6 +166,59 @@ public class MmsSendJob extends SendJob {
|
||||
if (message.getTo() == null && message.getCc() == null && message.getBcc() == null) {
|
||||
throw new UndeliverableMessageException("No to, cc, or bcc specified!");
|
||||
}
|
||||
|
||||
if (media.isSecure()) {
|
||||
throw new UndeliverableMessageException("Attempt to send encrypted MMS?");
|
||||
}
|
||||
}
|
||||
|
||||
private SendReq constructSendPdu(MasterSecret masterSecret, OutgoingMediaMessage message)
|
||||
throws UndeliverableMessageException
|
||||
{
|
||||
SendReq sendReq = new SendReq();
|
||||
PduBody body = new PduBody();
|
||||
|
||||
for (Recipient recipient : message.getRecipients()) {
|
||||
if (message.getDistributionType() == DistributionTypes.CONVERSATION) {
|
||||
sendReq.addTo(new EncodedStringValue(Util.toIsoBytes(recipient.getNumber())));
|
||||
} else {
|
||||
sendReq.addBcc(new EncodedStringValue(Util.toIsoBytes(recipient.getNumber())));
|
||||
}
|
||||
}
|
||||
|
||||
sendReq.setDate(message.getSentTimeMillis() / 1000L);
|
||||
|
||||
if (!TextUtils.isEmpty(message.getBody())) {
|
||||
PduPart part = new PduPart();
|
||||
part.setData(Util.toUtf8Bytes(message.getBody()));
|
||||
part.setCharset(CharacterSets.UTF_8);
|
||||
part.setContentType(ContentType.TEXT_PLAIN.getBytes());
|
||||
part.setContentId((System.currentTimeMillis()+"").getBytes());
|
||||
part.setName(("Text"+System.currentTimeMillis()).getBytes());
|
||||
|
||||
body.addPart(part);
|
||||
}
|
||||
|
||||
List<Attachment> scaledAttachments = scaleAttachments(masterSecret, MediaConstraints.MMS_CONSTRAINTS, message.getAttachments());
|
||||
|
||||
for (Attachment attachment : scaledAttachments) {
|
||||
try {
|
||||
if (attachment.getDataUri() == null) throw new IOException("Assertion failed, attachment for outgoing MMS has no data!");
|
||||
|
||||
PduPart part = new PduPart();
|
||||
part.setData(Util.readFully(PartAuthority.getAttachmentStream(context, masterSecret, attachment.getDataUri())));
|
||||
part.setContentType(Util.toIsoBytes(attachment.getContentType()));
|
||||
part.setContentId((System.currentTimeMillis() + "").getBytes());
|
||||
part.setName((System.currentTimeMillis() + "").getBytes());
|
||||
|
||||
body.addPart(part);
|
||||
} catch (IOException e) {
|
||||
Log.w(TAG, e);
|
||||
}
|
||||
}
|
||||
|
||||
sendReq.setBody(body);
|
||||
return sendReq;
|
||||
}
|
||||
|
||||
private void notifyMediaMessageDeliveryFailed(Context context, long messageId) {
|
||||
|
||||
@@ -1,15 +1,19 @@
|
||||
package org.thoughtcrime.securesms.jobs;
|
||||
|
||||
import org.thoughtcrime.securesms.database.PartDatabase.PartId;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import org.thoughtcrime.securesms.attachments.Attachment;
|
||||
|
||||
public class PartProgressEvent {
|
||||
public PartId partId;
|
||||
public long total;
|
||||
public long progress;
|
||||
|
||||
public PartProgressEvent(PartId partId, long total, long progress) {
|
||||
this.partId = partId;
|
||||
this.total = total;
|
||||
this.progress = progress;
|
||||
public final Attachment attachment;
|
||||
public final long total;
|
||||
public final long progress;
|
||||
|
||||
public PartProgressEvent(@NonNull Attachment attachment, long total, long progress) {
|
||||
this.attachment = attachment;
|
||||
this.total = total;
|
||||
this.progress = progress;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,8 @@ import android.util.Log;
|
||||
import android.util.Pair;
|
||||
|
||||
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;
|
||||
@@ -18,6 +20,7 @@ import org.thoughtcrime.securesms.database.EncryptingSmsDatabase;
|
||||
import org.thoughtcrime.securesms.database.MmsDatabase;
|
||||
import org.thoughtcrime.securesms.database.NoSuchMessageException;
|
||||
import org.thoughtcrime.securesms.database.PushDatabase;
|
||||
import org.thoughtcrime.securesms.database.ThreadDatabase;
|
||||
import org.thoughtcrime.securesms.groups.GroupMessageProcessor;
|
||||
import org.thoughtcrime.securesms.mms.IncomingMediaMessage;
|
||||
import org.thoughtcrime.securesms.mms.OutgoingMediaMessage;
|
||||
@@ -62,7 +65,6 @@ import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import ws.com.google.android.mms.MmsException;
|
||||
import ws.com.google.android.mms.pdu.PduPart;
|
||||
|
||||
public class PushDecryptJob extends ContextJob {
|
||||
|
||||
@@ -254,13 +256,14 @@ public class PushDecryptJob extends ContextJob {
|
||||
message.getGroupInfo(),
|
||||
message.getAttachments());
|
||||
|
||||
Pair<Long, Long> messageAndThreadId = database.insertSecureDecryptedMessageInbox(masterSecret, mediaMessage, -1);
|
||||
Pair<Long, Long> messageAndThreadId = database.insertSecureDecryptedMessageInbox(masterSecret, mediaMessage, -1);
|
||||
List<DatabaseAttachment> attachments = DatabaseFactory.getAttachmentDatabase(context).getAttachmentsForMessage(messageAndThreadId.first);
|
||||
|
||||
List<PduPart> parts = DatabaseFactory.getPartDatabase(context).getParts(messageAndThreadId.first);
|
||||
for (PduPart part : parts) {
|
||||
for (DatabaseAttachment attachment : attachments) {
|
||||
ApplicationContext.getInstance(context)
|
||||
.getJobManager()
|
||||
.add(new AttachmentDownloadJob(context, messageAndThreadId.first, part.getPartId()));
|
||||
.add(new AttachmentDownloadJob(context, messageAndThreadId.first,
|
||||
attachment.getAttachmentId()));
|
||||
}
|
||||
|
||||
if (smsMessageId.isPresent()) {
|
||||
@@ -277,22 +280,22 @@ public class PushDecryptJob extends ContextJob {
|
||||
{
|
||||
MmsDatabase database = DatabaseFactory.getMmsDatabase(context);
|
||||
Recipients recipients = getSyncMessageDestination(message);
|
||||
OutgoingMediaMessage mediaMessage = new OutgoingMediaMessage(context, masterSecret, recipients,
|
||||
message.getMessage().getAttachments().get(),
|
||||
message.getMessage().getBody().orNull());
|
||||
OutgoingMediaMessage mediaMessage = new OutgoingMediaMessage(recipients, message.getMessage().getBody().orNull(),
|
||||
PointerAttachment.forPointers(masterSecret, message.getMessage().getAttachments()),
|
||||
message.getTimestamp(), ThreadDatabase.DistributionTypes.DEFAULT);
|
||||
|
||||
mediaMessage = new OutgoingSecureMediaMessage(mediaMessage);
|
||||
|
||||
long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipients);
|
||||
long messageId = database.insertMessageOutbox(masterSecret, mediaMessage, threadId, false, message.getTimestamp());
|
||||
long messageId = database.insertMessageOutbox(masterSecret, mediaMessage, threadId, false);
|
||||
|
||||
database.markAsSent(messageId, "push".getBytes(), 0);
|
||||
database.markAsSent(messageId);
|
||||
database.markAsPush(messageId);
|
||||
|
||||
for (PduPart part : DatabaseFactory.getPartDatabase(context).getParts(messageId)) {
|
||||
for (DatabaseAttachment attachment : DatabaseFactory.getAttachmentDatabase(context).getAttachmentsForMessage(messageId)) {
|
||||
ApplicationContext.getInstance(context)
|
||||
.getJobManager()
|
||||
.add(new AttachmentDownloadJob(context, messageId, part.getPartId()));
|
||||
.add(new AttachmentDownloadJob(context, messageId, attachment.getAttachmentId()));
|
||||
}
|
||||
|
||||
if (smsMessageId.isPresent()) {
|
||||
|
||||
@@ -6,27 +6,25 @@ import android.util.Log;
|
||||
import org.thoughtcrime.securesms.crypto.MasterSecret;
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||
import org.thoughtcrime.securesms.database.MmsDatabase;
|
||||
import org.thoughtcrime.securesms.database.MmsSmsColumns;
|
||||
import org.thoughtcrime.securesms.database.NoSuchMessageException;
|
||||
import org.thoughtcrime.securesms.database.documents.NetworkFailure;
|
||||
import org.thoughtcrime.securesms.dependencies.InjectableType;
|
||||
import org.thoughtcrime.securesms.jobs.requirements.MasterSecretRequirement;
|
||||
import org.thoughtcrime.securesms.mms.MediaConstraints;
|
||||
import org.thoughtcrime.securesms.mms.PartParser;
|
||||
import org.thoughtcrime.securesms.mms.OutgoingGroupMediaMessage;
|
||||
import org.thoughtcrime.securesms.mms.OutgoingMediaMessage;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientFactory;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientFormattingException;
|
||||
import org.thoughtcrime.securesms.recipients.Recipients;
|
||||
import org.thoughtcrime.securesms.transport.UndeliverableMessageException;
|
||||
import org.thoughtcrime.securesms.util.Base64;
|
||||
import org.thoughtcrime.securesms.util.GroupUtil;
|
||||
import org.whispersystems.jobqueue.JobParameters;
|
||||
import org.whispersystems.jobqueue.requirements.NetworkRequirement;
|
||||
import org.whispersystems.textsecure.api.TextSecureMessageSender;
|
||||
import org.whispersystems.textsecure.api.crypto.UntrustedIdentityException;
|
||||
import org.whispersystems.textsecure.api.messages.TextSecureAttachment;
|
||||
import org.whispersystems.textsecure.api.messages.TextSecureGroup;
|
||||
import org.whispersystems.textsecure.api.messages.TextSecureDataMessage;
|
||||
import org.whispersystems.textsecure.api.messages.TextSecureGroup;
|
||||
import org.whispersystems.textsecure.api.push.TextSecureAddress;
|
||||
import org.whispersystems.textsecure.api.push.exceptions.EncapsulatedExceptions;
|
||||
import org.whispersystems.textsecure.api.push.exceptions.NetworkFailureException;
|
||||
@@ -40,7 +38,6 @@ import java.util.List;
|
||||
import javax.inject.Inject;
|
||||
|
||||
import ws.com.google.android.mms.MmsException;
|
||||
import ws.com.google.android.mms.pdu.SendReq;
|
||||
|
||||
import static org.thoughtcrime.securesms.dependencies.TextSecureCommunicationModule.TextSecureMessageSenderFactory;
|
||||
|
||||
@@ -78,16 +75,16 @@ public class PushGroupSendJob extends PushSendJob implements InjectableType {
|
||||
public void onSend(MasterSecret masterSecret)
|
||||
throws MmsException, IOException, NoSuchMessageException
|
||||
{
|
||||
MmsDatabase database = DatabaseFactory.getMmsDatabase(context);
|
||||
SendReq message = database.getOutgoingMessage(masterSecret, messageId);
|
||||
MmsDatabase database = DatabaseFactory.getMmsDatabase(context);
|
||||
OutgoingMediaMessage message = database.getOutgoingMessage(masterSecret, messageId);
|
||||
|
||||
try {
|
||||
deliver(masterSecret, message, filterRecipientId);
|
||||
|
||||
database.markAsPush(messageId);
|
||||
database.markAsSecure(messageId);
|
||||
database.markAsSent(messageId, "push".getBytes(), 0);
|
||||
markPartsUploaded(messageId, message.getBody());
|
||||
database.markAsSent(messageId);
|
||||
markAttachmentsUploaded(messageId, message.getAttachments());
|
||||
} catch (InvalidNumberException | RecipientFormattingException | UndeliverableMessageException e) {
|
||||
Log.w(TAG, e);
|
||||
database.markAsSentFailed(messageId);
|
||||
@@ -101,11 +98,6 @@ public class PushGroupSendJob extends PushSendJob implements InjectableType {
|
||||
failures.add(new NetworkFailure(recipient.getRecipientId()));
|
||||
}
|
||||
|
||||
// for (UnregisteredUserException uue : e.getUnregisteredUserExceptions()) {
|
||||
// Recipient recipient = RecipientFactory.getRecipientsFromString(context, uue.getE164Number(), false).getPrimaryRecipient();
|
||||
// failures.add(new NetworkFailure(recipient.getRecipientId(), NetworkFailure.UNREGISTERED_FAILURE));
|
||||
// }
|
||||
|
||||
for (UntrustedIdentityException uie : e.getUntrustedIdentityExceptions()) {
|
||||
Recipient recipient = RecipientFactory.getRecipientsFromString(context, uie.getE164Number(), false).getPrimaryRecipient();
|
||||
database.addMismatchedIdentity(messageId, recipient.getRecipientId(), uie.getIdentityKey());
|
||||
@@ -130,39 +122,31 @@ public class PushGroupSendJob extends PushSendJob implements InjectableType {
|
||||
DatabaseFactory.getMmsDatabase(context).markAsSentFailed(messageId);
|
||||
}
|
||||
|
||||
private void deliver(MasterSecret masterSecret, SendReq message, long filterRecipientId)
|
||||
private void deliver(MasterSecret masterSecret, OutgoingMediaMessage message, long filterRecipientId)
|
||||
throws IOException, RecipientFormattingException, InvalidNumberException,
|
||||
EncapsulatedExceptions, UndeliverableMessageException
|
||||
{
|
||||
message = getResolvedMessage(masterSecret, message, MediaConstraints.PUSH_CONSTRAINTS, false);
|
||||
|
||||
TextSecureMessageSender messageSender = messageSenderFactory.create();
|
||||
byte[] groupId = GroupUtil.getDecodedId(message.getTo()[0].getString());
|
||||
byte[] groupId = GroupUtil.getDecodedId(message.getRecipients().getPrimaryRecipient().getNumber());
|
||||
Recipients recipients = DatabaseFactory.getGroupDatabase(context).getGroupMembers(groupId, false);
|
||||
List<TextSecureAttachment> attachments = getAttachments(masterSecret, message);
|
||||
List<TextSecureAttachment> attachments = getAttachmentsFor(masterSecret, message.getAttachments());
|
||||
List<TextSecureAddress> addresses;
|
||||
|
||||
if (filterRecipientId >= 0) addresses = getPushAddresses(filterRecipientId);
|
||||
else addresses = getPushAddresses(recipients);
|
||||
|
||||
if (MmsSmsColumns.Types.isGroupUpdate(message.getDatabaseMessageBox()) ||
|
||||
MmsSmsColumns.Types.isGroupQuit(message.getDatabaseMessageBox()))
|
||||
{
|
||||
String content = PartParser.getMessageText(message.getBody());
|
||||
if (message.isGroup()) {
|
||||
OutgoingGroupMediaMessage groupMessage = (OutgoingGroupMediaMessage) message;
|
||||
GroupContext groupContext = groupMessage.getGroupContext();
|
||||
TextSecureAttachment avatar = attachments.isEmpty() ? null : attachments.get(0);
|
||||
TextSecureGroup.Type type = groupMessage.isGroupQuit() ? TextSecureGroup.Type.QUIT : TextSecureGroup.Type.UPDATE;
|
||||
TextSecureGroup group = new TextSecureGroup(type, groupId, groupContext.getName(), groupContext.getMembersList(), avatar);
|
||||
TextSecureDataMessage groupDataMessage = new TextSecureDataMessage(message.getSentTimeMillis(), group, null, null);
|
||||
|
||||
if (content != null && !content.trim().isEmpty()) {
|
||||
GroupContext groupContext = GroupContext.parseFrom(Base64.decode(content));
|
||||
TextSecureAttachment avatar = attachments.isEmpty() ? null : attachments.get(0);
|
||||
TextSecureGroup.Type type = MmsSmsColumns.Types.isGroupQuit(message.getDatabaseMessageBox()) ? TextSecureGroup.Type.QUIT : TextSecureGroup.Type.UPDATE;
|
||||
TextSecureGroup group = new TextSecureGroup(type, groupId, groupContext.getName(), groupContext.getMembersList(), avatar);
|
||||
TextSecureDataMessage groupMessage = new TextSecureDataMessage(message.getSentTimestamp(), group, null, null);
|
||||
|
||||
messageSender.sendMessage(addresses, groupMessage);
|
||||
}
|
||||
messageSender.sendMessage(addresses, groupDataMessage);
|
||||
} else {
|
||||
String body = PartParser.getMessageText(message.getBody());
|
||||
TextSecureGroup group = new TextSecureGroup(groupId);
|
||||
TextSecureDataMessage groupMessage = new TextSecureDataMessage(message.getSentTimestamp(), group, attachments, body);
|
||||
TextSecureDataMessage groupMessage = new TextSecureDataMessage(message.getSentTimeMillis(), group, attachments, message.getBody());
|
||||
|
||||
messageSender.sendMessage(addresses, groupMessage);
|
||||
}
|
||||
|
||||
@@ -4,14 +4,14 @@ import android.content.Context;
|
||||
import android.util.Log;
|
||||
|
||||
import org.thoughtcrime.securesms.ApplicationContext;
|
||||
import org.thoughtcrime.securesms.attachments.Attachment;
|
||||
import org.thoughtcrime.securesms.crypto.MasterSecret;
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||
import org.thoughtcrime.securesms.database.MmsDatabase;
|
||||
import org.thoughtcrime.securesms.database.NoSuchMessageException;
|
||||
import org.thoughtcrime.securesms.database.PartDatabase;
|
||||
import org.thoughtcrime.securesms.dependencies.InjectableType;
|
||||
import org.thoughtcrime.securesms.mms.MediaConstraints;
|
||||
import org.thoughtcrime.securesms.mms.PartParser;
|
||||
import org.thoughtcrime.securesms.mms.OutgoingMediaMessage;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientFactory;
|
||||
import org.thoughtcrime.securesms.recipients.Recipients;
|
||||
import org.thoughtcrime.securesms.transport.InsecureFallbackApprovalException;
|
||||
@@ -31,8 +31,6 @@ import java.util.List;
|
||||
import javax.inject.Inject;
|
||||
|
||||
import ws.com.google.android.mms.MmsException;
|
||||
import ws.com.google.android.mms.pdu.PduBody;
|
||||
import ws.com.google.android.mms.pdu.SendReq;
|
||||
|
||||
import static org.thoughtcrime.securesms.dependencies.TextSecureCommunicationModule.TextSecureMessageSenderFactory;
|
||||
|
||||
@@ -63,15 +61,15 @@ public class PushMediaSendJob extends PushSendJob implements InjectableType {
|
||||
throws RetryLaterException, MmsException, NoSuchMessageException,
|
||||
UndeliverableMessageException
|
||||
{
|
||||
MmsDatabase database = DatabaseFactory.getMmsDatabase(context);
|
||||
SendReq message = database.getOutgoingMessage(masterSecret, messageId);
|
||||
MmsDatabase database = DatabaseFactory.getMmsDatabase(context);
|
||||
OutgoingMediaMessage message = database.getOutgoingMessage(masterSecret, messageId);
|
||||
|
||||
try {
|
||||
deliver(masterSecret, message);
|
||||
database.markAsPush(messageId);
|
||||
database.markAsSecure(messageId);
|
||||
database.markAsSent(messageId, "push".getBytes(), 0);
|
||||
markPartsUploaded(messageId, message.getBody());
|
||||
database.markAsSent(messageId);
|
||||
markAttachmentsUploaded(messageId, message.getAttachments());
|
||||
} catch (InsecureFallbackApprovalException ifae) {
|
||||
Log.w(TAG, ifae);
|
||||
database.markAsPendingInsecureSmsFallback(messageId);
|
||||
@@ -100,24 +98,21 @@ public class PushMediaSendJob extends PushSendJob implements InjectableType {
|
||||
notifyMediaMessageDeliveryFailed(context, messageId);
|
||||
}
|
||||
|
||||
private void deliver(MasterSecret masterSecret, SendReq message)
|
||||
private void deliver(MasterSecret masterSecret, OutgoingMediaMessage message)
|
||||
throws RetryLaterException, InsecureFallbackApprovalException, UntrustedIdentityException,
|
||||
UndeliverableMessageException
|
||||
{
|
||||
TextSecureMessageSender messageSender = messageSenderFactory.create();
|
||||
String destination = message.getTo()[0].getString();
|
||||
|
||||
try {
|
||||
message = getResolvedMessage(masterSecret, message, MediaConstraints.PUSH_CONSTRAINTS, false);
|
||||
|
||||
TextSecureAddress address = getPushAddress(destination);
|
||||
List<TextSecureAttachment> attachments = getAttachments(masterSecret, message);
|
||||
String body = PartParser.getMessageText(message.getBody());
|
||||
TextSecureDataMessage mediaMessage = TextSecureDataMessage.newBuilder()
|
||||
.withBody(body)
|
||||
.withAttachments(attachments)
|
||||
.withTimestamp(message.getSentTimestamp())
|
||||
.build();
|
||||
TextSecureAddress address = getPushAddress(message.getRecipients().getPrimaryRecipient().getNumber());
|
||||
List<Attachment> scaledAttachments = scaleAttachments(masterSecret, MediaConstraints.PUSH_CONSTRAINTS, message.getAttachments());
|
||||
List<TextSecureAttachment> attachmentStreams = getAttachmentsFor(masterSecret, scaledAttachments);
|
||||
TextSecureDataMessage mediaMessage = TextSecureDataMessage.newBuilder()
|
||||
.withBody(message.getBody())
|
||||
.withAttachments(attachmentStreams)
|
||||
.withTimestamp(message.getSentTimeMillis())
|
||||
.build();
|
||||
|
||||
messageSender.sendMessage(address, mediaMessage);
|
||||
} catch (InvalidNumberException | UnregisteredUserException e) {
|
||||
|
||||
@@ -3,6 +3,7 @@ package org.thoughtcrime.securesms.jobs;
|
||||
import android.content.Context;
|
||||
import android.util.Log;
|
||||
|
||||
import org.thoughtcrime.securesms.attachments.Attachment;
|
||||
import org.thoughtcrime.securesms.crypto.MasterSecret;
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||
import org.thoughtcrime.securesms.database.TextSecureDirectory;
|
||||
@@ -16,7 +17,6 @@ import org.whispersystems.jobqueue.requirements.NetworkRequirement;
|
||||
import org.whispersystems.libaxolotl.util.guava.Optional;
|
||||
import org.whispersystems.textsecure.api.messages.TextSecureAttachment;
|
||||
import org.whispersystems.textsecure.api.messages.TextSecureAttachment.ProgressListener;
|
||||
import org.whispersystems.textsecure.api.messages.TextSecureAttachmentStream;
|
||||
import org.whispersystems.textsecure.api.push.TextSecureAddress;
|
||||
import org.whispersystems.textsecure.api.util.InvalidNumberException;
|
||||
|
||||
@@ -27,8 +27,6 @@ import java.util.List;
|
||||
|
||||
import de.greenrobot.event.EventBus;
|
||||
import ws.com.google.android.mms.ContentType;
|
||||
import ws.com.google.android.mms.pdu.PduPart;
|
||||
import ws.com.google.android.mms.pdu.SendReq;
|
||||
|
||||
public abstract class PushSendJob extends SendJob {
|
||||
|
||||
@@ -55,25 +53,25 @@ public abstract class PushSendJob extends SendJob {
|
||||
return new TextSecureAddress(e164number, Optional.fromNullable(relay));
|
||||
}
|
||||
|
||||
protected List<TextSecureAttachment> getAttachments(final MasterSecret masterSecret, final SendReq message) {
|
||||
protected List<TextSecureAttachment> getAttachmentsFor(MasterSecret masterSecret, List<Attachment> parts) {
|
||||
List<TextSecureAttachment> attachments = new LinkedList<>();
|
||||
|
||||
for (int i=0;i<message.getBody().getPartsNum();i++) {
|
||||
final PduPart part = message.getBody().getPart(i);
|
||||
final String contentType = Util.toIsoString(part.getContentType());
|
||||
if (ContentType.isImageType(contentType) ||
|
||||
ContentType.isAudioType(contentType) ||
|
||||
ContentType.isVideoType(contentType))
|
||||
for (final Attachment attachment : parts) {
|
||||
if (ContentType.isImageType(attachment.getContentType()) ||
|
||||
ContentType.isAudioType(attachment.getContentType()) ||
|
||||
ContentType.isVideoType(attachment.getContentType()))
|
||||
{
|
||||
try {
|
||||
InputStream is = PartAuthority.getPartStream(context, masterSecret, part.getDataUri());
|
||||
if (attachment.getDataUri() == null) throw new IOException("Assertion failed, outgoing attachment has no data!");
|
||||
InputStream is = PartAuthority.getAttachmentStream(context, masterSecret, attachment.getDataUri());
|
||||
attachments.add(TextSecureAttachment.newStreamBuilder()
|
||||
.withStream(is)
|
||||
.withContentType(contentType)
|
||||
.withLength(part.getDataSize())
|
||||
.withContentType(attachment.getContentType())
|
||||
.withLength(attachment.getSize())
|
||||
.withListener(new ProgressListener() {
|
||||
@Override public void onAttachmentProgress(long total, long progress) {
|
||||
EventBus.getDefault().postSticky(new PartProgressEvent(part.getPartId(), total, progress));
|
||||
@Override
|
||||
public void onAttachmentProgress(long total, long progress) {
|
||||
EventBus.getDefault().postSticky(new PartProgressEvent(attachment, total, progress));
|
||||
}
|
||||
})
|
||||
.build());
|
||||
|
||||
@@ -1,28 +1,28 @@
|
||||
package org.thoughtcrime.securesms.jobs;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.Log;
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import org.thoughtcrime.securesms.BuildConfig;
|
||||
import org.thoughtcrime.securesms.TextSecureExpiredException;
|
||||
import org.thoughtcrime.securesms.attachments.Attachment;
|
||||
import org.thoughtcrime.securesms.crypto.MasterSecret;
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||
import org.thoughtcrime.securesms.database.PartDatabase;
|
||||
import org.thoughtcrime.securesms.database.AttachmentDatabase;
|
||||
import org.thoughtcrime.securesms.mms.MediaConstraints;
|
||||
import org.thoughtcrime.securesms.transport.UndeliverableMessageException;
|
||||
import org.thoughtcrime.securesms.util.MediaUtil;
|
||||
import org.thoughtcrime.securesms.util.Util;
|
||||
import org.whispersystems.jobqueue.JobParameters;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import ws.com.google.android.mms.MmsException;
|
||||
import ws.com.google.android.mms.pdu.PduBody;
|
||||
import ws.com.google.android.mms.pdu.PduPart;
|
||||
import ws.com.google.android.mms.pdu.SendReq;
|
||||
|
||||
public abstract class SendJob extends MasterSecretJob {
|
||||
|
||||
private final static String TAG = SendJob.class.getSimpleName();
|
||||
|
||||
public SendJob(Context context, JobParameters parameters) {
|
||||
@@ -42,68 +42,37 @@ public abstract class SendJob extends MasterSecretJob {
|
||||
|
||||
protected abstract void onSend(MasterSecret masterSecret) throws Exception;
|
||||
|
||||
protected SendReq getResolvedMessage(MasterSecret masterSecret, SendReq message,
|
||||
MediaConstraints constraints, boolean toMemory)
|
||||
throws IOException, UndeliverableMessageException
|
||||
protected void markAttachmentsUploaded(long messageId, @NonNull List<Attachment> attachments) {
|
||||
AttachmentDatabase database = DatabaseFactory.getAttachmentDatabase(context);
|
||||
|
||||
for (Attachment attachment : attachments) {
|
||||
database.markAttachmentUploaded(messageId, attachment);
|
||||
}
|
||||
}
|
||||
|
||||
protected List<Attachment> scaleAttachments(@NonNull MasterSecret masterSecret,
|
||||
@NonNull MediaConstraints constraints,
|
||||
@NonNull List<Attachment> attachments)
|
||||
throws UndeliverableMessageException
|
||||
{
|
||||
PduBody body = new PduBody();
|
||||
try {
|
||||
for (int i = 0; i < message.getBody().getPartsNum(); i++) {
|
||||
body.addPart(getResolvedPart(masterSecret, constraints, message.getBody().getPart(i), toMemory));
|
||||
AttachmentDatabase attachmentDatabase = DatabaseFactory.getAttachmentDatabase(context);
|
||||
List<Attachment> results = new LinkedList<>();
|
||||
|
||||
for (Attachment attachment : attachments) {
|
||||
try {
|
||||
if (constraints.isSatisfied(context, masterSecret, attachment)) {
|
||||
results.add(attachment);
|
||||
} else if (constraints.canResize(attachment)) {
|
||||
InputStream resized = constraints.getResizedMedia(context, masterSecret, attachment);
|
||||
results.add(attachmentDatabase.updateAttachmentData(masterSecret, attachment, resized));
|
||||
} else {
|
||||
throw new UndeliverableMessageException("Size constraints could not be met!");
|
||||
}
|
||||
} catch (IOException | MmsException e) {
|
||||
throw new UndeliverableMessageException(e);
|
||||
}
|
||||
} catch (MmsException me) {
|
||||
throw new UndeliverableMessageException(me);
|
||||
}
|
||||
return new SendReq(message.getPduHeaders(),
|
||||
body,
|
||||
message.getDatabaseMessageId(),
|
||||
message.getDatabaseMessageBox(),
|
||||
message.getSentTimestamp());
|
||||
}
|
||||
|
||||
private PduPart getResolvedPart(MasterSecret masterSecret, MediaConstraints constraints,
|
||||
PduPart part, boolean toMemory)
|
||||
throws IOException, MmsException, UndeliverableMessageException
|
||||
{
|
||||
byte[] resizedData = null;
|
||||
|
||||
if (!constraints.isSatisfied(context, masterSecret, part)) {
|
||||
if (!constraints.canResize(part)) {
|
||||
throw new UndeliverableMessageException("Size constraints could not be satisfied.");
|
||||
}
|
||||
resizedData = getResizedPartData(masterSecret, constraints, part);
|
||||
}
|
||||
|
||||
if (toMemory && part.getDataUri() != null) {
|
||||
part.setData(resizedData != null ? resizedData : MediaUtil.getPartData(context, masterSecret, part));
|
||||
}
|
||||
|
||||
if (resizedData != null) {
|
||||
part.setDataSize(resizedData.length);
|
||||
}
|
||||
return part;
|
||||
}
|
||||
|
||||
protected void markPartsUploaded(long messageId, PduBody body) {
|
||||
if (body == null) return;
|
||||
PartDatabase database = DatabaseFactory.getPartDatabase(context);
|
||||
for (int i = 0; i < body.getPartsNum(); i++) {
|
||||
database.markPartUploaded(messageId, body.getPart(i));
|
||||
}
|
||||
}
|
||||
|
||||
private byte[] getResizedPartData(MasterSecret masterSecret, MediaConstraints constraints,
|
||||
PduPart part)
|
||||
throws IOException, MmsException
|
||||
{
|
||||
Log.w(TAG, "resizing part " + part.getPartId());
|
||||
|
||||
final long oldSize = part.getDataSize();
|
||||
final byte[] data = constraints.getResizedMedia(context, masterSecret, part);
|
||||
|
||||
DatabaseFactory.getPartDatabase(context).updatePartData(masterSecret, part, new ByteArrayInputStream(data));
|
||||
Log.w(TAG, String.format("Resized part %.1fkb => %.1fkb", oldSize / 1024.0, part.getDataSize() / 1024.0));
|
||||
|
||||
return data;
|
||||
return results;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,9 +6,10 @@ import android.net.NetworkInfo;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.util.Log;
|
||||
|
||||
import org.thoughtcrime.securesms.attachments.Attachment;
|
||||
import org.thoughtcrime.securesms.attachments.AttachmentId;
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||
import org.thoughtcrime.securesms.database.PartDatabase;
|
||||
import org.thoughtcrime.securesms.database.PartDatabase.PartId;
|
||||
import org.thoughtcrime.securesms.database.AttachmentDatabase;
|
||||
import org.thoughtcrime.securesms.util.MediaUtil;
|
||||
import org.thoughtcrime.securesms.util.ServiceUtil;
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||
@@ -18,8 +19,6 @@ import org.whispersystems.jobqueue.requirements.Requirement;
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
|
||||
import ws.com.google.android.mms.pdu.PduPart;
|
||||
|
||||
public class MediaNetworkRequirement implements Requirement, ContextDependent {
|
||||
private static final long serialVersionUID = 0L;
|
||||
private static final String TAG = MediaNetworkRequirement.class.getSimpleName();
|
||||
@@ -30,14 +29,15 @@ public class MediaNetworkRequirement implements Requirement, ContextDependent {
|
||||
private final long partRowId;
|
||||
private final long partUniqueId;
|
||||
|
||||
public MediaNetworkRequirement(Context context, long messageId, PartId partId) {
|
||||
public MediaNetworkRequirement(Context context, long messageId, AttachmentId attachmentId) {
|
||||
this.context = context;
|
||||
this.messageId = messageId;
|
||||
this.partRowId = partId.getRowId();
|
||||
this.partUniqueId = partId.getUniqueId();
|
||||
this.partRowId = attachmentId.getRowId();
|
||||
this.partUniqueId = attachmentId.getUniqueId();
|
||||
}
|
||||
|
||||
@Override public void setContext(Context context) {
|
||||
@Override
|
||||
public void setContext(Context context) {
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
@@ -74,23 +74,26 @@ public class MediaNetworkRequirement implements Requirement, ContextDependent {
|
||||
|
||||
@Override
|
||||
public boolean isPresent() {
|
||||
final PartId partId = new PartId(partRowId, partUniqueId);
|
||||
final PartDatabase db = DatabaseFactory.getPartDatabase(context);
|
||||
final PduPart part = db.getPart(partId);
|
||||
if (part == null) {
|
||||
Log.w(TAG, "part was null, returning vacuous true");
|
||||
final AttachmentId attachmentId = new AttachmentId(partRowId, partUniqueId);
|
||||
final AttachmentDatabase db = DatabaseFactory.getAttachmentDatabase(context);
|
||||
final Attachment attachment = db.getAttachment(attachmentId);
|
||||
|
||||
if (attachment == null) {
|
||||
Log.w(TAG, "attachment was null, returning vacuous true");
|
||||
return true;
|
||||
}
|
||||
|
||||
Log.w(TAG, "part transfer progress is " + part.getTransferProgress());
|
||||
switch (part.getTransferProgress()) {
|
||||
case PartDatabase.TRANSFER_PROGRESS_STARTED:
|
||||
Log.w(TAG, "part transfer progress is " + attachment.getTransferState());
|
||||
switch (attachment.getTransferState()) {
|
||||
case AttachmentDatabase.TRANSFER_PROGRESS_STARTED:
|
||||
return true;
|
||||
case PartDatabase.TRANSFER_PROGRESS_AUTO_PENDING:
|
||||
case AttachmentDatabase.TRANSFER_PROGRESS_AUTO_PENDING:
|
||||
final Set<String> allowedTypes = getAllowedAutoDownloadTypes();
|
||||
final boolean isAllowed = allowedTypes.contains(MediaUtil.getDiscreteMimeType(part));
|
||||
final boolean isAllowed = allowedTypes.contains(MediaUtil.getDiscreteMimeType(attachment.getContentType()));
|
||||
|
||||
if (isAllowed) db.setTransferState(messageId, partId, PartDatabase.TRANSFER_PROGRESS_STARTED);
|
||||
/// XXX WTF -- This is *hella* gross. A requirement shouldn't have the side effect of
|
||||
// *modifying the database* just by calling isPresent().
|
||||
if (isAllowed) db.setTransferState(messageId, attachmentId, AttachmentDatabase.TRANSFER_PROGRESS_STARTED);
|
||||
return isAllowed;
|
||||
default:
|
||||
return false;
|
||||
|
||||
Reference in New Issue
Block a user