1 package org
.asamk
.signal
.manager
.helper
;
3 import org
.asamk
.signal
.manager
.AttachmentInvalidException
;
4 import org
.asamk
.signal
.manager
.AttachmentStore
;
5 import org
.asamk
.signal
.manager
.SignalDependencies
;
6 import org
.asamk
.signal
.manager
.config
.ServiceConfig
;
7 import org
.asamk
.signal
.manager
.util
.AttachmentUtils
;
8 import org
.asamk
.signal
.manager
.util
.IOUtils
;
9 import org
.slf4j
.Logger
;
10 import org
.slf4j
.LoggerFactory
;
11 import org
.whispersystems
.libsignal
.InvalidMessageException
;
12 import org
.whispersystems
.signalservice
.api
.messages
.SignalServiceAttachment
;
13 import org
.whispersystems
.signalservice
.api
.messages
.SignalServiceAttachmentPointer
;
14 import org
.whispersystems
.signalservice
.api
.push
.exceptions
.MissingConfigurationException
;
17 import java
.io
.IOException
;
18 import java
.io
.InputStream
;
19 import java
.io
.OutputStream
;
20 import java
.nio
.file
.Files
;
21 import java
.util
.ArrayList
;
22 import java
.util
.List
;
24 public class AttachmentHelper
{
26 private final static Logger logger
= LoggerFactory
.getLogger(AttachmentHelper
.class);
28 private final SignalDependencies dependencies
;
29 private final AttachmentStore attachmentStore
;
31 public AttachmentHelper(
32 final SignalDependencies dependencies
, final AttachmentStore attachmentStore
34 this.dependencies
= dependencies
;
35 this.attachmentStore
= attachmentStore
;
38 public List
<SignalServiceAttachment
> uploadAttachments(final List
<String
> attachments
) throws AttachmentInvalidException
, IOException
{
39 var attachmentStreams
= AttachmentUtils
.getSignalServiceAttachments(attachments
);
41 // Upload attachments here, so we only upload once even for multiple recipients
42 var messageSender
= dependencies
.getMessageSender();
43 var attachmentPointers
= new ArrayList
<SignalServiceAttachment
>(attachmentStreams
.size());
44 for (var attachment
: attachmentStreams
) {
45 if (attachment
.isStream()) {
46 attachmentPointers
.add(messageSender
.uploadAttachment(attachment
.asStream()));
47 } else if (attachment
.isPointer()) {
48 attachmentPointers
.add(attachment
.asPointer());
51 return attachmentPointers
;
54 public void downloadAttachment(final SignalServiceAttachment attachment
) {
55 if (!attachment
.isPointer()) {
56 logger
.warn("Invalid state, can't store an attachment stream.");
59 var pointer
= attachment
.asPointer();
60 if (pointer
.getPreview().isPresent()) {
61 final var preview
= pointer
.getPreview().get();
63 attachmentStore
.storeAttachmentPreview(pointer
.getRemoteId(),
64 outputStream
-> outputStream
.write(preview
, 0, preview
.length
));
65 } catch (IOException e
) {
66 logger
.warn("Failed to download attachment preview, ignoring: {}", e
.getMessage());
71 attachmentStore
.storeAttachment(pointer
.getRemoteId(),
72 outputStream
-> this.retrieveAttachment(pointer
, outputStream
));
73 } catch (IOException e
) {
74 logger
.warn("Failed to download attachment ({}), ignoring: {}", pointer
.getRemoteId(), e
.getMessage());
78 void retrieveAttachment(SignalServiceAttachment attachment
, OutputStream outputStream
) throws IOException
{
79 retrieveAttachment(attachment
, input
-> IOUtils
.copyStream(input
, outputStream
));
82 public void retrieveAttachment(
83 SignalServiceAttachment attachment
, AttachmentHandler consumer
84 ) throws IOException
{
85 if (attachment
.isStream()) {
86 try (var input
= attachment
.asStream().getInputStream()) {
87 consumer
.handle(input
);
92 var tmpFile
= IOUtils
.createTempFile();
93 try (var input
= retrieveAttachmentAsStream(attachment
.asPointer(), tmpFile
)) {
94 consumer
.handle(input
);
97 Files
.delete(tmpFile
.toPath());
98 } catch (IOException e
) {
99 logger
.warn("Failed to delete received attachment temp file “{}”, ignoring: {}",
106 private InputStream
retrieveAttachmentAsStream(
107 SignalServiceAttachmentPointer pointer
, File tmpFile
108 ) throws IOException
{
110 return dependencies
.getMessageReceiver()
111 .retrieveAttachment(pointer
, tmpFile
, ServiceConfig
.MAX_ATTACHMENT_SIZE
);
112 } catch (MissingConfigurationException
| InvalidMessageException e
) {
113 throw new IOException(e
);
118 public interface AttachmentHandler
{
120 void handle(InputStream inputStream
) throws IOException
;