]> nmode's Git Repositories - signal-cli/blob - lib/src/main/java/org/asamk/signal/manager/storage/messageCache/MessageCache.java
51882a442835f1e7527235b4b273064810384220
[signal-cli] / lib / src / main / java / org / asamk / signal / manager / storage / messageCache / MessageCache.java
1 package org.asamk.signal.manager.storage.messageCache;
2
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;
9
10 import java.io.File;
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.Stream;
17
18 public class MessageCache {
19
20 private static final Logger logger = LoggerFactory.getLogger(MessageCache.class);
21
22 private final File messageCachePath;
23
24 public MessageCache(final File messageCachePath) {
25 this.messageCachePath = messageCachePath;
26 }
27
28 public Iterable<CachedMessage> getCachedMessages() {
29 if (!messageCachePath.exists()) {
30 return Collections.emptyList();
31 }
32
33 return Arrays.stream(Objects.requireNonNull(messageCachePath.listFiles())).flatMap(dir -> {
34 if (dir.isFile()) {
35 return Stream.of(dir);
36 }
37
38 final var files = Objects.requireNonNull(dir.listFiles());
39 if (files.length == 0) {
40 try {
41 Files.delete(dir.toPath());
42 } catch (IOException e) {
43 logger.warn("Failed to delete cache dir “{}”, ignoring: {}", dir, e.getMessage());
44 }
45 return Stream.empty();
46 }
47 return Arrays.stream(files).filter(File::isFile);
48 }).map(CachedMessage::new).toList();
49 }
50
51 public CachedMessage cacheMessage(SignalServiceEnvelope envelope, RecipientId recipientId) {
52 final var now = System.currentTimeMillis();
53
54 File cacheFile;
55 try {
56 cacheFile = getMessageCacheFile(recipientId, now, envelope.getTimestamp());
57 } catch (IOException e) {
58 logger.warn("Failed to create recipient folder in disk cache: {}", e.getMessage());
59 throw new RuntimeException(e);
60 }
61
62 final var cachedMessage = new CachedMessage(cacheFile, envelope);
63 try {
64 MessageCacheUtils.storeEnvelope(envelope, cacheFile);
65 return cachedMessage;
66 } catch (IOException e) {
67 logger.warn("Failed to store encrypted message in disk cache, ignoring: {}", e.getMessage());
68 return cachedMessage;
69 }
70 }
71
72 public CachedMessage replaceSender(CachedMessage cachedMessage, RecipientId sender) throws IOException {
73 final var cacheFile = getMessageCacheFile(sender, cachedMessage.getFile().getName());
74 if (cacheFile.equals(cachedMessage.getFile())) {
75 return cachedMessage;
76 }
77 logger.debug("Moving cached message {} to {}", cachedMessage.getFile().toPath(), cacheFile.toPath());
78 Files.move(cachedMessage.getFile().toPath(), cacheFile.toPath());
79 return new CachedMessage(cacheFile);
80 }
81
82 public void deleteMessages(final RecipientId recipientId) {
83 final var recipientMessageCachePath = getMessageCachePath(recipientId);
84 if (!recipientMessageCachePath.exists()) {
85 return;
86 }
87
88 for (var file : Objects.requireNonNull(recipientMessageCachePath.listFiles())) {
89 if (!file.isFile()) {
90 continue;
91 }
92
93 try {
94 Files.delete(file.toPath());
95 } catch (IOException e) {
96 logger.warn("Failed to delete cache file “{}”, ignoring: {}", file, e.getMessage());
97 }
98 }
99 }
100
101 private File getMessageCachePath(RecipientId recipientId) {
102 if (recipientId == null) {
103 return messageCachePath;
104 }
105
106 var sender = String.valueOf(recipientId.id());
107 return new File(messageCachePath, sender.replace("/", "_"));
108 }
109
110 private File getMessageCacheFile(RecipientId recipientId, String filename) throws IOException {
111 var cachePath = getMessageCachePath(recipientId);
112 IOUtils.createPrivateDirectories(cachePath);
113 return new File(cachePath, filename);
114 }
115
116 private File getMessageCacheFile(RecipientId recipientId, long now, long timestamp) throws IOException {
117 var cachePath = getMessageCachePath(recipientId);
118 IOUtils.createPrivateDirectories(cachePath);
119 return new File(cachePath, now + "_" + timestamp);
120 }
121
122 public void mergeRecipients(final RecipientId recipientId, final RecipientId toBeMergedRecipientId) {
123 final var toBeMergedMessageCachePath = getMessageCachePath(toBeMergedRecipientId);
124 if (!toBeMergedMessageCachePath.exists()) {
125 return;
126 }
127
128 for (var file : Objects.requireNonNull(toBeMergedMessageCachePath.listFiles())) {
129 if (!file.isFile()) {
130 continue;
131 }
132
133 try {
134 final var cacheFile = getMessageCacheFile(recipientId, file.getName());
135 Files.move(file.toPath(), cacheFile.toPath());
136 } catch (IOException e) {
137 logger.warn("Failed to move cache file “{}”, ignoring: {}", file, e.getMessage(), e);
138 }
139 }
140 }
141 }