]> nmode's Git Repositories - signal-cli/blob - lib/src/main/java/org/asamk/signal/manager/storage/messageCache/MessageCache.java
4853b2231de9909682c162e88dd1e57c611521ce
[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 final static 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 try {
55 var cacheFile = getMessageCacheFile(recipientId, now, envelope.getTimestamp());
56 MessageCacheUtils.storeEnvelope(envelope, cacheFile);
57 return new CachedMessage(cacheFile);
58 } catch (IOException e) {
59 logger.warn("Failed to store encrypted message in disk cache, ignoring: {}", e.getMessage());
60 return null;
61 }
62 }
63
64 public CachedMessage replaceSender(CachedMessage cachedMessage, RecipientId sender) throws IOException {
65 final var cacheFile = getMessageCacheFile(sender, cachedMessage.getFile().getName());
66 if (cacheFile.equals(cachedMessage.getFile())) {
67 return cachedMessage;
68 }
69 Files.move(cachedMessage.getFile().toPath(), cacheFile.toPath());
70 return new CachedMessage(cacheFile);
71 }
72
73 public void deleteMessages(final RecipientId recipientId) {
74 final var recipientMessageCachePath = getMessageCachePath(recipientId);
75 if (!recipientMessageCachePath.exists()) {
76 return;
77 }
78
79 for (var file : Objects.requireNonNull(recipientMessageCachePath.listFiles())) {
80 if (!file.isFile()) {
81 continue;
82 }
83
84 try {
85 Files.delete(file.toPath());
86 } catch (IOException e) {
87 logger.warn("Failed to delete cache file “{}”, ignoring: {}", file, e.getMessage());
88 }
89 }
90 }
91
92 private File getMessageCachePath(RecipientId recipientId) {
93 if (recipientId == null) {
94 return messageCachePath;
95 }
96
97 var sender = String.valueOf(recipientId.id());
98 return new File(messageCachePath, sender.replace("/", "_"));
99 }
100
101 private File getMessageCacheFile(RecipientId recipientId, String filename) throws IOException {
102 var cachePath = getMessageCachePath(recipientId);
103 IOUtils.createPrivateDirectories(cachePath);
104 return new File(cachePath, filename);
105 }
106
107 private File getMessageCacheFile(RecipientId recipientId, long now, long timestamp) throws IOException {
108 var cachePath = getMessageCachePath(recipientId);
109 IOUtils.createPrivateDirectories(cachePath);
110 return new File(cachePath, now + "_" + timestamp);
111 }
112
113 public void mergeRecipients(final RecipientId recipientId, final RecipientId toBeMergedRecipientId) {
114 final var toBeMergedMessageCachePath = getMessageCachePath(toBeMergedRecipientId);
115 if (!toBeMergedMessageCachePath.exists()) {
116 return;
117 }
118
119 for (var file : Objects.requireNonNull(toBeMergedMessageCachePath.listFiles())) {
120 if (!file.isFile()) {
121 continue;
122 }
123
124 try {
125 final var cacheFile = getMessageCacheFile(recipientId, file.getName());
126 Files.move(file.toPath(), cacheFile.toPath());
127 } catch (IOException e) {
128 logger.warn("Failed to move cache file “{}”, ignoring: {}", file, e.getMessage());
129 }
130 }
131 }
132 }