import com.fasterxml.jackson.databind.ObjectMapper;
-import org.asamk.Signal;
-import org.asamk.signal.AttachmentInvalidException;
-import org.asamk.signal.GroupNotFoundException;
-import org.asamk.signal.NotAGroupMemberException;
-import org.asamk.signal.StickerPackInvalidException;
-import org.asamk.signal.TrustLevel;
import org.asamk.signal.storage.SignalAccount;
import org.asamk.signal.storage.contacts.ContactInfo;
import org.asamk.signal.storage.groups.GroupInfo;
import org.whispersystems.signalservice.internal.util.Hex;
import org.whispersystems.util.Base64;
+import java.io.Closeable;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
-public class Manager implements Signal {
+public class Manager implements Closeable {
private final SleepTimer timer = new UptimeSleepTimer();
private final SignalServiceConfiguration serviceConfiguration;
Manager m = new Manager(account, pathConfig, serviceConfiguration, userAgent);
m.migrateLegacyConfigs();
- m.checkAccountState();
return m;
}
}
}
- private void checkAccountState() throws IOException {
+ public void checkAccountState() throws IOException {
if (account.isRegistered()) {
if (accountManager.getPreKeysCount() < ServiceConfig.PREKEY_MINIMUM_COUNT) {
refreshPreKeys();
return account.getGroupStore().getGroups();
}
- @Override
public long sendGroupMessage(String messageText, List<String> attachments,
byte[] groupId)
- throws IOException, EncapsulatedExceptions, GroupNotFoundException, AttachmentInvalidException {
+ throws IOException, EncapsulatedExceptions, GroupNotFoundException, AttachmentInvalidException, NotAGroupMemberException {
final SignalServiceDataMessage.Builder messageBuilder = SignalServiceDataMessage.newBuilder().withBody(messageText);
if (attachments != null) {
messageBuilder.withAttachments(Utils.getSignalServiceAttachments(attachments));
public void sendGroupMessageReaction(String emoji, boolean remove, String targetAuthor,
long targetSentTimestamp, byte[] groupId)
- throws IOException, EncapsulatedExceptions, AttachmentInvalidException, InvalidNumberException {
+ throws IOException, EncapsulatedExceptions, InvalidNumberException, NotAGroupMemberException, GroupNotFoundException {
SignalServiceDataMessage.Reaction reaction = new SignalServiceDataMessage.Reaction(emoji, remove, canonicalizeAndResolveSignalServiceAddress(targetAuthor), targetSentTimestamp);
final SignalServiceDataMessage.Builder messageBuilder = SignalServiceDataMessage.newBuilder()
.withReaction(reaction);
sendMessageLegacy(messageBuilder, g.getMembersWithout(account.getSelfAddress()));
}
- public void sendQuitGroupMessage(byte[] groupId) throws GroupNotFoundException, IOException, EncapsulatedExceptions {
+ public void sendQuitGroupMessage(byte[] groupId) throws GroupNotFoundException, IOException, EncapsulatedExceptions, NotAGroupMemberException {
SignalServiceGroup group = SignalServiceGroup.newBuilder(SignalServiceGroup.Type.QUIT)
.withId(groupId)
.build();
sendMessageLegacy(messageBuilder, g.getMembersWithout(account.getSelfAddress()));
}
- private byte[] sendUpdateGroupMessage(byte[] groupId, String name, Collection<SignalServiceAddress> members, String avatarFile) throws IOException, EncapsulatedExceptions, GroupNotFoundException, AttachmentInvalidException {
+ private byte[] sendUpdateGroupMessage(byte[] groupId, String name, Collection<SignalServiceAddress> members, String avatarFile) throws IOException, EncapsulatedExceptions, GroupNotFoundException, AttachmentInvalidException, NotAGroupMemberException {
GroupInfo g;
if (groupId == null) {
// Create new group
return g.groupId;
}
- private void sendUpdateGroupMessage(byte[] groupId, SignalServiceAddress recipient) throws IOException, EncapsulatedExceptions {
+ private void sendUpdateGroupMessage(byte[] groupId, SignalServiceAddress recipient) throws IOException, EncapsulatedExceptions, NotAGroupMemberException, GroupNotFoundException, AttachmentInvalidException {
if (groupId == null) {
return;
}
sendMessageLegacy(messageBuilder, Collections.singleton(recipient));
}
- private SignalServiceDataMessage.Builder getGroupUpdateMessageBuilder(GroupInfo g) {
+ private SignalServiceDataMessage.Builder getGroupUpdateMessageBuilder(GroupInfo g) throws AttachmentInvalidException {
SignalServiceGroup.Builder group = SignalServiceGroup.newBuilder(SignalServiceGroup.Type.UPDATE)
.withId(g.groupId)
.withName(g.name)
getMessageSender().sendReceipt(remoteAddress, getAccessFor(remoteAddress), receiptMessage);
}
- @Override
- public long sendMessage(String message, List<String> attachments, String recipient)
- throws EncapsulatedExceptions, AttachmentInvalidException, IOException, InvalidNumberException {
- List<String> recipients = new ArrayList<>(1);
- recipients.add(recipient);
- return sendMessage(message, attachments, recipients);
- }
-
- @Override
public long sendMessage(String messageText, List<String> attachments,
List<String> recipients)
throws IOException, EncapsulatedExceptions, AttachmentInvalidException, InvalidNumberException {
public void sendMessageReaction(String emoji, boolean remove, String targetAuthor,
long targetSentTimestamp, List<String> recipients)
- throws IOException, EncapsulatedExceptions, AttachmentInvalidException, InvalidNumberException {
+ throws IOException, EncapsulatedExceptions, InvalidNumberException {
SignalServiceDataMessage.Reaction reaction = new SignalServiceDataMessage.Reaction(emoji, remove, canonicalizeAndResolveSignalServiceAddress(targetAuthor), targetSentTimestamp);
final SignalServiceDataMessage.Builder messageBuilder = SignalServiceDataMessage.newBuilder()
.withReaction(reaction);
sendMessageLegacy(messageBuilder, getSignalServiceAddresses(recipients));
}
- @Override
public void sendEndSessionMessage(List<String> recipients) throws IOException, EncapsulatedExceptions, InvalidNumberException {
SignalServiceDataMessage.Builder messageBuilder = SignalServiceDataMessage.newBuilder()
.asEndSessionMessage();
}
}
- @Override
public String getContactName(String number) throws InvalidNumberException {
ContactInfo contact = account.getContactStore().getContact(canonicalizeAndResolveSignalServiceAddress(number));
if (contact == null) {
}
}
- @Override
public void setContactName(String number, String name) throws InvalidNumberException {
final SignalServiceAddress address = canonicalizeAndResolveSignalServiceAddress(number);
ContactInfo contact = account.getContactStore().getContact(address);
account.save();
}
- @Override
public void setContactBlocked(String number, boolean blocked) throws InvalidNumberException {
setContactBlocked(canonicalizeAndResolveSignalServiceAddress(number), blocked);
}
account.save();
}
- @Override
public void setGroupBlocked(final byte[] groupId, final boolean blocked) throws GroupNotFoundException {
GroupInfo group = getGroup(groupId);
if (group == null) {
}
}
- @Override
- public List<byte[]> getGroupIds() {
- List<GroupInfo> groups = getGroups();
- List<byte[]> ids = new ArrayList<>(groups.size());
- for (GroupInfo group : groups) {
- ids.add(group.groupId);
- }
- return ids;
- }
-
- @Override
- public String getGroupName(byte[] groupId) {
- GroupInfo group = getGroup(groupId);
- if (group == null) {
- return "";
- } else {
- return group.name;
- }
- }
-
- @Override
- public List<String> getGroupMembers(byte[] groupId) {
- GroupInfo group = getGroup(groupId);
- if (group == null) {
- return Collections.emptyList();
- } else {
- return new ArrayList<>(group.getMembersE164());
- }
- }
-
- @Override
- public byte[] updateGroup(byte[] groupId, String name, List<String> members, String avatar) throws IOException, EncapsulatedExceptions, GroupNotFoundException, AttachmentInvalidException, InvalidNumberException {
+ public byte[] updateGroup(byte[] groupId, String name, List<String> members, String avatar) throws IOException, EncapsulatedExceptions, GroupNotFoundException, AttachmentInvalidException, InvalidNumberException, NotAGroupMemberException {
if (groupId.length == 0) {
groupId = null;
}
/**
* Change the expiration timer for a contact
*/
- public void setExpirationTimer(SignalServiceAddress address, int messageExpirationTimer) {
- ContactInfo c = account.getContactStore().getContact(address);
- c.messageExpirationTime = messageExpirationTimer;
- account.getContactStore().updateContact(c);
+ public void setExpirationTimer(SignalServiceAddress address, int messageExpirationTimer) throws IOException {
+ final SignalServiceDataMessage.Builder messageBuilder = SignalServiceDataMessage.newBuilder();
+ ContactInfo contact = account.getContactStore().getContact(address);
+ contact.messageExpirationTime = messageExpirationTimer;
+ account.getContactStore().updateContact(contact);
+ account.save();
+ messageBuilder.withExpiration(messageExpirationTimer);
+ messageBuilder.asExpirationUpdate();
+ sendMessage(messageBuilder, Collections.singleton(address));
+ }
+
+ /**
+ * Change the expiration timer for a contact
+ */
+ public void setExpirationTimer(String number, int messageExpirationTimer) throws IOException, InvalidNumberException {
+ SignalServiceAddress address = canonicalizeAndResolveSignalServiceAddress(number);
+ setExpirationTimer(address, messageExpirationTimer);
}
/**
if (group != null) {
try {
sendUpdateGroupMessage(groupInfo.getGroupId(), source);
- } catch (IOException | EncapsulatedExceptions e) {
+ } catch (IOException | EncapsulatedExceptions | AttachmentInvalidException e) {
e.printStackTrace();
- } catch (NotAGroupMemberException e) {
+ } catch (GroupNotFoundException | NotAGroupMemberException e) {
// We have left this group, so don't send a group update message
}
}
retryFailedReceivedMessages(handler, ignoreAttachments);
final SignalServiceMessageReceiver messageReceiver = getMessageReceiver();
- try {
- if (messagePipe == null) {
- messagePipe = messageReceiver.createMessagePipe();
- }
+ if (messagePipe == null) {
+ messagePipe = messageReceiver.createMessagePipe();
+ }
- while (true) {
- SignalServiceEnvelope envelope;
- SignalServiceContent content = null;
- Exception exception = null;
- final long now = new Date().getTime();
- try {
- envelope = messagePipe.read(timeout, unit, envelope1 -> {
- // store message on disk, before acknowledging receipt to the server
- try {
- String source = envelope1.getSourceE164().isPresent() ? envelope1.getSourceE164().get() : "";
- File cacheFile = getMessageCacheFile(source, now, envelope1.getTimestamp());
- Utils.storeEnvelope(envelope1, cacheFile);
- } catch (IOException e) {
- System.err.println("Failed to store encrypted message in disk cache, ignoring: " + e.getMessage());
- }
- });
- } catch (TimeoutException e) {
- if (returnOnTimeout)
- return;
- continue;
- } catch (InvalidVersionException e) {
- System.err.println("Ignoring error: " + e.getMessage());
- continue;
- }
- if (!envelope.isReceipt()) {
- try {
- content = decryptMessage(envelope);
- } catch (Exception e) {
- exception = e;
- }
- handleMessage(envelope, content, ignoreAttachments);
- }
- account.save();
- if (!isMessageBlocked(envelope, content)) {
- handler.handleMessage(envelope, content, exception);
- }
- if (!(exception instanceof org.whispersystems.libsignal.UntrustedIdentityException)) {
- File cacheFile = null;
+ while (true) {
+ SignalServiceEnvelope envelope;
+ SignalServiceContent content = null;
+ Exception exception = null;
+ final long now = new Date().getTime();
+ try {
+ envelope = messagePipe.read(timeout, unit, envelope1 -> {
+ // store message on disk, before acknowledging receipt to the server
try {
- cacheFile = getMessageCacheFile(envelope.getSourceE164().get(), now, envelope.getTimestamp());
- Files.delete(cacheFile.toPath());
- // Try to delete directory if empty
- new File(getMessageCachePath()).delete();
+ String source = envelope1.getSourceE164().isPresent() ? envelope1.getSourceE164().get() : "";
+ File cacheFile = getMessageCacheFile(source, now, envelope1.getTimestamp());
+ Utils.storeEnvelope(envelope1, cacheFile);
} catch (IOException e) {
- System.err.println("Failed to delete cached message file “" + cacheFile + "”: " + e.getMessage());
+ System.err.println("Failed to store encrypted message in disk cache, ignoring: " + e.getMessage());
}
+ });
+ } catch (TimeoutException e) {
+ if (returnOnTimeout)
+ return;
+ continue;
+ } catch (InvalidVersionException e) {
+ System.err.println("Ignoring error: " + e.getMessage());
+ continue;
+ }
+ if (envelope.hasSource()) {
+ // Store uuid if we don't have it already
+ SignalServiceAddress source = envelope.getSourceAddress();
+ resolveSignalServiceAddress(source);
+ }
+ if (!envelope.isReceipt()) {
+ try {
+ content = decryptMessage(envelope);
+ } catch (Exception e) {
+ exception = e;
}
+ handleMessage(envelope, content, ignoreAttachments);
}
- } finally {
- if (messagePipe != null) {
- messagePipe.shutdown();
- messagePipe = null;
+ account.save();
+ if (!isMessageBlocked(envelope, content)) {
+ handler.handleMessage(envelope, content, exception);
+ }
+ if (!(exception instanceof org.whispersystems.libsignal.UntrustedIdentityException)) {
+ File cacheFile = null;
+ try {
+ cacheFile = getMessageCacheFile(envelope.getSourceE164().get(), now, envelope.getTimestamp());
+ Files.delete(cacheFile.toPath());
+ // Try to delete directory if empty
+ new File(getMessageCachePath()).delete();
+ } catch (IOException e) {
+ System.err.println("Failed to delete cached message file “" + cacheFile + "”: " + e.getMessage());
+ }
}
}
}
} else {
sender = content.getSender();
}
+ // Store uuid if we don't have it already
+ resolveSignalServiceAddress(sender);
+
if (content.getDataMessage().isPresent()) {
SignalServiceDataMessage message = content.getDataMessage().get();
return messageReceiver.retrieveAttachment(pointer, tmpFile, ServiceConfig.MAX_ATTACHMENT_SIZE);
}
- @Override
- public boolean isRemote() {
- return false;
- }
-
- @Override
- public String getObjectPath() {
- return null;
- }
-
private void sendGroups() throws IOException, UntrustedIdentityException {
File groupsFile = IOUtils.createTempFile();
return account.getRecipientStore().resolveServiceAddress(address);
}
+ @Override
+ public void close() throws IOException {
+ if (messagePipe != null) {
+ messagePipe.shutdown();
+ messagePipe = null;
+ }
+
+ if (unidentifiedMessagePipe != null) {
+ unidentifiedMessagePipe.shutdown();
+ unidentifiedMessagePipe = null;
+ }
+
+ account.close();
+ }
+
public interface ReceiveMessageHandler {
void handleMessage(SignalServiceEnvelope envelope, SignalServiceContent decryptedContent, Throwable e);