1 package org
.asamk
.signal
.manager
.storage
.messageCache
;
3 import org
.asamk
.signal
.manager
.storage
.recipients
.RecipientId
;
4 import org
.asamk
.signal
.manager
.util
.IOUtils
;
5 import org
.asamk
.signal
.manager
.util
.MessageCacheUtils
;
6 import org
.slf4j
.Logger
;
7 import org
.slf4j
.LoggerFactory
;
8 import org
.whispersystems
.signalservice
.api
.messages
.SignalServiceEnvelope
;
11 import java
.io
.IOException
;
12 import java
.nio
.file
.Files
;
13 import java
.util
.Arrays
;
14 import java
.util
.Collections
;
15 import java
.util
.Date
;
16 import java
.util
.Objects
;
17 import java
.util
.stream
.Collectors
;
18 import java
.util
.stream
.Stream
;
20 public class MessageCache
{
22 private final static Logger logger
= LoggerFactory
.getLogger(MessageCache
.class);
24 private final File messageCachePath
;
26 public MessageCache(final File messageCachePath
) {
27 this.messageCachePath
= messageCachePath
;
30 public Iterable
<CachedMessage
> getCachedMessages() {
31 if (!messageCachePath
.exists()) {
32 return Collections
.emptyList();
35 return Arrays
.stream(Objects
.requireNonNull(messageCachePath
.listFiles())).flatMap(dir
-> {
37 return Stream
.of(dir
);
40 final var files
= Objects
.requireNonNull(dir
.listFiles());
41 if (files
.length
== 0) {
43 Files
.delete(dir
.toPath());
44 } catch (IOException e
) {
45 logger
.warn("Failed to delete cache dir “{}”, ignoring: {}", dir
, e
.getMessage());
47 return Stream
.empty();
49 return Arrays
.stream(files
).filter(File
::isFile
);
50 }).map(CachedMessage
::new).collect(Collectors
.toList());
53 public CachedMessage
cacheMessage(SignalServiceEnvelope envelope
, RecipientId recipientId
) {
54 final var now
= new Date().getTime();
57 var cacheFile
= getMessageCacheFile(recipientId
, now
, envelope
.getTimestamp());
58 MessageCacheUtils
.storeEnvelope(envelope
, cacheFile
);
59 return new CachedMessage(cacheFile
);
60 } catch (IOException e
) {
61 logger
.warn("Failed to store encrypted message in disk cache, ignoring: {}", e
.getMessage());
66 public CachedMessage
replaceSender(CachedMessage cachedMessage
, RecipientId sender
) throws IOException
{
67 final var cacheFile
= getMessageCacheFile(sender
, cachedMessage
.getFile().getName());
68 if (cacheFile
.equals(cachedMessage
.getFile())) {
71 Files
.move(cachedMessage
.getFile().toPath(), cacheFile
.toPath());
72 return new CachedMessage(cacheFile
);
75 private File
getMessageCachePath(RecipientId recipientId
) {
76 if (recipientId
== null) {
77 return messageCachePath
;
80 var sender
= String
.valueOf(recipientId
.getId());
81 return new File(messageCachePath
, sender
.replace("/", "_"));
84 private File
getMessageCacheFile(RecipientId recipientId
, String filename
) throws IOException
{
85 var cachePath
= getMessageCachePath(recipientId
);
86 IOUtils
.createPrivateDirectories(cachePath
);
87 return new File(cachePath
, filename
);
90 private File
getMessageCacheFile(RecipientId recipientId
, long now
, long timestamp
) throws IOException
{
91 var cachePath
= getMessageCachePath(recipientId
);
92 IOUtils
.createPrivateDirectories(cachePath
);
93 return new File(cachePath
, now
+ "_" + timestamp
);
96 public void mergeRecipients(final RecipientId recipientId
, final RecipientId toBeMergedRecipientId
) {
97 final var toBeMergedMessageCachePath
= getMessageCachePath(toBeMergedRecipientId
);
98 if (!toBeMergedMessageCachePath
.exists()) {
102 for (var file
: Objects
.requireNonNull(toBeMergedMessageCachePath
.listFiles())) {
103 if (!file
.isFile()) {
108 final var cacheFile
= getMessageCacheFile(recipientId
, file
.getName());
109 Files
.move(file
.toPath(), cacheFile
.toPath());
110 } catch (IOException e
) {
111 logger
.warn("Failed to move cache file “{}”, ignoring: {}", file
, e
.getMessage());