updateProfile can now only update both name and avatar at the same time,
as the upstream API has changed.
- implementation 'com.github.turasa:signal-service-java:2.15.3_unofficial_11'
+ implementation 'com.github.turasa:signal-service-java:2.15.3_unofficial_12'
implementation 'org.bouncycastle:bcprov-jdk15on:1.66'
implementation 'net.sourceforge.argparse4j:argparse4j:0.8.1'
implementation 'org.bouncycastle:bcprov-jdk15on:1.66'
implementation 'net.sourceforge.argparse4j:argparse4j:0.8.1'
- implementation 'com.github.hypfvieh:dbus-java:3.2.2'
+ implementation 'com.github.hypfvieh:dbus-java:3.2.3'
implementation 'org.slf4j:slf4j-nop:1.7.30'
}
implementation 'org.slf4j:slf4j-nop:1.7.30'
}
-Update the name and/or avatar image visible by message recipients for the current users.
+Update the name and avatar image visible by message recipients for the current users.
The profile is stored encrypted on the Signal servers.
The decryption key is sent with every outgoing messages (excluding group messages).
The profile is stored encrypted on the Signal servers.
The decryption key is sent with every outgoing messages (excluding group messages).
SignalServiceCallMessage callMessage = content.getCallMessage().get();
if (callMessage.getAnswerMessage().isPresent()) {
AnswerMessage answerMessage = callMessage.getAnswerMessage().get();
SignalServiceCallMessage callMessage = content.getCallMessage().get();
if (callMessage.getAnswerMessage().isPresent()) {
AnswerMessage answerMessage = callMessage.getAnswerMessage().get();
- System.out.println("Answer message: " + answerMessage.getId() + ": " + answerMessage.getDescription());
+ System.out.println("Answer message: " + answerMessage.getId() + ": " + answerMessage.getSdp());
}
if (callMessage.getBusyMessage().isPresent()) {
BusyMessage busyMessage = callMessage.getBusyMessage().get();
}
if (callMessage.getBusyMessage().isPresent()) {
BusyMessage busyMessage = callMessage.getBusyMessage().get();
}
if (callMessage.getOfferMessage().isPresent()) {
OfferMessage offerMessage = callMessage.getOfferMessage().get();
}
if (callMessage.getOfferMessage().isPresent()) {
OfferMessage offerMessage = callMessage.getOfferMessage().get();
- System.out.println("Offer message: " + offerMessage.getId() + ": " + offerMessage.getDescription());
+ System.out.println("Offer message: " + offerMessage.getId() + ": " + offerMessage.getSdp());
}
}
if (content.getReceiptMessage().isPresent()) {
}
}
if (content.getReceiptMessage().isPresent()) {
@Override
public void attachToSubparser(final Subparser subparser) {
@Override
public void attachToSubparser(final Subparser subparser) {
- final MutuallyExclusiveGroup avatarOptions = subparser.addMutuallyExclusiveGroup();
+ final MutuallyExclusiveGroup avatarOptions = subparser.addMutuallyExclusiveGroup()
+ .required(true);
avatarOptions.addArgument("--avatar")
.help("Path to new profile avatar");
avatarOptions.addArgument("--remove-avatar")
.action(Arguments.storeTrue());
subparser.addArgument("--name")
avatarOptions.addArgument("--avatar")
.help("Path to new profile avatar");
avatarOptions.addArgument("--remove-avatar")
.action(Arguments.storeTrue());
subparser.addArgument("--name")
.help("New profile name");
.help("New profile name");
- subparser.help("Set a name and/or avatar image for the user profile");
+ subparser.help("Set a name and avatar image for the user profile");
}
String name = ns.getString("name");
}
String name = ns.getString("name");
-
- if (name != null) {
- try {
- m.setProfileName(name);
- } catch (IOException e) {
- System.err.println("UpdateAccount error: " + e.getMessage());
- return 3;
- }
- }
-
String avatarPath = ns.getString("avatar");
String avatarPath = ns.getString("avatar");
-
- if (avatarPath != null) {
- File avatarFile = new File(avatarPath);
-
- try {
- m.setProfileAvatar(avatarFile);
- } catch (IOException e) {
- System.err.println("UpdateAccount error: " + e.getMessage());
- return 3;
- }
- }
-
boolean removeAvatar = ns.getBoolean("remove_avatar");
boolean removeAvatar = ns.getBoolean("remove_avatar");
- if (removeAvatar) {
- try {
- m.removeProfileAvatar();
- } catch (IOException e) {
- System.err.println("UpdateAccount error: " + e.getMessage());
- return 3;
- }
+ try {
+ File avatarFile = removeAvatar ? null : new File(avatarPath);
+ m.setProfile(name, avatarFile);
+ } catch (IOException e) {
+ System.err.println("UpdateAccount error: " + e.getMessage());
+ return 3;
import org.signal.libsignal.metadata.SelfSendException;
import org.signal.libsignal.metadata.certificate.InvalidCertificateException;
import org.signal.zkgroup.InvalidInputException;
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 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.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;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.stream.Collectors;
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);
}
- 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 {
}
public void unregister() throws IOException {
// TODO implement ZkGroup support
final ClientZkProfileOperations clientZkProfileOperations = null;
final boolean attachmentsV3 = false;
// 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(),
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 getEncryptedRecipientProfile(SignalServiceAddress address, Optional<UnidentifiedAccess> unidentifiedAccess) throws IOException {
}
private SignalServiceProfile getEncryptedRecipientProfile(SignalServiceAddress address, Optional<UnidentifiedAccess> unidentifiedAccess) throws IOException {
if (pipe != null) {
try {
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 {
}
}
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);
InputStream attachmentStream = new FileInputStream(attachmentFile);
final long attachmentSize = attachmentFile.length();
final String mime = getFileMimeType(attachmentFile);
InputStream attachmentStream = new FileInputStream(attachmentFile);
final long attachmentSize = attachmentFile.length();
final String mime = getFileMimeType(attachmentFile);
- // TODO mabybe add a parameter to set the voiceNote, preview, width, height and caption option
+ // TODO mabybe add a parameter to set the voiceNote, borderless, preview, width, height and caption option
final long uploadTimestamp = System.currentTimeMillis();
Optional<byte[]> preview = Optional.absent();
Optional<String> caption = Optional.absent();
Optional<String> blurHash = Optional.absent();
final Optional<ResumableUploadSpec> resumableUploadSpec = Optional.absent();
final long uploadTimestamp = System.currentTimeMillis();
Optional<byte[]> preview = Optional.absent();
Optional<String> caption = Optional.absent();
Optional<String> blurHash = Optional.absent();
final Optional<ResumableUploadSpec> resumableUploadSpec = Optional.absent();
- return new SignalServiceAttachmentStream(attachmentStream, mime, attachmentSize, Optional.of(attachmentFile.getName()), false, preview, 0, 0, uploadTimestamp, caption, blurHash, null, null, resumableUploadSpec);
+ return new SignalServiceAttachmentStream(attachmentStream, mime, attachmentSize, Optional.of(attachmentFile.getName()), false, false, preview, 0, 0, uploadTimestamp, caption, blurHash, null, null, resumableUploadSpec);
}
static StreamDetails createStreamDetailsFromFile(File file) throws IOException {
}
static StreamDetails createStreamDetailsFromFile(File file) throws IOException {
try (FileInputStream f = new FileInputStream(file)) {
DataInputStream in = new DataInputStream(f);
int version = in.readInt();
try (FileInputStream f = new FileInputStream(file)) {
DataInputStream in = new DataInputStream(f);
int version = in.readInt();
return null;
}
int type = in.readInt();
return null;
}
int type = in.readInt();
legacyMessage = new byte[legacyMessageLen];
in.readFully(legacyMessage);
}
legacyMessage = new byte[legacyMessageLen];
in.readFully(legacyMessage);
}
- long serverTimestamp = 0;
+ long serverReceivedTimestamp = 0;
- if (version == 2) {
- serverTimestamp = in.readLong();
+ if (version >= 2) {
+ serverReceivedTimestamp = in.readLong();
uuid = in.readUTF();
if ("".equals(uuid)) {
uuid = null;
}
}
uuid = in.readUTF();
if ("".equals(uuid)) {
uuid = null;
}
}
+ long serverDeliveredTimestamp = 0;
+ if (version >= 4) {
+ serverDeliveredTimestamp = in.readLong();
+ }
Optional<SignalServiceAddress> addressOptional = sourceUuid == null && source.isEmpty()
? Optional.absent()
: Optional.of(new SignalServiceAddress(sourceUuid, source));
Optional<SignalServiceAddress> addressOptional = sourceUuid == null && source.isEmpty()
? Optional.absent()
: Optional.of(new SignalServiceAddress(sourceUuid, source));
- return new SignalServiceEnvelope(type, addressOptional, sourceDevice, timestamp, legacyMessage, content, serverTimestamp, uuid);
+ return new SignalServiceEnvelope(type, addressOptional, sourceDevice, timestamp, legacyMessage, content, serverReceivedTimestamp, serverDeliveredTimestamp, uuid);
}
}
static void storeEnvelope(SignalServiceEnvelope envelope, File file) throws IOException {
try (FileOutputStream f = new FileOutputStream(file)) {
try (DataOutputStream out = new DataOutputStream(f)) {
}
}
static void storeEnvelope(SignalServiceEnvelope envelope, File file) throws IOException {
try (FileOutputStream f = new FileOutputStream(file)) {
try (DataOutputStream out = new DataOutputStream(f)) {
- out.writeInt(3); // version
+ out.writeInt(4); // version
out.writeInt(envelope.getType());
out.writeUTF(envelope.getSourceE164().isPresent() ? envelope.getSourceE164().get() : "");
out.writeUTF(envelope.getSourceUuid().isPresent() ? envelope.getSourceUuid().get() : "");
out.writeInt(envelope.getType());
out.writeUTF(envelope.getSourceE164().isPresent() ? envelope.getSourceE164().get() : "");
out.writeUTF(envelope.getSourceUuid().isPresent() ? envelope.getSourceUuid().get() : "");
} else {
out.writeInt(0);
}
} else {
out.writeInt(0);
}
- out.writeLong(envelope.getServerTimestamp());
+ out.writeLong(envelope.getServerReceivedTimestamp());
String uuid = envelope.getUuid();
out.writeUTF(uuid == null ? "" : uuid);
String uuid = envelope.getUuid();
out.writeUTF(uuid == null ? "" : uuid);
+ out.writeLong(envelope.getServerDeliveredTimestamp());