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
.Objects
;
16 import java
.util
.stream
.Collectors
;
17 import java
.util
.stream
.Stream
;
19 public class MessageCache
{
21 private final static Logger logger
= LoggerFactory
.getLogger(MessageCache
.class);
23 private final File messageCachePath
;
25 public MessageCache(final File messageCachePath
) {
26 this.messageCachePath
= messageCachePath
;
29 public Iterable
<CachedMessage
> getCachedMessages() {
30 if (!messageCachePath
.exists()) {
31 return Collections
.emptyList();
34 return Arrays
.stream(Objects
.requireNonNull(messageCachePath
.listFiles())).flatMap(dir
-> {
36 return Stream
.of(dir
);
39 final var files
= Objects
.requireNonNull(dir
.listFiles());
40 if (files
.length
== 0) {
42 Files
.delete(dir
.toPath());
43 } catch (IOException e
) {
44 logger
.warn("Failed to delete cache dir “{}”, ignoring: {}", dir
, e
.getMessage());
46 return Stream
.empty();
48 return Arrays
.stream(files
).filter(File
::isFile
);
49 }).map(CachedMessage
::new).collect(Collectors
.toList());
52 public CachedMessage
cacheMessage(SignalServiceEnvelope envelope
, RecipientId recipientId
) {
53 final var now
= System
.currentTimeMillis();
56 var cacheFile
= getMessageCacheFile(recipientId
, now
, envelope
.getTimestamp());
57 MessageCacheUtils
.storeEnvelope(envelope
, cacheFile
);
58 return new CachedMessage(cacheFile
);
59 } catch (IOException e
) {
60 logger
.warn("Failed to store encrypted message in disk cache, ignoring: {}", e
.getMessage());
65 public CachedMessage
replaceSender(CachedMessage cachedMessage
, RecipientId sender
) throws IOException
{
66 final var cacheFile
= getMessageCacheFile(sender
, cachedMessage
.getFile().getName());
67 if (cacheFile
.equals(cachedMessage
.getFile())) {
70 Files
.move(cachedMessage
.getFile().toPath(), cacheFile
.toPath());
71 return new CachedMessage(cacheFile
);
74 private File
getMessageCachePath(RecipientId recipientId
) {
75 if (recipientId
== null) {
76 return messageCachePath
;
79 var sender
= String
.valueOf(recipientId
.getId());
80 return new File(messageCachePath
, sender
.replace("/", "_"));
83 private File
getMessageCacheFile(RecipientId recipientId
, String filename
) throws IOException
{
84 var cachePath
= getMessageCachePath(recipientId
);
85 IOUtils
.createPrivateDirectories(cachePath
);
86 return new File(cachePath
, filename
);
89 private File
getMessageCacheFile(RecipientId recipientId
, long now
, long timestamp
) throws IOException
{
90 var cachePath
= getMessageCachePath(recipientId
);
91 IOUtils
.createPrivateDirectories(cachePath
);
92 return new File(cachePath
, now
+ "_" + timestamp
);
95 public void mergeRecipients(final RecipientId recipientId
, final RecipientId toBeMergedRecipientId
) {
96 final var toBeMergedMessageCachePath
= getMessageCachePath(toBeMergedRecipientId
);
97 if (!toBeMergedMessageCachePath
.exists()) {
101 for (var file
: Objects
.requireNonNull(toBeMergedMessageCachePath
.listFiles())) {
102 if (!file
.isFile()) {
107 final var cacheFile
= getMessageCacheFile(recipientId
, file
.getName());
108 Files
.move(file
.toPath(), cacheFile
.toPath());
109 } catch (IOException e
) {
110 logger
.warn("Failed to move cache file “{}”, ignoring: {}", file
, e
.getMessage());