public void storeAttachment(
final SignalServiceAttachmentRemoteId attachmentId, final AttachmentStorer storer
) throws IOException {
- storeAttachment(getAttachmentFile(attachmentId.toString()), storer);
+ storeAttachment(getAttachmentFile(attachmentId), storer);
}
private void storeAttachment(final File attachmentFile, final AttachmentStorer storer) throws IOException {
return new File(attachmentsPath, attachmentId.toString() + ".preview");
}
- public File getAttachmentFile(String attachmentId) {
- return new File(attachmentsPath, attachmentId);
+ public File getAttachmentFile(SignalServiceAttachmentRemoteId attachmentId) {
+ return new File(attachmentsPath, attachmentId.toString());
}
private void createAttachmentsDir() throws IOException {
boolean isContactBlocked(RecipientIdentifier.Single recipient);
- File getAttachmentFile(String attachmentId);
-
void sendContacts() throws IOException;
List<Pair<RecipientAddress, Contact>> getContacts();
return contactHelper.isContactBlocked(recipientId);
}
- @Override
- public File getAttachmentFile(String attachmentId) {
- return attachmentHelper.getAttachmentFile(attachmentId);
- }
-
@Override
public void sendContacts() throws IOException {
syncHelper.sendContacts();
import org.asamk.signal.manager.storage.recipients.RecipientAddress;
import org.asamk.signal.manager.storage.recipients.RecipientResolver;
import org.whispersystems.signalservice.api.messages.SignalServiceAttachment;
+import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentRemoteId;
import org.whispersystems.signalservice.api.messages.SignalServiceContent;
import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage;
import org.whispersystems.signalservice.api.messages.SignalServiceEnvelope;
import org.whispersystems.signalservice.api.messages.multidevice.ViewOnceOpenMessage;
import org.whispersystems.signalservice.api.messages.multidevice.ViewedMessage;
+import java.io.File;
import java.util.List;
import java.util.Optional;
import java.util.Set;
static Data from(
final SignalServiceDataMessage dataMessage,
RecipientResolver recipientResolver,
- RecipientAddressResolver addressResolver
+ RecipientAddressResolver addressResolver,
+ final AttachmentFileProvider fileProvider
) {
return new Data(dataMessage.getTimestamp(),
Optional.ofNullable(dataMessage.getGroupContext().transform(GroupContext::from).orNull()),
.transform(r -> Reaction.from(r, recipientResolver, addressResolver))
.orNull()),
Optional.ofNullable(dataMessage.getQuote()
- .transform(q -> Quote.from(q, recipientResolver, addressResolver))
+ .transform(q -> Quote.from(q, recipientResolver, addressResolver, fileProvider))
.orNull()),
dataMessage.getAttachments()
- .transform(a -> a.stream().map(Attachment::from).collect(Collectors.toList()))
+ .transform(a -> a.stream()
+ .map(as -> Attachment.from(as, fileProvider))
+ .collect(Collectors.toList()))
.or(List.of()),
Optional.ofNullable(dataMessage.getRemoteDelete()
.transform(SignalServiceDataMessage.RemoteDelete::getTargetSentTimestamp)
.orNull()),
Optional.ofNullable(dataMessage.getSticker().transform(Sticker::from).orNull()),
dataMessage.getSharedContacts()
- .transform(a -> a.stream().map(SharedContact::from).collect(Collectors.toList()))
+ .transform(a -> a.stream()
+ .map(sharedContact -> SharedContact.from(sharedContact, fileProvider))
+ .collect(Collectors.toList()))
.or(List.of()),
dataMessage.getMentions()
.transform(a -> a.stream()
.collect(Collectors.toList()))
.or(List.of()),
dataMessage.getPreviews()
- .transform(a -> a.stream().map(Preview::from).collect(Collectors.toList()))
+ .transform(a -> a.stream()
+ .map(preview -> Preview.from(preview, fileProvider))
+ .collect(Collectors.toList()))
.or(List.of()));
}
static Quote from(
SignalServiceDataMessage.Quote quote,
RecipientResolver recipientResolver,
- RecipientAddressResolver addressResolver
+ RecipientAddressResolver addressResolver,
+ final AttachmentFileProvider fileProvider
) {
return new Quote(quote.getId(),
addressResolver.resolveRecipientAddress(recipientResolver.resolveRecipient(quote.getAuthor())),
.collect(Collectors.toList()),
quote.getAttachments() == null
? List.of()
- : quote.getAttachments().stream().map(Attachment::from).collect(Collectors.toList()));
+ : quote.getAttachments()
+ .stream()
+ .map(a -> Attachment.from(a, fileProvider))
+ .collect(Collectors.toList()));
}
}
public record Attachment(
Optional<String> id,
+ Optional<File> file,
Optional<String> fileName,
String contentType,
Optional<Long> uploadTimestamp,
boolean isBorderless
) {
- static Attachment from(SignalServiceAttachment attachment) {
+ static Attachment from(SignalServiceAttachment attachment, AttachmentFileProvider fileProvider) {
if (attachment.isPointer()) {
final var a = attachment.asPointer();
return new Attachment(Optional.of(a.getRemoteId().toString()),
+ Optional.of(fileProvider.getFile(a.getRemoteId())),
Optional.ofNullable(a.getFileName().orNull()),
a.getContentType(),
a.getUploadTimestamp() == 0 ? Optional.empty() : Optional.of(a.getUploadTimestamp()),
} else {
final var a = attachment.asStream();
return new Attachment(Optional.empty(),
+ Optional.empty(),
Optional.ofNullable(a.getFileName().orNull()),
a.getContentType(),
a.getUploadTimestamp() == 0 ? Optional.empty() : Optional.of(a.getUploadTimestamp()),
}
}
- static Attachment from(SignalServiceDataMessage.Quote.QuotedAttachment a) {
+ static Attachment from(
+ SignalServiceDataMessage.Quote.QuotedAttachment a, final AttachmentFileProvider fileProvider
+ ) {
return new Attachment(Optional.empty(),
+ Optional.empty(),
Optional.ofNullable(a.getFileName()),
a.getContentType(),
Optional.empty(),
Optional.empty(),
Optional.empty(),
- a.getThumbnail() == null ? Optional.empty() : Optional.of(Attachment.from(a.getThumbnail())),
+ a.getThumbnail() == null
+ ? Optional.empty()
+ : Optional.of(Attachment.from(a.getThumbnail(), fileProvider)),
Optional.empty(),
Optional.empty(),
Optional.empty(),
Optional<String> organization
) {
- static SharedContact from(org.whispersystems.signalservice.api.messages.shared.SharedContact sharedContact) {
+ static SharedContact from(
+ org.whispersystems.signalservice.api.messages.shared.SharedContact sharedContact,
+ final AttachmentFileProvider fileProvider
+ ) {
return new SharedContact(Name.from(sharedContact.getName()),
- Optional.ofNullable(sharedContact.getAvatar().transform(Avatar::from).orNull()),
+ Optional.ofNullable(sharedContact.getAvatar()
+ .transform(avatar1 -> Avatar.from(avatar1, fileProvider))
+ .orNull()),
sharedContact.getPhone()
.transform(p -> p.stream().map(Phone::from).collect(Collectors.toList()))
.or(List.of()),
public record Avatar(Attachment attachment, boolean isProfile) {
- static Avatar from(org.whispersystems.signalservice.api.messages.shared.SharedContact.Avatar avatar) {
- return new Avatar(Attachment.from(avatar.getAttachment()), avatar.isProfile());
+ static Avatar from(
+ org.whispersystems.signalservice.api.messages.shared.SharedContact.Avatar avatar,
+ final AttachmentFileProvider fileProvider
+ ) {
+ return new Avatar(Attachment.from(avatar.getAttachment(), fileProvider), avatar.isProfile());
}
}
public record Preview(String title, String description, long date, String url, Optional<Attachment> image) {
- static Preview from(SignalServiceDataMessage.Preview preview) {
+ static Preview from(
+ SignalServiceDataMessage.Preview preview, final AttachmentFileProvider fileProvider
+ ) {
return new Preview(preview.getTitle(),
preview.getDescription(),
preview.getDate(),
preview.getUrl(),
- Optional.ofNullable(preview.getImage().transform(Attachment::from).orNull()));
+ Optional.ofNullable(preview.getImage()
+ .transform(as -> Attachment.from(as, fileProvider))
+ .orNull()));
}
}
}
public static Sync from(
final SignalServiceSyncMessage syncMessage,
RecipientResolver recipientResolver,
- RecipientAddressResolver addressResolver
+ RecipientAddressResolver addressResolver,
+ final AttachmentFileProvider fileProvider
) {
return new Sync(Optional.ofNullable(syncMessage.getSent()
- .transform(s -> Sent.from(s, recipientResolver, addressResolver))
+ .transform(s -> Sent.from(s, recipientResolver, addressResolver, fileProvider))
.orNull()),
Optional.ofNullable(syncMessage.getBlockedList()
.transform(b -> Blocked.from(b, recipientResolver, addressResolver))
static Sent from(
SentTranscriptMessage sentMessage,
RecipientResolver recipientResolver,
- RecipientAddressResolver addressResolver
+ RecipientAddressResolver addressResolver,
+ final AttachmentFileProvider fileProvider
) {
return new Sent(sentMessage.getTimestamp(),
sentMessage.getExpirationStartTimestamp(),
.stream()
.map(d -> addressResolver.resolveRecipientAddress(recipientResolver.resolveRecipient(d)))
.collect(Collectors.toSet()),
- Data.from(sentMessage.getMessage(), recipientResolver, addressResolver));
+ Data.from(sentMessage.getMessage(), recipientResolver, addressResolver, fileProvider));
}
}
public record Busy(long id) {
- static Offer from(OfferMessage offerMessage) {
- return new Offer(offerMessage.getId(),
- offerMessage.getSdp(),
- Offer.Type.from(offerMessage.getType()),
- offerMessage.getOpaque());
- }
-
static Busy from(BusyMessage busyMessage) {
return new Busy(busyMessage.getId());
}
SignalServiceEnvelope envelope,
SignalServiceContent content,
RecipientResolver recipientResolver,
- RecipientAddressResolver addressResolver
+ RecipientAddressResolver addressResolver,
+ final AttachmentFileProvider fileProvider
) {
final var source = !envelope.isUnidentifiedSender() && envelope.hasSourceUuid()
? recipientResolver.resolveRecipient(envelope.getSourceAddress())
receipt = Optional.ofNullable(content.getReceiptMessage().transform(Receipt::from).orNull());
typing = Optional.ofNullable(content.getTypingMessage().transform(Typing::from).orNull());
data = Optional.ofNullable(content.getDataMessage()
- .transform(dataMessage -> Data.from(dataMessage, recipientResolver, addressResolver))
+ .transform(dataMessage -> Data.from(dataMessage, recipientResolver, addressResolver, fileProvider))
.orNull());
sync = Optional.ofNullable(content.getSyncMessage()
- .transform(s -> Sync.from(s, recipientResolver, addressResolver))
+ .transform(s -> Sync.from(s, recipientResolver, addressResolver, fileProvider))
.orNull());
call = Optional.ofNullable(content.getCallMessage().transform(Call::from).orNull());
} else {
sync,
call);
}
+
+ public interface AttachmentFileProvider {
+
+ File getFile(SignalServiceAttachmentRemoteId attachmentRemoteId);
+ }
}
import org.whispersystems.libsignal.InvalidMessageException;
import org.whispersystems.signalservice.api.messages.SignalServiceAttachment;
import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentPointer;
+import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentRemoteId;
import org.whispersystems.signalservice.api.push.exceptions.MissingConfigurationException;
import java.io.File;
this.attachmentStore = attachmentStore;
}
- public File getAttachmentFile(String attachmentId) {
+ public File getAttachmentFile(SignalServiceAttachmentRemoteId attachmentId) {
return attachmentStore.getAttachmentFile(attachmentId);
}
handler.handleMessage(MessageEnvelope.from(envelope,
content,
recipientResolver,
- account.getRecipientStore()::resolveRecipientAddress), exception);
+ account.getRecipientStore()::resolveRecipientAddress,
+ attachmentHelper::getAttachmentFile), exception);
return actions;
}
}
import org.asamk.Signal;
import org.asamk.signal.commands.Command;
import org.asamk.signal.commands.Commands;
-import org.asamk.signal.commands.ExtendedDbusCommand;
import org.asamk.signal.commands.LocalCommand;
import org.asamk.signal.commands.MultiLocalCommand;
import org.asamk.signal.commands.ProvisioningCommand;
? TrustNewIdentity.ON_FIRST_USE
: trustNewIdentityCli == TrustNewIdentityCli.ALWAYS ? TrustNewIdentity.ALWAYS : TrustNewIdentity.NEVER;
- if (command instanceof ProvisioningCommand) {
+ if (command instanceof ProvisioningCommand provisioningCommand) {
if (username != null) {
throw new UserErrorException("You cannot specify a username (phone number) when linking");
}
- handleProvisioningCommand((ProvisioningCommand) command, dataPath, serviceEnvironment, outputWriter);
+ handleProvisioningCommand(provisioningCommand, dataPath, serviceEnvironment, outputWriter);
return;
}
if (username == null) {
var usernames = Manager.getAllLocalNumbers(dataPath);
- if (command instanceof MultiLocalCommand) {
- handleMultiLocalCommand((MultiLocalCommand) command,
+ if (command instanceof MultiLocalCommand multiLocalCommand) {
+ handleMultiLocalCommand(multiLocalCommand,
dataPath,
serviceEnvironment,
usernames,
throw new UserErrorException("Invalid username (phone number), make sure you include the country code.");
}
- if (command instanceof RegistrationCommand) {
- handleRegistrationCommand((RegistrationCommand) command, username, dataPath, serviceEnvironment);
+ if (command instanceof RegistrationCommand registrationCommand) {
+ handleRegistrationCommand(registrationCommand, username, dataPath, serviceEnvironment);
return;
}
private void handleCommand(
Command command, Signal ts, DBusConnection dBusConn, OutputWriter outputWriter
) throws CommandException {
- if (command instanceof ExtendedDbusCommand) {
- ((ExtendedDbusCommand) command).handleCommand(ns, ts, dBusConn, outputWriter);
- } else if (command instanceof LocalCommand) {
+ if (command instanceof LocalCommand localCommand) {
try {
- ((LocalCommand) command).handleCommand(ns, new DbusManagerImpl(ts, dBusConn), outputWriter);
+ localCommand.handleCommand(ns, new DbusManagerImpl(ts, dBusConn), outputWriter);
} catch (UnsupportedOperationException e) {
throw new UserErrorException("Command is not yet implemented via dbus", e);
} catch (DBusExecutionException e) {
var attachments = new ArrayList<String>();
if (message.attachments().size() > 0) {
for (var attachment : message.attachments()) {
- if (attachment.id().isPresent()) {
- attachments.add(m.getAttachmentFile(attachment.id().get()).getAbsolutePath());
+ if (attachment.file().isPresent()) {
+ attachments.add(attachment.file().get().getAbsolutePath());
}
}
}
"author",
new Variant<>(quote.author().getLegacyIdentifier()),
"text",
- new Variant<>(quote.text()));
+ new Variant<>(quote.text().orElse("")));
}
private Map<String, Variant<? extends Serializable>> getStickerMap(final MessageEnvelope.Data.Sticker sticker) {
) {
final var map = new HashMap<String, Variant<?>>();
if (a.id().isPresent()) {
- map.put("file", new Variant<>(m.getAttachmentFile(a.id().get()).getAbsolutePath()));
map.put("remoteId", new Variant<>(a.id().get()));
}
+ if (a.file().isPresent()) {
+ map.put("file", new Variant<>(a.file().get().getAbsolutePath()));
+ }
+ map.put("contentType", new Variant<>(a.contentType()));
map.put("isVoiceNote", new Variant<>(a.isVoiceNote()));
map.put("isBorderless", new Variant<>(a.isBorderless()));
map.put("isGif", new Variant<>(a.isGif()));
if (attachment.width().isPresent() || attachment.height().isPresent()) {
writer.println("Dimensions: {}x{}", attachment.width().orElse(0), attachment.height().orElse(0));
}
- if (attachment.id().isPresent()) {
- var file = m.getAttachmentFile(attachment.id().get());
+ if (attachment.file().isPresent()) {
+ var file = attachment.file().get();
if (file.exists()) {
writer.println("Stored plaintext in: {}", file);
}
+++ /dev/null
-package org.asamk.signal.commands;
-
-import net.sourceforge.argparse4j.inf.Namespace;
-
-import org.asamk.Signal;
-import org.asamk.signal.OutputWriter;
-import org.asamk.signal.commands.exceptions.CommandException;
-import org.freedesktop.dbus.connections.impl.DBusConnection;
-
-public interface ExtendedDbusCommand extends CliCommand {
-
- void handleCommand(
- Namespace ns, Signal signal, DBusConnection dbusconnection, final OutputWriter outputWriter
- ) throws CommandException;
-}
import net.sourceforge.argparse4j.inf.Namespace;
import net.sourceforge.argparse4j.inf.Subparser;
-import org.asamk.Signal;
import org.asamk.signal.JsonReceiveMessageHandler;
import org.asamk.signal.JsonWriter;
import org.asamk.signal.OutputType;
import org.asamk.signal.ReceiveMessageHandler;
import org.asamk.signal.commands.exceptions.CommandException;
import org.asamk.signal.commands.exceptions.IOErrorException;
-import org.asamk.signal.commands.exceptions.UnexpectedErrorException;
-import org.asamk.signal.json.JsonMessageEnvelope;
import org.asamk.signal.manager.Manager;
-import org.asamk.signal.util.DateUtils;
-import org.freedesktop.dbus.DBusMap;
-import org.freedesktop.dbus.connections.impl.DBusConnection;
-import org.freedesktop.dbus.exceptions.DBusException;
-import org.freedesktop.dbus.types.Variant;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
-import java.util.Base64;
import java.util.List;
-import java.util.Map;
import java.util.concurrent.TimeUnit;
-public class ReceiveCommand implements ExtendedDbusCommand, LocalCommand {
+public class ReceiveCommand implements LocalCommand {
private final static Logger logger = LoggerFactory.getLogger(ReceiveCommand.class);
return List.of(OutputType.PLAIN_TEXT, OutputType.JSON);
}
- public void handleCommand(
- final Namespace ns, final Signal signal, DBusConnection dbusconnection, final OutputWriter outputWriter
- ) throws CommandException {
- try {
- if (outputWriter instanceof JsonWriter jsonWriter) {
-
- dbusconnection.addSigHandler(Signal.MessageReceived.class, signal, messageReceived -> {
- var envelope = JsonMessageEnvelope.from(messageReceived);
- final var object = Map.of("envelope", envelope);
- jsonWriter.write(object);
- });
-
- dbusconnection.addSigHandler(Signal.ReceiptReceived.class, signal, receiptReceived -> {
- var envelope = JsonMessageEnvelope.from(receiptReceived);
- final var object = Map.of("envelope", envelope);
- jsonWriter.write(object);
- });
-
- dbusconnection.addSigHandler(Signal.SyncMessageReceived.class, signal, syncReceived -> {
- var envelope = JsonMessageEnvelope.from(syncReceived);
- final var object = Map.of("envelope", envelope);
- jsonWriter.write(object);
- });
- } else {
- final var writer = (PlainTextWriter) outputWriter;
-
- dbusconnection.addSigHandler(Signal.MessageReceivedV2.class, signal, messageReceived -> {
- writer.println("Envelope from: {}", messageReceived.getSender());
- writer.println("Timestamp: {}", DateUtils.formatTimestamp(messageReceived.getTimestamp()));
- writer.println("Body: {}", messageReceived.getMessage());
- if (messageReceived.getGroupId().length > 0) {
- writer.println("Group info:");
- writer.indentedWriter()
- .println("Id: {}", Base64.getEncoder().encodeToString(messageReceived.getGroupId()));
- }
- final var extras = messageReceived.getExtras();
- printMessageExtras(writer, extras);
- writer.println();
- });
-
- dbusconnection.addSigHandler(Signal.ReceiptReceivedV2.class, signal, receiptReceived -> {
- writer.println("Receipt from: {}", receiptReceived.getSender());
- writer.println("Timestamp: {}", DateUtils.formatTimestamp(receiptReceived.getTimestamp()));
- writer.println("Type: {}", receiptReceived.getReceiptType());
- });
-
- dbusconnection.addSigHandler(Signal.SyncMessageReceivedV2.class, signal, syncReceived -> {
- writer.println("Sync Envelope from: {} to: {}",
- syncReceived.getSource(),
- syncReceived.getDestination());
- writer.println("Timestamp: {}", DateUtils.formatTimestamp(syncReceived.getTimestamp()));
- writer.println("Body: {}", syncReceived.getMessage());
- if (syncReceived.getGroupId().length > 0) {
- writer.println("Group info:");
- writer.indentedWriter()
- .println("Id: {}", Base64.getEncoder().encodeToString(syncReceived.getGroupId()));
- }
- final var extras = syncReceived.getExtras();
- printMessageExtras(writer, extras);
- writer.println();
- });
- }
- } catch (DBusException e) {
- logger.error("Dbus client failed", e);
- throw new UnexpectedErrorException("Dbus client failed", e);
- }
-
- double timeout = ns.getDouble("timeout");
- long timeoutMilliseconds = timeout < 0 ? 10000 : (long) (timeout * 1000);
-
- while (true) {
- try {
- Thread.sleep(timeoutMilliseconds);
- } catch (InterruptedException ignored) {
- break;
- }
- if (timeout >= 0) {
- break;
- }
- }
- }
-
- private void printMessageExtras(final PlainTextWriter writer, final Map<String, Variant<?>> extras) {
- if (extras.containsKey("attachments")) {
- final List<DBusMap<String, Variant<?>>> attachments = getValue(extras, "attachments");
- if (attachments.size() > 0) {
- writer.println("Attachments:");
- for (var attachment : attachments) {
- final String value = getValue(attachment, "file");
- writer.println("- Stored plaintext in: {}", value);
- }
- }
- }
- }
-
- @SuppressWarnings("unchecked")
- private <T> T getValue(final Map<String, Variant<?>> stringVariantMap, final String field) {
- return (T) stringVariantMap.get(field).getValue();
- }
-
@Override
public void handleCommand(
final Namespace ns, final Manager m, final OutputWriter outputWriter
import org.asamk.signal.manager.api.InactiveGroupLinkException;
import org.asamk.signal.manager.api.InvalidDeviceLinkException;
import org.asamk.signal.manager.api.Message;
+import org.asamk.signal.manager.api.MessageEnvelope;
import org.asamk.signal.manager.api.Pair;
import org.asamk.signal.manager.api.RecipientIdentifier;
import org.asamk.signal.manager.api.SendGroupMessageResults;
import org.asamk.signal.manager.storage.recipients.Contact;
import org.asamk.signal.manager.storage.recipients.Profile;
import org.asamk.signal.manager.storage.recipients.RecipientAddress;
+import org.freedesktop.dbus.DBusMap;
import org.freedesktop.dbus.DBusPath;
import org.freedesktop.dbus.connections.impl.DBusConnection;
import org.freedesktop.dbus.exceptions.DBusException;
import org.freedesktop.dbus.interfaces.DBusInterface;
+import org.freedesktop.dbus.interfaces.DBusSigHandler;
+import org.freedesktop.dbus.types.Variant;
import java.io.File;
import java.io.IOException;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
private final Signal signal;
private final DBusConnection connection;
+ private final Set<ReceiveMessageHandler> messageHandlers = new HashSet<>();
+ private DBusSigHandler<Signal.MessageReceivedV2> dbusMsgHandler;
+ private DBusSigHandler<Signal.ReceiptReceivedV2> dbusRcptHandler;
+ private DBusSigHandler<Signal.SyncMessageReceivedV2> dbusSyncHandler;
+
public DbusManagerImpl(final Signal signal, DBusConnection connection) {
this.signal = signal;
this.connection = connection;
@Override
public void submitRateLimitRecaptchaChallenge(final String challenge, final String captcha) throws IOException {
- throw new UnsupportedOperationException();
+ signal.submitRateLimitChallenge(challenge, captcha);
}
@Override
public void sendViewedReceipt(
final RecipientIdentifier.Single sender, final List<Long> messageIds
) throws IOException, UntrustedIdentityException {
- throw new UnsupportedOperationException();
+ signal.sendViewedReceipt(sender.getIdentifier(), messageIds);
}
@Override
@Override
public void addReceiveHandler(final ReceiveMessageHandler handler) {
- throw new UnsupportedOperationException();
+ synchronized (messageHandlers) {
+ if (messageHandlers.size() == 0) {
+ installMessageHandlers();
+ }
+ messageHandlers.add(handler);
+ }
}
@Override
public void removeReceiveHandler(final ReceiveMessageHandler handler) {
- throw new UnsupportedOperationException();
+ synchronized (messageHandlers) {
+ messageHandlers.remove(handler);
+ if (messageHandlers.size() == 0) {
+ try {
+ connection.removeSigHandler(Signal.MessageReceivedV2.class, signal, this.dbusMsgHandler);
+ connection.removeSigHandler(Signal.ReceiptReceivedV2.class, signal, this.dbusRcptHandler);
+ connection.removeSigHandler(Signal.SyncMessageReceivedV2.class, signal, this.dbusSyncHandler);
+ } catch (DBusException e) {
+ e.printStackTrace();
+ }
+ }
+ }
}
@Override
public boolean isReceiving() {
- throw new UnsupportedOperationException();
+ synchronized (messageHandlers) {
+ return messageHandlers.size() > 0;
+ }
}
@Override
public void receiveMessages(final ReceiveMessageHandler handler) throws IOException {
- throw new UnsupportedOperationException();
+ addReceiveHandler(handler);
+ try {
+ synchronized (this) {
+ this.wait();
+ }
+ } catch (InterruptedException ignored) {
+ }
+ removeReceiveHandler(handler);
}
@Override
public void receiveMessages(
final long timeout, final TimeUnit unit, final ReceiveMessageHandler handler
) throws IOException {
- throw new UnsupportedOperationException();
+ addReceiveHandler(handler);
+ try {
+ Thread.sleep(unit.toMillis(timeout));
+ } catch (InterruptedException ignored) {
+ }
+ removeReceiveHandler(handler);
}
@Override
public void setIgnoreAttachments(final boolean ignoreAttachments) {
- throw new UnsupportedOperationException();
}
@Override
public boolean hasCaughtUpWithOldMessages() {
- throw new UnsupportedOperationException();
+ return true;
}
@Override
return signal.isContactBlocked(recipient.getIdentifier());
}
- @Override
- public File getAttachmentFile(final String attachmentId) {
- throw new UnsupportedOperationException();
- }
-
@Override
public void sendContacts() throws IOException {
signal.sendContacts();
throw new AssertionError(e);
}
}
+
+ private void installMessageHandlers() {
+ try {
+ this.dbusMsgHandler = messageReceived -> {
+ final var extras = messageReceived.getExtras();
+ final var envelope = new MessageEnvelope(Optional.of(new RecipientAddress(null,
+ messageReceived.getSender())),
+ 0,
+ messageReceived.getTimestamp(),
+ 0,
+ 0,
+ false,
+ Optional.empty(),
+ Optional.empty(),
+ Optional.of(new MessageEnvelope.Data(messageReceived.getTimestamp(),
+ messageReceived.getGroupId().length > 0
+ ? Optional.of(new MessageEnvelope.Data.GroupContext(GroupId.unknownVersion(
+ messageReceived.getGroupId()), false, 0))
+ : Optional.empty(),
+ Optional.empty(),
+ Optional.of(messageReceived.getMessage()),
+ 0,
+ false,
+ false,
+ false,
+ false,
+ Optional.empty(),
+ Optional.empty(),
+ getAttachments(extras),
+ Optional.empty(),
+ Optional.empty(),
+ List.of(),
+ List.of(),
+ List.of())),
+ Optional.empty(),
+ Optional.empty());
+ synchronized (messageHandlers) {
+ for (final var messageHandler : messageHandlers) {
+ messageHandler.handleMessage(envelope, null);
+ }
+ }
+ };
+ connection.addSigHandler(Signal.MessageReceivedV2.class, signal, this.dbusMsgHandler);
+
+ this.dbusRcptHandler = receiptReceived -> {
+ final var type = switch (receiptReceived.getReceiptType()) {
+ case "read" -> MessageEnvelope.Receipt.Type.READ;
+ case "viewed" -> MessageEnvelope.Receipt.Type.VIEWED;
+ case "delivery" -> MessageEnvelope.Receipt.Type.DELIVERY;
+ default -> MessageEnvelope.Receipt.Type.UNKNOWN;
+ };
+ final var envelope = new MessageEnvelope(Optional.of(new RecipientAddress(null,
+ receiptReceived.getSender())),
+ 0,
+ receiptReceived.getTimestamp(),
+ 0,
+ 0,
+ false,
+ Optional.of(new MessageEnvelope.Receipt(receiptReceived.getTimestamp(),
+ type,
+ List.of(receiptReceived.getTimestamp()))),
+ Optional.empty(),
+ Optional.empty(),
+ Optional.empty(),
+ Optional.empty());
+ synchronized (messageHandlers) {
+ for (final var messageHandler : messageHandlers) {
+ messageHandler.handleMessage(envelope, null);
+ }
+ }
+ };
+ connection.addSigHandler(Signal.ReceiptReceivedV2.class, signal, this.dbusRcptHandler);
+
+ this.dbusSyncHandler = syncReceived -> {
+ final var extras = syncReceived.getExtras();
+ final var envelope = new MessageEnvelope(Optional.of(new RecipientAddress(null,
+ syncReceived.getSource())),
+ 0,
+ syncReceived.getTimestamp(),
+ 0,
+ 0,
+ false,
+ Optional.empty(),
+ Optional.empty(),
+ Optional.empty(),
+ Optional.of(new MessageEnvelope.Sync(Optional.of(new MessageEnvelope.Sync.Sent(syncReceived.getTimestamp(),
+ syncReceived.getTimestamp(),
+ syncReceived.getDestination().isEmpty()
+ ? Optional.empty()
+ : Optional.of(new RecipientAddress(null, syncReceived.getDestination())),
+ Set.of(),
+ new MessageEnvelope.Data(syncReceived.getTimestamp(),
+ syncReceived.getGroupId().length > 0
+ ? Optional.of(new MessageEnvelope.Data.GroupContext(GroupId.unknownVersion(
+ syncReceived.getGroupId()), false, 0))
+ : Optional.empty(),
+ Optional.empty(),
+ Optional.of(syncReceived.getMessage()),
+ 0,
+ false,
+ false,
+ false,
+ false,
+ Optional.empty(),
+ Optional.empty(),
+ getAttachments(extras),
+ Optional.empty(),
+ Optional.empty(),
+ List.of(),
+ List.of(),
+ List.of()))),
+ Optional.empty(),
+ List.of(),
+ List.of(),
+ Optional.empty(),
+ Optional.empty(),
+ Optional.empty(),
+ Optional.empty())),
+ Optional.empty());
+ synchronized (messageHandlers) {
+ for (final var messageHandler : messageHandlers) {
+ messageHandler.handleMessage(envelope, null);
+ }
+ }
+ };
+ connection.addSigHandler(Signal.SyncMessageReceivedV2.class, signal, this.dbusSyncHandler);
+ } catch (DBusException e) {
+ e.printStackTrace();
+ }
+ }
+
+ private List<MessageEnvelope.Data.Attachment> getAttachments(final Map<String, Variant<?>> extras) {
+ if (!extras.containsKey("attachments")) {
+ return List.of();
+ }
+
+ final List<DBusMap<String, Variant<?>>> attachments = getValue(extras, "attachments");
+ return attachments.stream().map(a -> {
+ final String file = a.containsKey("file") ? getValue(a, "file") : null;
+ return new MessageEnvelope.Data.Attachment(a.containsKey("remoteId")
+ ? Optional.of(getValue(a, "remoteId"))
+ : Optional.empty(),
+ file != null ? Optional.of(new File(file)) : Optional.empty(),
+ Optional.empty(),
+ getValue(a, "contentType"),
+ Optional.empty(),
+ Optional.empty(),
+ Optional.empty(),
+ Optional.empty(),
+ Optional.empty(),
+ Optional.empty(),
+ Optional.empty(),
+ getValue(a, "isVoiceNote"),
+ getValue(a, "isGif"),
+ getValue(a, "isBorderless"));
+ }).collect(Collectors.toList());
+ }
+
+ @SuppressWarnings("unchecked")
+ private <T> T getValue(
+ final Map<String, Variant<?>> stringVariantMap, final String field
+ ) {
+ return (T) stringVariantMap.get(field).getValue();
+ }
}
import com.fasterxml.jackson.annotation.JsonInclude;
-import org.asamk.Signal;
import org.asamk.signal.manager.Manager;
import org.asamk.signal.manager.UntrustedIdentityException;
import org.asamk.signal.manager.api.InvalidNumberException;
import org.asamk.signal.manager.api.MessageEnvelope;
import org.asamk.signal.manager.api.RecipientIdentifier;
-import java.util.List;
import java.util.UUID;
public record JsonMessageEnvelope(
receiptMessage,
typingMessage);
}
-
- public static JsonMessageEnvelope from(Signal.MessageReceived messageReceived) {
- return new JsonMessageEnvelope(messageReceived.getSource(),
- null,
- null,
- null,
- null,
- messageReceived.getTimestamp(),
- JsonDataMessage.from(messageReceived),
- null,
- null,
- null,
- null);
- }
-
- public static JsonMessageEnvelope from(Signal.ReceiptReceived receiptReceived) {
- return new JsonMessageEnvelope(receiptReceived.getSender(),
- null,
- null,
- null,
- null,
- receiptReceived.getTimestamp(),
- null,
- null,
- null,
- JsonReceiptMessage.deliveryReceipt(receiptReceived.getTimestamp(),
- List.of(receiptReceived.getTimestamp())),
- null);
- }
-
- public static JsonMessageEnvelope from(Signal.SyncMessageReceived messageReceived) {
- return new JsonMessageEnvelope(messageReceived.getSource(),
- null,
- null,
- null,
- null,
- messageReceived.getTimestamp(),
- null,
- JsonSyncMessage.from(messageReceived),
- null,
- null,
- null);
- }
}
final var timestamps = receiptMessage.timestamps();
return new JsonReceiptMessage(when, isDelivery, isRead, isViewed, timestamps);
}
-
- static JsonReceiptMessage deliveryReceipt(final long when, final List<Long> timestamps) {
- return new JsonReceiptMessage(when, true, false, false, timestamps);
- }
}
import com.fasterxml.jackson.annotation.JsonUnwrapped;
-import org.asamk.Signal;
import org.asamk.signal.manager.api.MessageEnvelope;
import java.util.UUID;
return new JsonSyncDataMessage(null, null, null, JsonDataMessage.from(transcriptMessage.message()));
}
}
-
- static JsonSyncDataMessage from(Signal.SyncMessageReceived messageReceived) {
- return new JsonSyncDataMessage(messageReceived.getDestination(),
- null,
- null,
- JsonDataMessage.from(messageReceived));
- }
}
import com.fasterxml.jackson.annotation.JsonInclude;
-import org.asamk.Signal;
import org.asamk.signal.manager.api.MessageEnvelope;
import org.asamk.signal.manager.groups.GroupId;
import org.asamk.signal.manager.storage.recipients.RecipientAddress;
}
return new JsonSyncMessage(sentMessage, blockedNumbers, blockedGroupIds, readMessages, type);
}
-
- static JsonSyncMessage from(Signal.SyncMessageReceived messageReceived) {
- return new JsonSyncMessage(JsonSyncDataMessage.from(messageReceived), null, null, null, null);
- }
}