2014-11-08 19:35:58 +00:00
|
|
|
package org.thoughtcrime.securesms.jobs;
|
|
|
|
|
|
|
|
import android.content.Context;
|
|
|
|
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;
|
2015-01-15 21:35:35 +00:00
|
|
|
import org.thoughtcrime.securesms.database.documents.NetworkFailure;
|
2014-11-12 03:57:53 +00:00
|
|
|
import org.thoughtcrime.securesms.dependencies.InjectableType;
|
2014-11-08 19:35:58 +00:00
|
|
|
import org.thoughtcrime.securesms.jobs.requirements.MasterSecretRequirement;
|
2015-04-07 21:16:05 +00:00
|
|
|
import org.thoughtcrime.securesms.mms.MediaConstraints;
|
2014-11-08 19:35:58 +00:00
|
|
|
import org.thoughtcrime.securesms.mms.PartParser;
|
|
|
|
import org.thoughtcrime.securesms.recipients.Recipient;
|
2015-01-15 21:35:35 +00:00
|
|
|
import org.thoughtcrime.securesms.recipients.RecipientFactory;
|
2014-11-08 19:35:58 +00:00
|
|
|
import org.thoughtcrime.securesms.recipients.RecipientFormattingException;
|
|
|
|
import org.thoughtcrime.securesms.recipients.Recipients;
|
2015-04-07 21:16:05 +00:00
|
|
|
import org.thoughtcrime.securesms.transport.UndeliverableMessageException;
|
2014-11-12 19:15:05 +00:00
|
|
|
import org.thoughtcrime.securesms.util.Base64;
|
2014-11-08 19:35:58 +00:00
|
|
|
import org.thoughtcrime.securesms.util.GroupUtil;
|
|
|
|
import org.whispersystems.jobqueue.JobParameters;
|
|
|
|
import org.whispersystems.jobqueue.requirements.NetworkRequirement;
|
|
|
|
import org.whispersystems.textsecure.api.TextSecureMessageSender;
|
2014-11-12 03:57:53 +00:00
|
|
|
import org.whispersystems.textsecure.api.crypto.UntrustedIdentityException;
|
2014-11-08 19:35:58 +00:00
|
|
|
import org.whispersystems.textsecure.api.messages.TextSecureAttachment;
|
|
|
|
import org.whispersystems.textsecure.api.messages.TextSecureGroup;
|
|
|
|
import org.whispersystems.textsecure.api.messages.TextSecureMessage;
|
2015-02-28 00:57:32 +00:00
|
|
|
import org.whispersystems.textsecure.api.push.TextSecureAddress;
|
2014-11-12 19:35:54 +00:00
|
|
|
import org.whispersystems.textsecure.api.push.exceptions.EncapsulatedExceptions;
|
2015-01-15 21:35:35 +00:00
|
|
|
import org.whispersystems.textsecure.api.push.exceptions.NetworkFailureException;
|
2014-11-12 19:15:05 +00:00
|
|
|
import org.whispersystems.textsecure.api.util.InvalidNumberException;
|
2015-01-15 21:35:35 +00:00
|
|
|
import org.whispersystems.textsecure.internal.push.PushMessageProtos;
|
2014-11-08 19:35:58 +00:00
|
|
|
|
|
|
|
import java.io.IOException;
|
|
|
|
import java.util.LinkedList;
|
|
|
|
import java.util.List;
|
|
|
|
|
2014-11-12 03:57:53 +00:00
|
|
|
import javax.inject.Inject;
|
|
|
|
|
2014-11-08 19:35:58 +00:00
|
|
|
import ws.com.google.android.mms.MmsException;
|
|
|
|
import ws.com.google.android.mms.pdu.SendReq;
|
|
|
|
|
2014-11-12 03:57:53 +00:00
|
|
|
import static org.thoughtcrime.securesms.dependencies.TextSecureCommunicationModule.TextSecureMessageSenderFactory;
|
|
|
|
|
|
|
|
public class PushGroupSendJob extends PushSendJob implements InjectableType {
|
2014-11-08 19:35:58 +00:00
|
|
|
|
|
|
|
private static final String TAG = PushGroupSendJob.class.getSimpleName();
|
|
|
|
|
2014-11-12 03:57:53 +00:00
|
|
|
@Inject transient TextSecureMessageSenderFactory messageSenderFactory;
|
|
|
|
|
2014-11-08 19:35:58 +00:00
|
|
|
private final long messageId;
|
2015-01-15 21:35:35 +00:00
|
|
|
private final long filterRecipientId;
|
2014-11-08 19:35:58 +00:00
|
|
|
|
2015-01-15 21:35:35 +00:00
|
|
|
public PushGroupSendJob(Context context, long messageId, String destination, long filterRecipientId) {
|
2014-11-08 19:35:58 +00:00
|
|
|
super(context, JobParameters.newBuilder()
|
|
|
|
.withPersistence()
|
|
|
|
.withGroupId(destination)
|
|
|
|
.withRequirement(new MasterSecretRequirement(context))
|
|
|
|
.withRequirement(new NetworkRequirement(context))
|
|
|
|
.withRetryCount(5)
|
|
|
|
.create());
|
|
|
|
|
2015-01-15 21:35:35 +00:00
|
|
|
this.messageId = messageId;
|
|
|
|
this.filterRecipientId = filterRecipientId;
|
2014-11-08 19:35:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onAdded() {
|
2015-01-15 21:35:35 +00:00
|
|
|
DatabaseFactory.getMmsDatabase(context)
|
|
|
|
.markAsSending(messageId);
|
2014-11-08 19:35:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2015-01-15 21:35:35 +00:00
|
|
|
public void onSend(MasterSecret masterSecret)
|
2015-03-03 19:44:49 +00:00
|
|
|
throws MmsException, IOException, NoSuchMessageException
|
2015-01-15 21:35:35 +00:00
|
|
|
{
|
2014-11-12 03:57:53 +00:00
|
|
|
MmsDatabase database = DatabaseFactory.getMmsDatabase(context);
|
|
|
|
SendReq message = database.getOutgoingMessage(masterSecret, messageId);
|
2014-11-08 19:35:58 +00:00
|
|
|
|
|
|
|
try {
|
2015-01-15 21:35:35 +00:00
|
|
|
deliver(masterSecret, message, filterRecipientId);
|
2014-11-08 19:35:58 +00:00
|
|
|
|
|
|
|
database.markAsPush(messageId);
|
|
|
|
database.markAsSecure(messageId);
|
|
|
|
database.markAsSent(messageId, "push".getBytes(), 0);
|
2015-04-07 21:16:05 +00:00
|
|
|
} catch (InvalidNumberException | RecipientFormattingException | UndeliverableMessageException e) {
|
2014-11-08 19:35:58 +00:00
|
|
|
Log.w(TAG, e);
|
|
|
|
database.markAsSentFailed(messageId);
|
|
|
|
notifyMediaMessageDeliveryFailed(context, messageId);
|
|
|
|
} catch (EncapsulatedExceptions e) {
|
|
|
|
Log.w(TAG, e);
|
2015-01-15 21:35:35 +00:00
|
|
|
List<NetworkFailure> failures = new LinkedList<>();
|
|
|
|
|
|
|
|
for (NetworkFailureException nfe : e.getNetworkExceptions()) {
|
|
|
|
Recipient recipient = RecipientFactory.getRecipientsFromString(context, nfe.getE164number(), false).getPrimaryRecipient();
|
|
|
|
failures.add(new NetworkFailure(recipient.getRecipientId()));
|
2014-11-08 19:35:58 +00:00
|
|
|
}
|
|
|
|
|
2015-01-15 21:35:35 +00:00
|
|
|
// for (UnregisteredUserException uue : e.getUnregisteredUserExceptions()) {
|
|
|
|
// Recipient recipient = RecipientFactory.getRecipientsFromString(context, uue.getE164Number(), false).getPrimaryRecipient();
|
|
|
|
// failures.add(new NetworkFailure(recipient.getRecipientId(), NetworkFailure.UNREGISTERED_FAILURE));
|
|
|
|
// }
|
|
|
|
|
2014-11-08 19:35:58 +00:00
|
|
|
for (UntrustedIdentityException uie : e.getUntrustedIdentityExceptions()) {
|
2015-01-15 21:35:35 +00:00
|
|
|
Recipient recipient = RecipientFactory.getRecipientsFromString(context, uie.getE164Number(), false).getPrimaryRecipient();
|
|
|
|
database.addMismatchedIdentity(messageId, recipient.getRecipientId(), uie.getIdentityKey());
|
2014-11-08 19:35:58 +00:00
|
|
|
}
|
|
|
|
|
2015-01-15 21:35:35 +00:00
|
|
|
database.addFailures(messageId, failures);
|
|
|
|
database.markAsSentFailed(messageId);
|
|
|
|
database.markAsPush(messageId);
|
|
|
|
|
2014-11-08 19:35:58 +00:00
|
|
|
notifyMediaMessageDeliveryFailed(context, messageId);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2014-11-12 05:11:57 +00:00
|
|
|
public boolean onShouldRetryThrowable(Exception exception) {
|
|
|
|
if (exception instanceof IOException) return true;
|
2014-11-12 03:57:53 +00:00
|
|
|
return false;
|
2014-11-08 19:35:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2014-11-12 03:57:53 +00:00
|
|
|
public void onCanceled() {
|
|
|
|
DatabaseFactory.getMmsDatabase(context).markAsSentFailed(messageId);
|
2014-11-08 19:35:58 +00:00
|
|
|
}
|
|
|
|
|
2015-01-15 21:35:35 +00:00
|
|
|
private void deliver(MasterSecret masterSecret, SendReq message, long filterRecipientId)
|
2015-04-07 21:16:05 +00:00
|
|
|
throws IOException, RecipientFormattingException, InvalidNumberException,
|
|
|
|
EncapsulatedExceptions, UndeliverableMessageException
|
2014-11-08 19:35:58 +00:00
|
|
|
{
|
2015-04-07 21:16:05 +00:00
|
|
|
message = getResolvedMessage(masterSecret, message, MediaConstraints.PUSH_CONSTRAINTS, false);
|
|
|
|
|
2014-11-12 03:57:53 +00:00
|
|
|
TextSecureMessageSender messageSender = messageSenderFactory.create(masterSecret);
|
2014-11-08 19:35:58 +00:00
|
|
|
byte[] groupId = GroupUtil.getDecodedId(message.getTo()[0].getString());
|
|
|
|
Recipients recipients = DatabaseFactory.getGroupDatabase(context).getGroupMembers(groupId, false);
|
2014-12-12 09:03:24 +00:00
|
|
|
List<TextSecureAttachment> attachments = getAttachments(masterSecret, message);
|
2015-02-28 00:57:32 +00:00
|
|
|
List<TextSecureAddress> addresses;
|
2015-01-15 21:35:35 +00:00
|
|
|
|
|
|
|
if (filterRecipientId >= 0) addresses = getPushAddresses(filterRecipientId);
|
|
|
|
else addresses = getPushAddresses(recipients);
|
2014-11-08 19:35:58 +00:00
|
|
|
|
|
|
|
if (MmsSmsColumns.Types.isGroupUpdate(message.getDatabaseMessageBox()) ||
|
|
|
|
MmsSmsColumns.Types.isGroupQuit(message.getDatabaseMessageBox()))
|
|
|
|
{
|
|
|
|
String content = PartParser.getMessageText(message.getBody());
|
|
|
|
|
|
|
|
if (content != null && !content.trim().isEmpty()) {
|
|
|
|
PushMessageProtos.PushMessageContent.GroupContext groupContext = PushMessageProtos.PushMessageContent.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);
|
|
|
|
TextSecureMessage groupMessage = new TextSecureMessage(message.getSentTimestamp(), group, null, null);
|
|
|
|
|
|
|
|
messageSender.sendMessage(addresses, groupMessage);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
String body = PartParser.getMessageText(message.getBody());
|
|
|
|
TextSecureGroup group = new TextSecureGroup(groupId);
|
|
|
|
TextSecureMessage groupMessage = new TextSecureMessage(message.getSentTimestamp(), group, attachments, body);
|
|
|
|
|
|
|
|
messageSender.sendMessage(addresses, groupMessage);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-02-28 00:57:32 +00:00
|
|
|
private List<TextSecureAddress> getPushAddresses(Recipients recipients) throws InvalidNumberException {
|
|
|
|
List<TextSecureAddress> addresses = new LinkedList<>();
|
2014-11-08 19:35:58 +00:00
|
|
|
|
|
|
|
for (Recipient recipient : recipients.getRecipientsList()) {
|
2015-03-03 19:44:49 +00:00
|
|
|
addresses.add(getPushAddress(recipient.getNumber()));
|
2014-11-08 19:35:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return addresses;
|
|
|
|
}
|
|
|
|
|
2015-02-28 00:57:32 +00:00
|
|
|
private List<TextSecureAddress> getPushAddresses(long filterRecipientId) throws InvalidNumberException {
|
|
|
|
List<TextSecureAddress> addresses = new LinkedList<>();
|
2015-03-03 19:44:49 +00:00
|
|
|
addresses.add(getPushAddress(RecipientFactory.getRecipientForId(context, filterRecipientId, false).getNumber()));
|
2015-01-15 21:35:35 +00:00
|
|
|
return addresses;
|
|
|
|
}
|
|
|
|
|
2014-11-08 19:35:58 +00:00
|
|
|
}
|