import org.signal.libsignal.metadata.SelfSendException;
import org.signal.libsignal.metadata.certificate.InvalidCertificateException;
import org.signal.zkgroup.InvalidInputException;
-import org.signal.zkgroup.VerificationFailedException;
import org.signal.zkgroup.profiles.ClientZkProfileOperations;
import org.signal.zkgroup.profiles.ProfileKey;
import org.whispersystems.libsignal.IdentityKey;
import java.util.Objects;
import java.util.Set;
import java.util.UUID;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.stream.Collectors;
private SignalServiceAccountManager accountManager;
private SignalServiceMessagePipe messagePipe = null;
private SignalServiceMessagePipe unidentifiedMessagePipe = null;
+ private boolean discoverableByPhoneNumber = true;
public Manager(SignalAccount account, PathConfig pathConfig, SignalServiceConfiguration serviceConfiguration, String userAgent) {
this.account = account;
}
public void updateAccountAttributes() throws IOException {
- accountManager.setAccountAttributes(account.getSignalingKey(), account.getSignalProtocolStore().getLocalRegistrationId(), true, account.getRegistrationLockPin(), account.getRegistrationLock(), getSelfUnidentifiedAccessKey(), false, ServiceConfig.capabilities);
+ accountManager.setAccountAttributes(account.getSignalingKey(), account.getSignalProtocolStore().getLocalRegistrationId(), true, account.getRegistrationLockPin(), account.getRegistrationLock(), getSelfUnidentifiedAccessKey(), false, ServiceConfig.capabilities, discoverableByPhoneNumber);
}
- public void setProfileName(String name) throws IOException {
- accountManager.setProfileName(account.getProfileKey(), name);
- }
-
- public void setProfileAvatar(File avatar) throws IOException {
- final StreamDetails streamDetails = Utils.createStreamDetailsFromFile(avatar);
- accountManager.setProfileAvatar(account.getProfileKey(), streamDetails);
- streamDetails.getStream().close();
- }
-
- public void removeProfileAvatar() throws IOException {
- accountManager.setProfileAvatar(account.getProfileKey(), null);
+ public void setProfile(String name, File avatar) throws IOException {
+ try (final StreamDetails streamDetails = avatar == null ? null : Utils.createStreamDetailsFromFile(avatar)) {
+ accountManager.setVersionedProfile(account.getUuid(), account.getProfileKey(), name, streamDetails);
+ }
}
public void unregister() throws IOException {
verificationCode = verificationCode.replace("-", "");
account.setSignalingKey(KeyUtils.createSignalingKey());
// TODO make unrestricted unidentified access configurable
- VerifyAccountResponse response = accountManager.verifyAccountWithCode(verificationCode, account.getSignalingKey(), account.getSignalProtocolStore().getLocalRegistrationId(), true, pin, null, getSelfUnidentifiedAccessKey(), false, ServiceConfig.capabilities);
+ VerifyAccountResponse response = accountManager.verifyAccountWithCode(verificationCode, account.getSignalingKey(), account.getSignalProtocolStore().getLocalRegistrationId(), true, pin, null, getSelfUnidentifiedAccessKey(), false, ServiceConfig.capabilities, discoverableByPhoneNumber);
UUID uuid = UuidUtil.parseOrNull(response.getUuid());
// TODO response.isStorageCapable()
// TODO implement ZkGroup support
final ClientZkProfileOperations clientZkProfileOperations = null;
final boolean attachmentsV3 = false;
+ final ExecutorService executor = null;
return new SignalServiceMessageSender(serviceConfiguration, account.getUuid(), account.getUsername(), account.getPassword(),
- account.getDeviceId(), account.getSignalProtocolStore(), userAgent, account.isMultiDevice(), attachmentsV3, Optional.fromNullable(messagePipe), Optional.fromNullable(unidentifiedMessagePipe), Optional.absent(), clientZkProfileOperations);
+ account.getDeviceId(), account.getSignalProtocolStore(), userAgent, account.isMultiDevice(), attachmentsV3, Optional.fromNullable(messagePipe), Optional.fromNullable(unidentifiedMessagePipe), Optional.absent(), clientZkProfileOperations, executor);
}
- private SignalServiceProfile getRecipientProfile(SignalServiceAddress address, Optional<UnidentifiedAccess> unidentifiedAccess) throws IOException {
+ private SignalServiceProfile getEncryptedRecipientProfile(SignalServiceAddress address, Optional<UnidentifiedAccess> unidentifiedAccess) throws IOException {
SignalServiceMessagePipe pipe = unidentifiedMessagePipe != null && unidentifiedAccess.isPresent() ? unidentifiedMessagePipe
: messagePipe;
if (pipe != null) {
try {
- return pipe.getProfile(address, Optional.absent(), unidentifiedAccess, SignalServiceProfile.RequestType.PROFILE).getProfile();
- } catch (IOException ignored) {
+ return pipe.getProfile(address, Optional.absent(), unidentifiedAccess, SignalServiceProfile.RequestType.PROFILE).get(10, TimeUnit.SECONDS).getProfile();
+ } catch (IOException | InterruptedException | ExecutionException | TimeoutException ignored) {
}
}
SignalServiceMessageReceiver receiver = getMessageReceiver();
try {
- return receiver.retrieveProfile(address, Optional.absent(), unidentifiedAccess, SignalServiceProfile.RequestType.PROFILE).getProfile();
- } catch (VerificationFailedException e) {
- throw new AssertionError(e);
+ return receiver.retrieveProfile(address, Optional.absent(), unidentifiedAccess, SignalServiceProfile.RequestType.PROFILE).get(10, TimeUnit.SECONDS).getProfile();
+ } catch (InterruptedException | ExecutionException | TimeoutException e) {
+ throw new IOException("Failed to retrieve profile", e);
}
}
+ private SignalProfile getRecipientProfile(SignalServiceAddress address, Optional<UnidentifiedAccess> unidentifiedAccess, ProfileKey profileKey) throws IOException {
+ return decryptProfile(getEncryptedRecipientProfile(address, unidentifiedAccess), profileKey);
+ }
+
private Optional<SignalServiceAttachmentStream> createGroupAvatarAttachment(byte[] groupId) throws IOException {
File file = getGroupAvatarFile(groupId);
if (!file.exists()) {
for (ContactTokenDetails contact : contacts) {
newE164Members.remove(contact.getNumber());
}
- System.err.println("Failed to add members " + Util.join(", ", newE164Members) + " to group: Not registered on Signal");
- System.err.println("Aborting…");
- System.exit(1);
+ throw new IOException("Failed to add members " + Util.join(", ", newE164Members) + " to group: Not registered on Signal");
}
g.addMembers(members);
ContactInfo contact = account.getContactStore().getContact(address);
if (contact == null) {
contact = new ContactInfo(address);
- System.err.println("Add contact " + contact.number + " named " + name);
- } else {
- System.err.println("Updating contact " + contact.number + " name " + contact.name + " -> " + name);
}
contact.name = name;
account.getContactStore().updateContact(contact);
ContactInfo contact = account.getContactStore().getContact(address);
if (contact == null) {
contact = new ContactInfo(address);
- System.err.println("Adding and " + (blocked ? "blocking" : "unblocking") + " contact " + address.getNumber().orNull());
- } else {
- System.err.println((blocked ? "Blocking" : "Unblocking") + " contact " + address.getNumber().orNull());
}
contact.blocked = blocked;
account.getContactStore().updateContact(contact);
GroupInfo group = getGroup(groupId);
if (group == null) {
throw new GroupNotFoundException(groupId);
- } else {
- System.err.println((blocked ? "Blocking" : "Unblocking") + " group " + Base64.encodeBytes(groupId));
- group.blocked = blocked;
- account.getGroupStore().updateGroup(group);
- account.save();
}
+
+ group.blocked = blocked;
+ account.getGroupStore().updateGroup(group);
+ account.save();
}
public byte[] updateGroup(byte[] groupId, String name, List<String> members, String avatar) throws IOException, EncapsulatedExceptions, GroupNotFoundException, AttachmentInvalidException, InvalidNumberException, NotAGroupMemberException {
throw new StickerPackInvalidException("Could not find find " + sticker.file);
}
- StickerInfo stickerInfo = new StickerInfo(data.first(), data.second(), Optional.fromNullable(sticker.emoji).or(""));
+ String contentType = Utils.getFileMimeType(new File(sticker.file), null);
+ StickerInfo stickerInfo = new StickerInfo(data.first(), data.second(), Optional.fromNullable(sticker.emoji).or(""), contentType);
stickers.add(stickerInfo);
}
throw new StickerPackInvalidException("Could not find find " + pack.cover.file);
}
- cover = new StickerInfo(data.first(), data.second(), Optional.fromNullable(pack.cover.emoji).or(""));
+ String contentType = Utils.getFileMimeType(new File(pack.cover.file), null);
+ cover = new StickerInfo(data.first(), data.second(), Optional.fromNullable(pack.cover.emoji).or(""), contentType);
}
return new SignalServiceStickerManifestUpload(
private byte[] getSenderCertificate() {
// TODO support UUID capable sender certificates
- // byte[] certificate = accountManager.getSenderCertificate();
+ // byte[] certificate = accountManager.getSenderCertificateForPhoneNumberPrivacy();
byte[] certificate;
try {
- certificate = accountManager.getSenderCertificateLegacy();
+ certificate = accountManager.getSenderCertificate();
} catch (IOException e) {
System.err.println("Failed to get sender certificate: " + e);
return null;
}
SignalProfile targetProfile;
try {
- targetProfile = decryptProfile(getRecipientProfile(recipient, Optional.absent()), theirProfileKey);
+ targetProfile = getRecipientProfile(recipient, Optional.absent(), theirProfileKey);
} catch (IOException e) {
System.err.println("Failed to get recipient profile: " + e);
return null;