org.whispersystems.signalservice.api.messages.SendMessageResult Java Examples

The following examples show how to use org.whispersystems.signalservice.api.messages.SendMessageResult. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: SignalServiceMessageSender.java    From mollyim-android with GNU General Public License v3.0 6 votes vote down vote up
/**
 * Send a message to a group.
 *
 * @param recipients The group members.
 * @param message The group message.
 * @throws IOException
 */
public List<SendMessageResult> sendMessage(List<SignalServiceAddress>             recipients,
                                           List<Optional<UnidentifiedAccessPair>> unidentifiedAccess,
                                           boolean                                isRecipientUpdate,
                                           SignalServiceDataMessage               message)
    throws IOException, UntrustedIdentityException
{
  byte[]                  content            = createMessageContent(message);
  long                    timestamp          = message.getTimestamp();
  List<SendMessageResult> results            = sendMessage(recipients, getTargetUnidentifiedAccess(unidentifiedAccess), timestamp, content, false);
  boolean                 needsSyncInResults = false;

  for (SendMessageResult result : results) {
    if (result.getSuccess() != null && result.getSuccess().isNeedsSync()) {
      needsSyncInResults = true;
      break;
    }
  }

  if (needsSyncInResults || isMultiDevice.get()) {
    byte[] syncMessage = createMultiDeviceSentTranscriptContent(content, Optional.<SignalServiceAddress>absent(), timestamp, results, isRecipientUpdate);
    sendMessage(localAddress, Optional.<UnidentifiedAccess>absent(), timestamp, syncMessage, false);
  }

  return results;
}
 
Example #2
Source File: SignalServiceMessageSender.java    From mollyim-android with GNU General Public License v3.0 6 votes vote down vote up
private void sendMessage(VerifiedMessage message, Optional<UnidentifiedAccessPair> unidentifiedAccess)
    throws IOException, UntrustedIdentityException
{
  byte[] nullMessageBody = DataMessage.newBuilder()
                                      .setBody(Base64.encodeBytes(Util.getRandomLengthBytes(140)))
                                      .build()
                                      .toByteArray();

  NullMessage nullMessage = NullMessage.newBuilder()
                                       .setPadding(ByteString.copyFrom(nullMessageBody))
                                       .build();

  byte[] content          = Content.newBuilder()
                                   .setNullMessage(nullMessage)
                                   .build()
                                   .toByteArray();

  SendMessageResult result = sendMessage(message.getDestination(), getTargetUnidentifiedAccess(unidentifiedAccess), message.getTimestamp(), content, false);

  if (result.getSuccess().isNeedsSync()) {
    byte[] syncMessage = createMultiDeviceVerifiedContent(message, nullMessage.toByteArray());
    sendMessage(localAddress, Optional.<UnidentifiedAccess>absent(), message.getTimestamp(), syncMessage, false);
  }
}
 
Example #3
Source File: Manager.java    From signal-cli with GNU General Public License v3.0 6 votes vote down vote up
private SendMessageResult sendSelfMessage(SignalServiceDataMessage message) throws IOException {
    SignalServiceMessageSender messageSender = getMessageSender();

    SignalServiceAddress recipient = account.getSelfAddress();

    final Optional<UnidentifiedAccessPair> unidentifiedAccess = getAccessFor(recipient);
    SentTranscriptMessage transcript = new SentTranscriptMessage(Optional.of(recipient),
            message.getTimestamp(),
            message,
            message.getExpiresInSeconds(),
            Collections.singletonMap(recipient, unidentifiedAccess.isPresent()),
            false);
    SignalServiceSyncMessage syncMessage = SignalServiceSyncMessage.forSentTranscript(transcript);

    try {
        messageSender.sendMessage(syncMessage, unidentifiedAccess);
        return SendMessageResult.success(recipient, unidentifiedAccess.isPresent(), false);
    } catch (UntrustedIdentityException e) {
        account.getSignalProtocolStore().saveIdentity(resolveSignalServiceAddress(e.getIdentifier()), e.getIdentityKey(), TrustLevel.UNTRUSTED);
        return SendMessageResult.identityFailure(recipient, e.getIdentityKey());
    }
}
 
Example #4
Source File: Manager.java    From signal-cli with GNU General Public License v3.0 6 votes vote down vote up
/**
 * This method throws an EncapsulatedExceptions exception instead of returning a list of SendMessageResult.
 */
private long sendMessageLegacy(SignalServiceDataMessage.Builder messageBuilder, Collection<SignalServiceAddress> recipients)
        throws EncapsulatedExceptions, IOException {
    final long timestamp = System.currentTimeMillis();
    messageBuilder.withTimestamp(timestamp);
    List<SendMessageResult> results = sendMessage(messageBuilder, recipients);

    List<UntrustedIdentityException> untrustedIdentities = new LinkedList<>();
    List<UnregisteredUserException> unregisteredUsers = new LinkedList<>();
    List<NetworkFailureException> networkExceptions = new LinkedList<>();

    for (SendMessageResult result : results) {
        if (result.isUnregisteredFailure()) {
            unregisteredUsers.add(new UnregisteredUserException(result.getAddress().getLegacyIdentifier(), null));
        } else if (result.isNetworkFailure()) {
            networkExceptions.add(new NetworkFailureException(result.getAddress().getLegacyIdentifier(), null));
        } else if (result.getIdentityFailure() != null) {
            untrustedIdentities.add(new UntrustedIdentityException("Untrusted", result.getAddress().getLegacyIdentifier(), result.getIdentityFailure().getIdentityKey()));
        }
    }
    if (!untrustedIdentities.isEmpty() || !unregisteredUsers.isEmpty() || !networkExceptions.isEmpty()) {
        throw new EncapsulatedExceptions(untrustedIdentities, unregisteredUsers, networkExceptions);
    }
    return timestamp;
}
 
Example #5
Source File: SignalServiceMessageSender.java    From libsignal-service-java with GNU General Public License v3.0 6 votes vote down vote up
private void sendMessage(VerifiedMessage message, Optional<UnidentifiedAccessPair> unidentifiedAccess)
    throws IOException, UntrustedIdentityException
{
  byte[] nullMessageBody = DataMessage.newBuilder()
                                      .setBody(Base64.encodeBytes(Util.getRandomLengthBytes(140)))
                                      .build()
                                      .toByteArray();

  NullMessage nullMessage = NullMessage.newBuilder()
                                       .setPadding(ByteString.copyFrom(nullMessageBody))
                                       .build();

  byte[] content          = Content.newBuilder()
                                   .setNullMessage(nullMessage)
                                   .build()
                                   .toByteArray();

  SendMessageResult result = sendMessage(message.getDestination(), getTargetUnidentifiedAccess(unidentifiedAccess), message.getTimestamp(), content, false);

  if (result.getSuccess().isNeedsSync()) {
    byte[] syncMessage = createMultiDeviceVerifiedContent(message, nullMessage.toByteArray());
    sendMessage(localAddress, Optional.<UnidentifiedAccess>absent(), message.getTimestamp(), syncMessage, false);
  }
}
 
Example #6
Source File: SignalServiceMessageSender.java    From libsignal-service-java with GNU General Public License v3.0 6 votes vote down vote up
/**
 * Send a message to a group.
 *
 * @param recipients The group members.
 * @param message The group message.
 * @throws IOException
 */
public List<SendMessageResult> sendMessage(List<SignalServiceAddress>             recipients,
                                           List<Optional<UnidentifiedAccessPair>> unidentifiedAccess,
                                           boolean                                isRecipientUpdate,
                                           SignalServiceDataMessage               message)
    throws IOException, UntrustedIdentityException
{
  byte[]                  content            = createMessageContent(message);
  long                    timestamp          = message.getTimestamp();
  List<SendMessageResult> results            = sendMessage(recipients, getTargetUnidentifiedAccess(unidentifiedAccess), timestamp, content, false);
  boolean                 needsSyncInResults = false;

  for (SendMessageResult result : results) {
    if (result.getSuccess() != null && result.getSuccess().isNeedsSync()) {
      needsSyncInResults = true;
      break;
    }
  }

  if (needsSyncInResults || isMultiDevice.get()) {
    byte[] syncMessage = createMultiDeviceSentTranscriptContent(content, Optional.<SignalServiceAddress>absent(), timestamp, results, isRecipientUpdate);
    sendMessage(localAddress, Optional.<UnidentifiedAccess>absent(), timestamp, syncMessage, false);
  }

  return results;
}
 
Example #7
Source File: Manager.java    From signal-cli with GNU General Public License v3.0 5 votes vote down vote up
private SendMessageResult sendMessage(SignalServiceAddress address, SignalServiceDataMessage message) throws IOException {
    SignalServiceMessageSender messageSender = getMessageSender();

    try {
        return messageSender.sendMessage(address, getAccessFor(address), message);
    } catch (UntrustedIdentityException e) {
        account.getSignalProtocolStore().saveIdentity(resolveSignalServiceAddress(e.getIdentifier()), e.getIdentityKey(), TrustLevel.UNTRUSTED);
        return SendMessageResult.identityFailure(address, e.getIdentityKey());
    }
}
 
Example #8
Source File: SignalServiceMessageSender.java    From mollyim-android with GNU General Public License v3.0 5 votes vote down vote up
/**
 * Send a message to a single recipient.
 *
 * @param recipient The message's destination.
 * @param message The message.
 * @throws UntrustedIdentityException
 * @throws IOException
 */
public SendMessageResult sendMessage(SignalServiceAddress             recipient,
                                     Optional<UnidentifiedAccessPair> unidentifiedAccess,
                                     SignalServiceDataMessage         message)
    throws UntrustedIdentityException, IOException
{
  byte[]            content   = createMessageContent(message);
  long              timestamp = message.getTimestamp();
  SendMessageResult result    = sendMessage(recipient, getTargetUnidentifiedAccess(unidentifiedAccess), timestamp, content, false);

  if (result.getSuccess() != null && result.getSuccess().isNeedsSync()) {
    byte[] syncMessage = createMultiDeviceSentTranscriptContent(content, Optional.of(recipient), timestamp, Collections.singletonList(result), false);
    sendMessage(localAddress, Optional.<UnidentifiedAccess>absent(), timestamp, syncMessage, false);
  }

  if (message.isEndSession()) {
    if (recipient.getUuid().isPresent()) {
      store.deleteAllSessions(recipient.getUuid().get().toString());
    }
    if (recipient.getNumber().isPresent()) {
      store.deleteAllSessions(recipient.getNumber().get());
    }

    if (eventListener.isPresent()) {
      eventListener.get().onSecurityEvent(recipient);
    }
  }

  return result;
}
 
Example #9
Source File: SignalServiceMessageSender.java    From libsignal-service-java with GNU General Public License v3.0 5 votes vote down vote up
private byte[] createMultiDeviceSentTranscriptContent(SentTranscriptMessage transcript, Optional<UnidentifiedAccessPair> unidentifiedAccess) throws IOException {
  SignalServiceAddress address = transcript.getDestination().get();
  SendMessageResult    result  = SendMessageResult.success(address, unidentifiedAccess.isPresent(), true);

  return createMultiDeviceSentTranscriptContent(createMessageContent(transcript.getMessage()),
                                                Optional.of(address),
                                                transcript.getTimestamp(),
                                                Collections.singletonList(result),
                                                false);
}
 
Example #10
Source File: SignalServiceMessageSender.java    From libsignal-service-java with GNU General Public License v3.0 5 votes vote down vote up
/**
 * Send a message to a single recipient.
 *
 * @param recipient The message's destination.
 * @param message The message.
 * @throws UntrustedIdentityException
 * @throws IOException
 */
public SendMessageResult sendMessage(SignalServiceAddress             recipient,
                                     Optional<UnidentifiedAccessPair> unidentifiedAccess,
                                     SignalServiceDataMessage         message)
    throws UntrustedIdentityException, IOException
{
  byte[]            content   = createMessageContent(message);
  long              timestamp = message.getTimestamp();
  SendMessageResult result    = sendMessage(recipient, getTargetUnidentifiedAccess(unidentifiedAccess), timestamp, content, false);

  if (result.getSuccess() != null && result.getSuccess().isNeedsSync()) {
    byte[] syncMessage = createMultiDeviceSentTranscriptContent(content, Optional.of(recipient), timestamp, Collections.singletonList(result), false);
    sendMessage(localAddress, Optional.<UnidentifiedAccess>absent(), timestamp, syncMessage, false);
  }

  if (message.isEndSession()) {
    if (recipient.getUuid().isPresent()) {
      store.deleteAllSessions(recipient.getUuid().get().toString());
    }
    if (recipient.getNumber().isPresent()) {
      store.deleteAllSessions(recipient.getNumber().get());
    }

    if (eventListener.isPresent()) {
      eventListener.get().onSecurityEvent(recipient);
    }
  }

  return result;
}
 
Example #11
Source File: RemoteDeleteSendJob.java    From mollyim-android with GNU General Public License v3.0 5 votes vote down vote up
private @NonNull List<Recipient> deliver(@NonNull Recipient conversationRecipient, @NonNull List<Recipient> destinations, long targetSentTimestamp)
    throws IOException, UntrustedIdentityException
{
  SignalServiceMessageSender             messageSender      = ApplicationDependencies.getSignalServiceMessageSender();
  List<SignalServiceAddress>             addresses          = Stream.of(destinations).map(t -> RecipientUtil.toSignalServiceAddress(context, t)).toList();
  List<Optional<UnidentifiedAccessPair>> unidentifiedAccess = Stream.of(destinations).map(recipient -> UnidentifiedAccessUtil.getAccessFor(context, recipient)).toList();
  SignalServiceDataMessage.Builder       dataMessage        = SignalServiceDataMessage.newBuilder()
                                                                                      .withTimestamp(System.currentTimeMillis())
                                                                                      .withRemoteDelete(new SignalServiceDataMessage.RemoteDelete(targetSentTimestamp));

  if (conversationRecipient.isGroup()) {
    GroupUtil.setDataMessageGroupContext(context, dataMessage, conversationRecipient.requireGroupId().requirePush());
  }

  List<SendMessageResult> results = messageSender.sendMessage(addresses, unidentifiedAccess, false, dataMessage.build());

  Stream.of(results)
        .filter(r -> r.getIdentityFailure() != null)
        .map(SendMessageResult::getAddress)
        .map(a -> Recipient.externalPush(context, a))
        .forEach(r -> Log.w(TAG, "Identity failure for " + r.getId()));

  Stream.of(results)
        .filter(SendMessageResult::isUnregisteredFailure)
        .map(SendMessageResult::getAddress)
        .map(a -> Recipient.externalPush(context, a))
        .forEach(r -> Log.w(TAG, "Unregistered failure for " + r.getId()));

  return Stream.of(results)
               .filter(r -> r.getSuccess() != null || r.getIdentityFailure() != null || r.isUnregisteredFailure())
               .map(SendMessageResult::getAddress)
               .map(a -> Recipient.externalPush(context, a))
               .toList();
}
 
Example #12
Source File: ReactionSendJob.java    From mollyim-android with GNU General Public License v3.0 5 votes vote down vote up
private @NonNull List<Recipient> deliver(@NonNull Recipient conversationRecipient, @NonNull List<Recipient> destinations, @NonNull Recipient targetAuthor, long targetSentTimestamp)
    throws IOException, UntrustedIdentityException
{
  SignalServiceMessageSender             messageSender      = ApplicationDependencies.getSignalServiceMessageSender();
  List<SignalServiceAddress>             addresses          = Stream.of(destinations).map(t -> RecipientUtil.toSignalServiceAddress(context, t)).toList();
  List<Optional<UnidentifiedAccessPair>> unidentifiedAccess = Stream.of(destinations).map(recipient -> UnidentifiedAccessUtil.getAccessFor(context, recipient)).toList();
  SignalServiceDataMessage.Builder       dataMessage        = SignalServiceDataMessage.newBuilder()
                                                                                      .withTimestamp(System.currentTimeMillis())
                                                                                      .withReaction(buildReaction(context, reaction, remove, targetAuthor, targetSentTimestamp));

  if (conversationRecipient.isGroup()) {
    GroupUtil.setDataMessageGroupContext(context, dataMessage, conversationRecipient.requireGroupId().requirePush());
  }


  List<SendMessageResult> results = messageSender.sendMessage(addresses, unidentifiedAccess, false, dataMessage.build());

  Stream.of(results)
        .filter(r -> r.getIdentityFailure() != null)
        .map(SendMessageResult::getAddress)
        .map(a -> Recipient.externalPush(context, a))
        .forEach(r -> Log.w(TAG, "Identity failure for " + r.getId()));

  Stream.of(results)
        .filter(SendMessageResult::isUnregisteredFailure)
        .map(SendMessageResult::getAddress)
        .map(a -> Recipient.externalPush(context, a))
        .forEach(r -> Log.w(TAG, "Unregistered failure for " + r.getId()));


  return Stream.of(results)
               .filter(r -> r.getSuccess() != null || r.getIdentityFailure() != null || r.isUnregisteredFailure())
               .map(SendMessageResult::getAddress)
               .map(a -> Recipient.externalPush(context, a))
               .toList();
}
 
Example #13
Source File: ProfileKeySendJob.java    From mollyim-android with GNU General Public License v3.0 5 votes vote down vote up
private List<Recipient> deliver(@NonNull Recipient conversationRecipient, @NonNull List<Recipient> destinations) throws IOException, UntrustedIdentityException {
  SignalServiceMessageSender             messageSender      = ApplicationDependencies.getSignalServiceMessageSender();
  List<SignalServiceAddress>             addresses          = Stream.of(destinations).map(t -> RecipientUtil.toSignalServiceAddress(context, t)).toList();
  List<Optional<UnidentifiedAccessPair>> unidentifiedAccess = Stream.of(destinations).map(recipient -> UnidentifiedAccessUtil.getAccessFor(context, recipient)).toList();
  SignalServiceDataMessage.Builder       dataMessage        = SignalServiceDataMessage.newBuilder()
                                                                                      .asProfileKeyUpdate(true)
                                                                                      .withTimestamp(System.currentTimeMillis())
                                                                                      .withProfileKey(Recipient.self().resolve().getProfileKey());

  if (conversationRecipient.isGroup()) {
    dataMessage.asGroupMessage(new SignalServiceGroup(conversationRecipient.requireGroupId().getDecodedId()));
  }

  List<SendMessageResult> results = messageSender.sendMessage(addresses, unidentifiedAccess, false, dataMessage.build());

  Stream.of(results)
        .filter(r -> r.getIdentityFailure() != null)
        .map(SendMessageResult::getAddress)
        .map(a -> Recipient.externalPush(context, a))
        .forEach(r -> Log.w(TAG, "Identity failure for " + r.getId()));

  Stream.of(results)
        .filter(SendMessageResult::isUnregisteredFailure)
        .map(SendMessageResult::getAddress)
        .map(a -> Recipient.externalPush(context, a))
        .forEach(r -> Log.w(TAG, "Unregistered failure for " + r.getId()));


  return Stream.of(results)
               .filter(r -> r.getSuccess() != null || r.getIdentityFailure() != null || r.isUnregisteredFailure())
               .map(SendMessageResult::getAddress)
               .map(a -> Recipient.externalPush(context, a))
               .toList();
}
 
Example #14
Source File: LeaveGroupJob.java    From mollyim-android with GNU General Public License v3.0 5 votes vote down vote up
private static @NonNull List<Recipient> deliver(@NonNull Context context,
                                                @NonNull GroupId.Push groupId,
                                                @NonNull String name,
                                                @NonNull List<RecipientId> members,
                                                @NonNull List<RecipientId> destinations)
    throws IOException, UntrustedIdentityException
{
  SignalServiceMessageSender             messageSender      = ApplicationDependencies.getSignalServiceMessageSender();
  List<SignalServiceAddress>             addresses          = Stream.of(destinations).map(Recipient::resolved).map(t -> RecipientUtil.toSignalServiceAddress(context, t)).toList();
  List<SignalServiceAddress>             memberAddresses    = Stream.of(members).map(Recipient::resolved).map(t -> RecipientUtil.toSignalServiceAddress(context, t)).toList();
  List<Optional<UnidentifiedAccessPair>> unidentifiedAccess = Stream.of(destinations).map(Recipient::resolved).map(recipient -> UnidentifiedAccessUtil.getAccessFor(context, recipient)).toList();
  SignalServiceGroup                     serviceGroup       = new SignalServiceGroup(SignalServiceGroup.Type.QUIT, groupId.getDecodedId(), name, memberAddresses, null);
  SignalServiceDataMessage.Builder       dataMessage        = SignalServiceDataMessage.newBuilder()
                                                                                      .withTimestamp(System.currentTimeMillis())
                                                                                      .asGroupMessage(serviceGroup);


  List<SendMessageResult> results = messageSender.sendMessage(addresses, unidentifiedAccess, false, dataMessage.build());

  Stream.of(results)
        .filter(r -> r.getIdentityFailure() != null)
        .map(SendMessageResult::getAddress)
        .map(a -> Recipient.externalPush(context, a))
        .forEach(r -> Log.w(TAG, "Identity failure for " + r.getId()));

  Stream.of(results)
        .filter(SendMessageResult::isUnregisteredFailure)
        .map(SendMessageResult::getAddress)
        .map(a -> Recipient.externalPush(context, a))
        .forEach(r -> Log.w(TAG, "Unregistered failure for " + r.getId()));


  return Stream.of(results)
               .filter(r -> r.getSuccess() != null || r.getIdentityFailure() != null || r.isUnregisteredFailure())
               .map(SendMessageResult::getAddress)
               .map(a -> Recipient.externalPush(context, a))
               .toList();
}
 
Example #15
Source File: SignalServiceMessageSender.java    From mollyim-android with GNU General Public License v3.0 5 votes vote down vote up
private byte[] createMultiDeviceSentTranscriptContent(SentTranscriptMessage transcript, Optional<UnidentifiedAccessPair> unidentifiedAccess) throws IOException {
  SignalServiceAddress address = transcript.getDestination().get();
  SendMessageResult    result  = SendMessageResult.success(address, unidentifiedAccess.isPresent(), true);

  return createMultiDeviceSentTranscriptContent(createMessageContent(transcript.getMessage()),
                                                Optional.of(address),
                                                transcript.getTimestamp(),
                                                Collections.singletonList(result),
                                                false);
}
 
Example #16
Source File: PushGroupSendJob.java    From mollyim-android with GNU General Public License v3.0 4 votes vote down vote up
@Override
public void onPushSend()
    throws IOException, MmsException, NoSuchMessageException,  RetryLaterException
{
  MmsDatabase               database                   = DatabaseFactory.getMmsDatabase(context);
  OutgoingMediaMessage      message                    = database.getOutgoingMessage(messageId);
  List<NetworkFailure>      existingNetworkFailures    = message.getNetworkFailures();
  List<IdentityKeyMismatch> existingIdentityMismatches = message.getIdentityKeyMismatches();

  if (database.isSent(messageId)) {
    log(TAG, "Message " + messageId + " was already sent. Ignoring.");
    return;
  }

  Recipient groupRecipient = message.getRecipient().fresh();

  if (!groupRecipient.isPushGroup()) {
    throw new MmsException("Message recipient isn't a group!");
  }

  try {
    log(TAG, "Sending message: " + messageId);

    if (!groupRecipient.resolve().isProfileSharing() && !database.isGroupQuitMessage(messageId)) {
      RecipientUtil.shareProfileIfFirstSecureMessage(context, groupRecipient);
    }

    List<Recipient> target;

    if      (filterRecipient != null)            target = Collections.singletonList(Recipient.resolved(filterRecipient));
    else if (!existingNetworkFailures.isEmpty()) target = Stream.of(existingNetworkFailures).map(nf -> Recipient.resolved(nf.getRecipientId(context))).toList();
    else                                         target = getGroupMessageRecipients(groupRecipient.requireGroupId(), messageId);

    Map<String, Recipient> idByE164 = Stream.of(target).filter(Recipient::hasE164).collect(Collectors.toMap(Recipient::requireE164, r -> r));
    Map<UUID, Recipient>   idByUuid = Stream.of(target).filter(Recipient::hasUuid).collect(Collectors.toMap(Recipient::requireUuid, r -> r));

    List<SendMessageResult>   results = deliver(message, groupRecipient, target);
    Log.i(TAG, JobLogger.format(this, "Finished send."));

    List<NetworkFailure>             networkFailures           = Stream.of(results).filter(SendMessageResult::isNetworkFailure).map(result -> new NetworkFailure(findId(result.getAddress(), idByE164, idByUuid))).toList();
    List<IdentityKeyMismatch>        identityMismatches        = Stream.of(results).filter(result -> result.getIdentityFailure() != null).map(result -> new IdentityKeyMismatch(findId(result.getAddress(), idByE164, idByUuid), result.getIdentityFailure().getIdentityKey())).toList();
    List<SendMessageResult>          successes                 = Stream.of(results).filter(result -> result.getSuccess() != null).toList();
    List<Pair<RecipientId, Boolean>> successUnidentifiedStatus = Stream.of(successes).map(result -> new Pair<>(findId(result.getAddress(), idByE164, idByUuid), result.getSuccess().isUnidentified())).toList();
    Set<RecipientId>                 successIds                = Stream.of(successUnidentifiedStatus).map(Pair::first).collect(Collectors.toSet());
    List<NetworkFailure>             resolvedNetworkFailures   = Stream.of(existingNetworkFailures).filter(failure -> successIds.contains(failure.getRecipientId(context))).toList();
    List<IdentityKeyMismatch>        resolvedIdentityFailures = Stream.of(existingIdentityMismatches).filter(failure -> successIds.contains(failure.getRecipientId(context))).toList();

    for (NetworkFailure resolvedFailure : resolvedNetworkFailures) {
      database.removeFailure(messageId, resolvedFailure);
      existingNetworkFailures.remove(resolvedFailure);
    }

    for (IdentityKeyMismatch resolvedIdentity : resolvedIdentityFailures) {
      database.removeMismatchedIdentity(messageId, resolvedIdentity.getRecipientId(context), resolvedIdentity.getIdentityKey());
      existingIdentityMismatches.remove(resolvedIdentity);
    }

    if (!networkFailures.isEmpty()) {
      database.addFailures(messageId, networkFailures);
    }

    for (IdentityKeyMismatch mismatch : identityMismatches) {
      database.addMismatchedIdentity(messageId, mismatch.getRecipientId(context), mismatch.getIdentityKey());
    }

    DatabaseFactory.getGroupReceiptDatabase(context).setUnidentified(successUnidentifiedStatus, messageId);

    if (existingNetworkFailures.isEmpty() && networkFailures.isEmpty() && identityMismatches.isEmpty() && existingIdentityMismatches.isEmpty()) {
      database.markAsSent(messageId, true);

      markAttachmentsUploaded(messageId, message.getAttachments());

      if (message.getExpiresIn() > 0 && !message.isExpirationUpdate()) {
        database.markExpireStarted(messageId);
        ApplicationContext.getInstance(context)
                          .getExpiringMessageManager()
                          .scheduleDeletion(messageId, true, message.getExpiresIn());
      }

      if (message.isViewOnce()) {
        DatabaseFactory.getAttachmentDatabase(context).deleteAttachmentFilesForViewOnceMessage(messageId);
      }
    } else if (!networkFailures.isEmpty()) {
      throw new RetryLaterException();
    } else if (!identityMismatches.isEmpty()) {
      database.markAsSentFailed(messageId);
      notifyMediaMessageDeliveryFailed(context, messageId);
    }
  } catch (UntrustedIdentityException | UndeliverableMessageException e) {
    warn(TAG, e);
    database.markAsSentFailed(messageId);
    notifyMediaMessageDeliveryFailed(context, messageId);
  }
}
 
Example #17
Source File: SignalServiceMessageSender.java    From libsignal-service-java with GNU General Public License v3.0 4 votes vote down vote up
private byte[] createMultiDeviceSentTranscriptContent(byte[] content, Optional<SignalServiceAddress> recipient,
                                                      long timestamp, List<SendMessageResult> sendMessageResults,
                                                      boolean isRecipientUpdate)
{
  try {
    Content.Builder          container   = Content.newBuilder();
    SyncMessage.Builder      syncMessage = createSyncMessageBuilder();
    SyncMessage.Sent.Builder sentMessage = SyncMessage.Sent.newBuilder();
    DataMessage              dataMessage = Content.parseFrom(content).getDataMessage();

    sentMessage.setTimestamp(timestamp);
    sentMessage.setMessage(dataMessage);

    for (SendMessageResult result : sendMessageResults) {
      if (result.getSuccess() != null) {
        SyncMessage.Sent.UnidentifiedDeliveryStatus.Builder builder = SyncMessage.Sent.UnidentifiedDeliveryStatus.newBuilder();

        if (result.getAddress().getUuid().isPresent()) {
          builder = builder.setDestinationUuid(result.getAddress().getUuid().get().toString());
        }

        if (result.getAddress().getNumber().isPresent()) {
          builder = builder.setDestinationE164(result.getAddress().getNumber().get());
        }

        builder.setUnidentified(result.getSuccess().isUnidentified());

        sentMessage.addUnidentifiedStatus(builder.build());
      }
    }

    if (recipient.isPresent()) {
      if (recipient.get().getUuid().isPresent())   sentMessage.setDestinationUuid(recipient.get().getUuid().get().toString());
      if (recipient.get().getNumber().isPresent()) sentMessage.setDestinationE164(recipient.get().getNumber().get());
    }

    if (dataMessage.getExpireTimer() > 0) {
      sentMessage.setExpirationStartTimestamp(System.currentTimeMillis());
    }

    if (dataMessage.getIsViewOnce()) {
      dataMessage = dataMessage.toBuilder().clearAttachments().build();
      sentMessage.setMessage(dataMessage);
    }

    sentMessage.setIsRecipientUpdate(isRecipientUpdate);

    return container.setSyncMessage(syncMessage.setSent(sentMessage)).build().toByteArray();
  } catch (InvalidProtocolBufferException e) {
    throw new AssertionError(e);
  }
}
 
Example #18
Source File: Manager.java    From signal-cli with GNU General Public License v3.0 4 votes vote down vote up
private List<SendMessageResult> sendMessage(SignalServiceDataMessage.Builder messageBuilder, Collection<SignalServiceAddress> recipients)
        throws IOException {
    if (messagePipe == null) {
        messagePipe = getMessageReceiver().createMessagePipe();
    }
    if (unidentifiedMessagePipe == null) {
        unidentifiedMessagePipe = getMessageReceiver().createUnidentifiedMessagePipe();
    }
    SignalServiceDataMessage message = null;
    try {
        message = messageBuilder.build();
        if (message.getGroupContext().isPresent()) {
            try {
                SignalServiceMessageSender messageSender = getMessageSender();
                final boolean isRecipientUpdate = false;
                List<SendMessageResult> result = messageSender.sendMessage(new ArrayList<>(recipients), getAccessFor(recipients), isRecipientUpdate, message);
                for (SendMessageResult r : result) {
                    if (r.getIdentityFailure() != null) {
                        account.getSignalProtocolStore().saveIdentity(r.getAddress(), r.getIdentityFailure().getIdentityKey(), TrustLevel.UNTRUSTED);
                    }
                }
                return result;
            } catch (UntrustedIdentityException e) {
                account.getSignalProtocolStore().saveIdentity(resolveSignalServiceAddress(e.getIdentifier()), e.getIdentityKey(), TrustLevel.UNTRUSTED);
                return Collections.emptyList();
            }
        } else {
            // Send to all individually, so sync messages are sent correctly
            List<SendMessageResult> results = new ArrayList<>(recipients.size());
            for (SignalServiceAddress address : recipients) {
                ContactInfo contact = account.getContactStore().getContact(address);
                if (contact != null) {
                    messageBuilder.withExpiration(contact.messageExpirationTime);
                    messageBuilder.withProfileKey(account.getProfileKey().serialize());
                } else {
                    messageBuilder.withExpiration(0);
                    messageBuilder.withProfileKey(null);
                }
                message = messageBuilder.build();
                if (address.matches(account.getSelfAddress())) {
                    results.add(sendSelfMessage(message));
                } else {
                    results.add(sendMessage(address, message));
                }
            }
            return results;
        }
    } finally {
        if (message != null && message.isEndSession()) {
            for (SignalServiceAddress recipient : recipients) {
                handleEndSession(recipient);
            }
        }
        account.save();
    }
}
 
Example #19
Source File: SignalServiceMessageSender.java    From mollyim-android with GNU General Public License v3.0 4 votes vote down vote up
private byte[] createMultiDeviceSentTranscriptContent(byte[] content, Optional<SignalServiceAddress> recipient,
                                                      long timestamp, List<SendMessageResult> sendMessageResults,
                                                      boolean isRecipientUpdate)
{
  try {
    Content.Builder          container   = Content.newBuilder();
    SyncMessage.Builder      syncMessage = createSyncMessageBuilder();
    SyncMessage.Sent.Builder sentMessage = SyncMessage.Sent.newBuilder();
    DataMessage              dataMessage = Content.parseFrom(content).getDataMessage();

    sentMessage.setTimestamp(timestamp);
    sentMessage.setMessage(dataMessage);

    for (SendMessageResult result : sendMessageResults) {
      if (result.getSuccess() != null) {
        SyncMessage.Sent.UnidentifiedDeliveryStatus.Builder builder = SyncMessage.Sent.UnidentifiedDeliveryStatus.newBuilder();

        if (result.getAddress().getUuid().isPresent()) {
          builder = builder.setDestinationUuid(result.getAddress().getUuid().get().toString());
        }

        if (result.getAddress().getNumber().isPresent()) {
          builder = builder.setDestinationE164(result.getAddress().getNumber().get());
        }

        builder.setUnidentified(result.getSuccess().isUnidentified());

        sentMessage.addUnidentifiedStatus(builder.build());
      }
    }

    if (recipient.isPresent()) {
      if (recipient.get().getUuid().isPresent())   sentMessage.setDestinationUuid(recipient.get().getUuid().get().toString());
      if (recipient.get().getNumber().isPresent()) sentMessage.setDestinationE164(recipient.get().getNumber().get());
    }

    if (dataMessage.getExpireTimer() > 0) {
      sentMessage.setExpirationStartTimestamp(System.currentTimeMillis());
    }

    if (dataMessage.getIsViewOnce()) {
      dataMessage = dataMessage.toBuilder().clearAttachments().build();
      sentMessage.setMessage(dataMessage);
    }

    sentMessage.setIsRecipientUpdate(isRecipientUpdate);

    return container.setSyncMessage(syncMessage.setSent(sentMessage)).build().toByteArray();
  } catch (InvalidProtocolBufferException e) {
    throw new AssertionError(e);
  }
}