import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
+import static org.asamk.signal.manager.ServiceConfig.capabilities;
+
public class Manager implements Closeable {
private final SleepTimer timer = new UptimeSleepTimer();
}
public void updateAccountAttributes() throws IOException {
- accountManager.setAccountAttributes(account.getSignalingKey(), account.getSignalProtocolStore().getLocalRegistrationId(), true, account.getRegistrationLockPin(), account.getRegistrationLock(), getSelfUnidentifiedAccessKey(), false, ServiceConfig.capabilities, discoverableByPhoneNumber);
+ accountManager.setAccountAttributes(account.getSignalingKey(), account.getSignalProtocolStore().getLocalRegistrationId(), true, account.getRegistrationLockPin(), account.getRegistrationLock(), getSelfUnidentifiedAccessKey(), false, capabilities, discoverableByPhoneNumber);
}
public void setProfile(String name, File avatar) 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, discoverableByPhoneNumber);
+ VerifyAccountResponse response = accountManager.verifyAccountWithCode(verificationCode, account.getSignalingKey(), account.getSignalProtocolStore().getLocalRegistrationId(), true, pin, null, getSelfUnidentifiedAccessKey(), false, capabilities, discoverableByPhoneNumber);
UUID uuid = UuidUtil.parseOrNull(response.getUuid());
// TODO response.isStorageCapable()
}
private SignalProfile getRecipientProfile(SignalServiceAddress address, Optional<UnidentifiedAccess> unidentifiedAccess, ProfileKey profileKey) throws IOException {
- return decryptProfile(getEncryptedRecipientProfile(address, unidentifiedAccess), profileKey);
+ final SignalServiceProfile encryptedProfile = getEncryptedRecipientProfile(address, unidentifiedAccess);
+
+ File avatarFile = null;
+ try {
+ avatarFile = encryptedProfile.getAvatar() == null ? null : retrieveProfileAvatar(address, encryptedProfile.getAvatar(), profileKey);
+ } catch (AssertionError e) {
+ System.err.println("Failed to retrieve profile avatar: " + e.getMessage());
+ }
+
+ ProfileCipher profileCipher = new ProfileCipher(profileKey);
+ try {
+ return new SignalProfile(
+ encryptedProfile.getIdentityKey(),
+ encryptedProfile.getName() == null ? null : new String(profileCipher.decryptName(Base64.decode(encryptedProfile.getName()))),
+ avatarFile,
+ encryptedProfile.getUnidentifiedAccess() == null || !profileCipher.verifyUnidentifiedAccess(Base64.decode(encryptedProfile.getUnidentifiedAccess())) ? null : encryptedProfile.getUnidentifiedAccess(),
+ encryptedProfile.isUnrestrictedUnidentifiedAccess(),
+ encryptedProfile.getCapabilities());
+ } catch (InvalidCiphertextException e) {
+ return null;
+ }
}
private Optional<SignalServiceAttachmentStream> createGroupAvatarAttachment(byte[] groupId) throws IOException {
return UnidentifiedAccess.deriveAccessKeyFrom(account.getProfileKey());
}
- private static SignalProfile decryptProfile(SignalServiceProfile encryptedProfile, ProfileKey profileKey) throws IOException {
- ProfileCipher profileCipher = new ProfileCipher(profileKey);
- try {
- return new SignalProfile(
- encryptedProfile.getIdentityKey(),
- encryptedProfile.getName() == null ? null : new String(profileCipher.decryptName(Base64.decode(encryptedProfile.getName()))),
- encryptedProfile.getAvatar(),
- encryptedProfile.getUnidentifiedAccess() == null || !profileCipher.verifyUnidentifiedAccess(Base64.decode(encryptedProfile.getUnidentifiedAccess())) ? null : encryptedProfile.getUnidentifiedAccess(),
- encryptedProfile.isUnrestrictedUnidentifiedAccess()
- );
- } catch (InvalidCiphertextException e) {
- return null;
- }
- }
-
private byte[] getTargetUnidentifiedAccessKey(SignalServiceAddress recipient) {
ContactInfo contact = account.getContactStore().getContact(recipient);
if (contact == null || contact.profileKey == null) {
}
}
+ private File getProfileAvatarFile(SignalServiceAddress address) {
+ return new File(pathConfig.getAvatarsPath(), "profile-" + address.getLegacyIdentifier());
+ }
+
+ private File retrieveProfileAvatar(SignalServiceAddress address, String avatarPath, ProfileKey profileKey) throws IOException {
+ IOUtils.createPrivateDirectories(pathConfig.getAvatarsPath());
+ SignalServiceMessageReceiver receiver = getMessageReceiver();
+ File outputFile = getProfileAvatarFile(address);
+
+ File tmpFile = IOUtils.createTempFile();
+ try (InputStream input = receiver.retrieveProfileAvatar(avatarPath, tmpFile, profileKey, ServiceConfig.AVATAR_DOWNLOAD_FAILSAFE_MAX_SIZE)) {
+ // Use larger buffer size to prevent AssertionError: Need: 12272 but only have: 8192 ...
+ IOUtils.copyStreamToFile(input, outputFile, (int) ServiceConfig.AVATAR_DOWNLOAD_FAILSAFE_MAX_SIZE);
+ } finally {
+ try {
+ Files.delete(tmpFile.toPath());
+ } catch (IOException e) {
+ System.err.println("Failed to delete received avatar temp file “" + tmpFile + "”: " + e.getMessage());
+ }
+ }
+ return outputFile;
+ }
+
public File getAttachmentFile(SignalServiceAttachmentRemoteId attachmentId) {
return new File(pathConfig.getAttachmentsPath(), attachmentId.toString());
}
File tmpFile = IOUtils.createTempFile();
try (InputStream input = messageReceiver.retrieveAttachment(pointer, tmpFile, ServiceConfig.MAX_ATTACHMENT_SIZE)) {
- try (OutputStream output = new FileOutputStream(outputFile)) {
- byte[] buffer = new byte[4096];
- int read;
-
- while ((read = input.read(buffer)) != -1) {
- output.write(buffer, 0, read);
- }
- } catch (FileNotFoundException e) {
- e.printStackTrace();
- return null;
- }
+ IOUtils.copyStreamToFile(input, outputFile);
} finally {
try {
Files.delete(tmpFile.toPath());
package org.asamk.signal.manager;
+import org.whispersystems.signalservice.api.profiles.SignalServiceProfile;
+
+import java.io.File;
+
public class SignalProfile {
private final String identityKey;
private final String name;
- private final String avatar;
+ private final File avatarFile;
private final String unidentifiedAccess;
private final boolean unrestrictedUnidentifiedAccess;
- public SignalProfile(final String identityKey, final String name, final String avatar, final String unidentifiedAccess, final boolean unrestrictedUnidentifiedAccess) {
+ private final SignalServiceProfile.Capabilities capabilities;
+
+ public SignalProfile(final String identityKey, final String name, final File avatarFile, final String unidentifiedAccess, final boolean unrestrictedUnidentifiedAccess, final SignalServiceProfile.Capabilities capabilities) {
this.identityKey = identityKey;
this.name = name;
- this.avatar = avatar;
+ this.avatarFile = avatarFile;
this.unidentifiedAccess = unidentifiedAccess;
this.unrestrictedUnidentifiedAccess = unrestrictedUnidentifiedAccess;
+ this.capabilities = capabilities;
}
public String getIdentityKey() {
return name;
}
- public String getAvatar() {
- return avatar;
+ public File getAvatarFile() {
+ return avatarFile;
}
public String getUnidentifiedAccess() {
return unrestrictedUnidentifiedAccess;
}
+ public SignalServiceProfile.Capabilities getCapabilities() {
+ return capabilities;
+ }
+
@Override
public String toString() {
return "SignalProfile{" +
"identityKey='" + identityKey + '\'' +
", name='" + name + '\'' +
- ", avatar='" + avatar + '\'' +
+ ", avatarFile=" + avatarFile +
", unidentifiedAccess='" + unidentifiedAccess + '\'' +
", unrestrictedUnidentifiedAccess=" + unrestrictedUnidentifiedAccess +
+ ", capabilities=" + capabilities +
'}';
}
}