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