configurable local message retention period. (untested)

This commit is contained in:
Daniel Gultsch 2017-01-23 17:14:30 +01:00
parent 4c6ef3b24e
commit 9b6ae6d75f
11 changed files with 114 additions and 25 deletions

View file

@ -114,6 +114,8 @@ public final class Config {
public static final ChatState DEFAULT_CHATSTATE = ChatState.ACTIVE; public static final ChatState DEFAULT_CHATSTATE = ChatState.ACTIVE;
public static final int TYPING_TIMEOUT = 8; public static final int TYPING_TIMEOUT = 8;
public static final int EXPIRY_INTERVAL = 30 * 60 * 1000; // 30 minutes
public static final String ENABLED_CIPHERS[] = { public static final String ENABLED_CIPHERS[] = {
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA384", "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA384",

View file

@ -20,6 +20,7 @@ import java.util.Collections;
import java.util.Comparator; import java.util.Comparator;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.ListIterator;
import java.util.Locale; import java.util.Locale;
import eu.siacs.conversations.Config; import eu.siacs.conversations.Config;
@ -930,6 +931,17 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl
account.getPgpDecryptionService().decrypt(messages); account.getPgpDecryptionService().decrypt(messages);
} }
public void expireOldMessages(long timestamp) {
synchronized (this.messages) {
for(ListIterator<Message> iterator = this.messages.listIterator(); iterator.hasNext();) {
if (iterator.next().getTimeSent() < timestamp) {
iterator.remove();
}
}
untieMessages();
}
}
public void sort() { public void sort() {
synchronized (this.messages) { synchronized (this.messages) {
Collections.sort(this.messages, new Comparator<Message>() { Collections.sort(this.messages, new Comparator<Message>() {

View file

@ -521,6 +521,12 @@ public class MessageParser extends AbstractParser implements OnMessagePacketRece
} }
} }
long deletionDate = mXmppConnectionService.getAutomaticMessageDeletionDate();
if (deletionDate != 0 && message.getTimeSent() < deletionDate) {
Log.d(Config.LOGTAG,account.getJid().toBareJid()+": skipping message from "+message.getCounterpart().toString()+" because it was sent prior to our deletion date");
return;
}
boolean checkForDuplicates = query != null boolean checkForDuplicates = query != null
|| (isTypeGroupChat && packet.hasChild("delay","urn:xmpp:delay")) || (isTypeGroupChat && packet.hasChild("delay","urn:xmpp:delay"))
|| message.getType() == Message.TYPE_PRIVATE; || message.getType() == Message.TYPE_PRIVATE;
@ -570,9 +576,7 @@ public class MessageParser extends AbstractParser implements OnMessagePacketRece
conversation.endOtrIfNeeded(); conversation.endOtrIfNeeded();
} }
if (message.getEncryption() == Message.ENCRYPTION_NONE || mXmppConnectionService.saveEncryptedMessages()) {
mXmppConnectionService.databaseBackend.createMessage(message); mXmppConnectionService.databaseBackend.createMessage(message);
}
final HttpConnectionManager manager = this.mXmppConnectionService.getHttpConnectionManager(); final HttpConnectionManager manager = this.mXmppConnectionService.getHttpConnectionManager();
if (message.trusted() && message.treatAsDownloadable() != Message.Decision.NEVER && manager.getAutoAcceptFileSize() > 0) { if (message.trusted() && message.treatAsDownloadable() != Message.Decision.NEVER && manager.getAutoAcceptFileSize() > 0) {
manager.createNewDownloadConnection(message); manager.createNewDownloadConnection(message);

View file

@ -769,6 +769,13 @@ public class DatabaseBackend extends SQLiteOpenHelper {
db.delete(Message.TABLENAME, Message.CONVERSATION + "=?", args); db.delete(Message.TABLENAME, Message.CONVERSATION + "=?", args);
} }
public boolean expireOldMessages(long timestamp) {
String where = Message.TIME_SENT+"<?";
String[] whereArgs = {String.valueOf(timestamp)};
SQLiteDatabase db = this.getReadableDatabase();
return db.delete(Message.TABLENAME,where,whereArgs) > 0;
}
public Pair<Long, String> getLastMessageReceived(Account account) { public Pair<Long, String> getLastMessageReceived(Account account) {
Cursor cursor = null; Cursor cursor = null;
try { try {

View file

@ -56,6 +56,7 @@ public class MessageArchiveService implements OnAdvancedStreamFeaturesLoaded {
startCatchup = lastClearDate.first; startCatchup = lastClearDate.first;
reference = null; reference = null;
} }
startCatchup = Math.max(startCatchup,mXmppConnectionService.getAutomaticMessageDeletionDate());
long endCatchup = account.getXmppConnection().getLastSessionEstablished(); long endCatchup = account.getXmppConnection().getLastSessionEstablished();
final Query query; final Query query;
if (startCatchup == 0) { if (startCatchup == 0) {
@ -107,12 +108,15 @@ public class MessageArchiveService implements OnAdvancedStreamFeaturesLoaded {
public Query query(Conversation conversation, long start, long end) { public Query query(Conversation conversation, long start, long end) {
synchronized (this.queries) { synchronized (this.queries) {
if (start > end) {
return null;
}
final Query query = new Query(conversation, start, end,PagingOrder.REVERSE); final Query query = new Query(conversation, start, end,PagingOrder.REVERSE);
if (start==0) { if (start==0) {
query.reference = conversation.getFirstMamReference(); query.reference = conversation.getFirstMamReference();
Log.d(Config.LOGTAG,"setting mam reference");
}
Log.d(Config.LOGTAG,"checking max of "+start+" end "+mXmppConnectionService.getAutomaticMessageDeletionDate());
query.start = Math.max(start,mXmppConnectionService.getAutomaticMessageDeletionDate());
if (start > end) {
return null;
} }
this.queries.add(query); this.queries.add(query);
this.execute(query); this.execute(query);
@ -222,7 +226,7 @@ public class MessageArchiveService implements OnAdvancedStreamFeaturesLoaded {
query.getConversation().setFirstMamReference(first == null ? null : first.getContent()); query.getConversation().setFirstMamReference(first == null ? null : first.getContent());
} }
if (complete || relevant == null || abort) { if (complete || relevant == null || abort) {
final boolean done = (complete || query.getMessageCount() == 0) && query.getStart() == 0; final boolean done = (complete || query.getMessageCount() == 0) && query.getStart() <= mXmppConnectionService.getAutomaticMessageDeletionDate();
this.finalizeQuery(query, done); this.finalizeQuery(query, done);
Log.d(Config.LOGTAG,query.getAccount().getJid().toBareJid()+": finished mam after "+query.getTotalCount()+" messages. messages left="+Boolean.toString(!done)); Log.d(Config.LOGTAG,query.getAccount().getJid().toBareJid()+": finished mam after "+query.getTotalCount()+" messages. messages left="+Boolean.toString(!done));
if (query.getWith() == null && query.getMessageCount() > 0) { if (query.getWith() == null && query.getMessageCount() > 0) {

View file

@ -59,6 +59,7 @@ import java.util.ListIterator;
import java.util.Locale; import java.util.Locale;
import java.util.Map; import java.util.Map;
import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicLong;
import de.duenndns.ssl.MemorizingTrustManager; import de.duenndns.ssl.MemorizingTrustManager;
import eu.siacs.conversations.Config; import eu.siacs.conversations.Config;
@ -265,6 +266,7 @@ public class XmppConnectionService extends Service {
private int mucRosterChangedListenerCount = 0; private int mucRosterChangedListenerCount = 0;
private OnKeyStatusUpdated mOnKeyStatusUpdated = null; private OnKeyStatusUpdated mOnKeyStatusUpdated = null;
private int keyStatusUpdatedListenerCount = 0; private int keyStatusUpdatedListenerCount = 0;
private AtomicLong mLastExpiryRun = new AtomicLong(0);
private SecureRandom mRandom; private SecureRandom mRandom;
private LruCache<Pair<String,String>,ServiceDiscoveryResult> discoCache = new LruCache<>(20); private LruCache<Pair<String,String>,ServiceDiscoveryResult> discoCache = new LruCache<>(20);
private final OnBindListener mOnBindListener = new OnBindListener() { private final OnBindListener mOnBindListener = new OnBindListener() {
@ -645,6 +647,9 @@ public class XmppConnectionService extends Service {
} }
} }
} }
if (SystemClock.elapsedRealtime() - mLastExpiryRun.get() >= Config.EXPIRY_INTERVAL) {
expireOldMessages();
}
return START_STICKY; return START_STICKY;
} }
@ -854,6 +859,20 @@ public class XmppConnectionService extends Service {
} }
} }
private void expireOldMessages() {
mLastExpiryRun.set(SystemClock.elapsedRealtime());
synchronized (this.conversations) {
long timestamp = getAutomaticMessageDeletionDate();
if (timestamp > 0) {
databaseBackend.expireOldMessages(timestamp);
for (Conversation conversation : this.conversations) {
conversation.expireOldMessages(timestamp);
}
updateConversationUi();
}
}
}
public boolean hasInternetConnection() { public boolean hasInternetConnection() {
ConnectivityManager cm = (ConnectivityManager) getApplicationContext() ConnectivityManager cm = (ConnectivityManager) getApplicationContext()
.getSystemService(Context.CONNECTIVITY_SERVICE); .getSystemService(Context.CONNECTIVITY_SERVICE);
@ -1232,13 +1251,11 @@ public class XmppConnectionService extends Service {
if (addToConversation) { if (addToConversation) {
conversation.add(message); conversation.add(message);
} }
if (message.getEncryption() == Message.ENCRYPTION_NONE || saveEncryptedMessages()) {
if (saveInDb) { if (saveInDb) {
databaseBackend.createMessage(message); databaseBackend.createMessage(message);
} else if (message.edited()) { } else if (message.edited()) {
databaseBackend.updateMessage(message, message.getEditedId()); databaseBackend.updateMessage(message, message.getEditedId());
} }
}
updateConversationUi(); updateConversationUi();
} }
if (packet != null) { if (packet != null) {
@ -1347,6 +1364,12 @@ public class XmppConnectionService extends Service {
Runnable runnable = new Runnable() { Runnable runnable = new Runnable() {
@Override @Override
public void run() { public void run() {
long deletionDate = getAutomaticMessageDeletionDate();
mLastExpiryRun.set(SystemClock.elapsedRealtime());
if (deletionDate > 0) {
Log.d(Config.LOGTAG, "deleting messages that are older than "+AbstractGenerator.getTimestamp(deletionDate));
databaseBackend.expireOldMessages(deletionDate);
}
Log.d(Config.LOGTAG, "restoring roster"); Log.d(Config.LOGTAG, "restoring roster");
for (Account account : accounts) { for (Account account : accounts) {
databaseBackend.readRoster(account.getRoster()); databaseBackend.readRoster(account.getRoster());
@ -1518,8 +1541,11 @@ public class XmppConnectionService extends Service {
MessageArchiveService.Query query = getMessageArchiveService().query(conversation, 0, timestamp); MessageArchiveService.Query query = getMessageArchiveService().query(conversation, 0, timestamp);
if (query != null) { if (query != null) {
query.setCallback(callback); query.setCallback(callback);
}
callback.informUser(R.string.fetching_history_from_server); callback.informUser(R.string.fetching_history_from_server);
} else {
callback.informUser(R.string.not_fetching_history_retention_period);
}
} }
} }
} }
@ -3046,6 +3072,15 @@ public class XmppConnectionService extends Service {
.getDefaultSharedPreferences(getApplicationContext()); .getDefaultSharedPreferences(getApplicationContext());
} }
public long getAutomaticMessageDeletionDate() {
try {
final long timeout = Long.parseLong(getPreferences().getString(SettingsActivity.AUTOMATIC_MESSAGE_DELETION, "0")) * 1000;
return timeout == 0 ? timeout : System.currentTimeMillis() - timeout;
} catch (NumberFormatException e) {
return 0;
}
}
public boolean confirmMessages() { public boolean confirmMessages() {
return getPreferences().getBoolean("confirm_messages", true); return getPreferences().getBoolean("confirm_messages", true);
} }
@ -3058,10 +3093,6 @@ public class XmppConnectionService extends Service {
return getPreferences().getBoolean("chat_states", false); return getPreferences().getBoolean("chat_states", false);
} }
public boolean saveEncryptedMessages() {
return !getPreferences().getBoolean("dont_save_encrypted", false);
}
private boolean respectAutojoin() { private boolean respectAutojoin() {
return getPreferences().getBoolean("autojoin", true); return getPreferences().getBoolean("autojoin", true);
} }

View file

@ -44,6 +44,7 @@ public class SettingsActivity extends XmppActivity implements
public static final String TREAT_VIBRATE_AS_SILENT = "treat_vibrate_as_silent"; public static final String TREAT_VIBRATE_AS_SILENT = "treat_vibrate_as_silent";
public static final String MANUALLY_CHANGE_PRESENCE = "manually_change_presence"; public static final String MANUALLY_CHANGE_PRESENCE = "manually_change_presence";
public static final String BLIND_TRUST_BEFORE_VERIFICATION = "btbv"; public static final String BLIND_TRUST_BEFORE_VERIFICATION = "btbv";
public static final String AUTOMATIC_MESSAGE_DELETION = "automatic_message_deletion";
public static final int REQUEST_WRITE_LOGS = 0xbf8701; public static final int REQUEST_WRITE_LOGS = 0xbf8701;
private SettingsFragment mSettingsFragment; private SettingsFragment mSettingsFragment;

View file

@ -52,6 +52,7 @@ import eu.siacs.conversations.entities.Message;
import eu.siacs.conversations.entities.Message.FileParams; import eu.siacs.conversations.entities.Message.FileParams;
import eu.siacs.conversations.entities.Transferable; import eu.siacs.conversations.entities.Transferable;
import eu.siacs.conversations.persistance.FileBackend; import eu.siacs.conversations.persistance.FileBackend;
import eu.siacs.conversations.services.MessageArchiveService;
import eu.siacs.conversations.services.NotificationService; import eu.siacs.conversations.services.NotificationService;
import eu.siacs.conversations.ui.ConversationActivity; import eu.siacs.conversations.ui.ConversationActivity;
import eu.siacs.conversations.ui.text.DividerSpan; import eu.siacs.conversations.ui.text.DividerSpan;
@ -565,8 +566,12 @@ public class MessageAdapter extends ArrayAdapter<Message> implements CopyTextVie
timestamp = System.currentTimeMillis(); timestamp = System.currentTimeMillis();
} }
activity.setMessagesLoaded(); activity.setMessagesLoaded();
activity.xmppConnectionService.getMessageArchiveService().query(conversation, 0, timestamp); MessageArchiveService.Query query = activity.xmppConnectionService.getMessageArchiveService().query(conversation, 0, timestamp);
if (query != null) {
Toast.makeText(activity, R.string.fetching_history_from_server, Toast.LENGTH_LONG).show(); Toast.makeText(activity, R.string.fetching_history_from_server, Toast.LENGTH_LONG).show();
} else {
Toast.makeText(activity,R.string.not_fetching_history_retention_period, Toast.LENGTH_SHORT).show();
}
} }
@Override @Override

View file

@ -103,4 +103,18 @@
<item>610</item> <item>610</item>
<item>2584</item> <item>2584</item>
</string-array> </string-array>
<string-array name="automatic_message_deletion_values">
<item>0</item>
<item>86400</item>
<item>604800</item>
<item>2592000</item>
<item>15811200</item>
</string-array>
<string-array name="automatic_message_deletion">
<item>@string/never</item>
<item>@string/timeout_24_hours</item>
<item>@string/timeout_7_days</item>
<item>@string/timeout_30_days</item>
<item>@string/timeout_6_months</item>
</string-array>
</resources> </resources>

View file

@ -724,5 +724,12 @@
<string name="hide_inactive_devices">Hide inactive devices</string> <string name="hide_inactive_devices">Hide inactive devices</string>
<string name="distrust_omemo_key">Distrust device</string> <string name="distrust_omemo_key">Distrust device</string>
<string name="distrust_omemo_key_text">Are you sure you want to remove the verification for this device?\nThis device and messages coming from that device will be marked as untrusted.</string> <string name="distrust_omemo_key_text">Are you sure you want to remove the verification for this device?\nThis device and messages coming from that device will be marked as untrusted.</string>
<string name="timeout_24_hours">24 hours</string>
<string name="timeout_7_days">7 days</string>
<string name="timeout_30_days">30 days</string>
<string name="timeout_6_months">6 months</string>
<string name="pref_automatically_delete_messages">Automatic message deletion</string>
<string name="pref_automatically_delete_messages_description">Automatically delete messages from this device that are older than the configured time frame.</string>
<string name="encrypting_message">Encrypting message</string> <string name="encrypting_message">Encrypting message</string>
<string name="not_fetching_history_retention_period">Overstepping local retention period.</string>
</resources> </resources>

View file

@ -170,11 +170,13 @@
android:key="btbv" android:key="btbv"
android:title="@string/pref_blind_trust_before_verification" android:title="@string/pref_blind_trust_before_verification"
android:summary="@string/pref_blind_trust_before_verification_summary"/> android:summary="@string/pref_blind_trust_before_verification_summary"/>
<CheckBoxPreference <ListPreference
android:defaultValue="false" android:key="automatic_message_deletion"
android:key="dont_save_encrypted" android:title="@string/pref_automatically_delete_messages"
android:summary="@string/pref_dont_save_encrypted_summary" android:summary="@string/pref_automatically_delete_messages_description"
android:title="@string/pref_dont_save_encrypted"/> android:defaultValue="0"
android:entries="@array/automatic_message_deletion"
android:entryValues="@array/automatic_message_deletion_values" />
<CheckBoxPreference <CheckBoxPreference
android:defaultValue="false" android:defaultValue="false"
android:key="dont_trust_system_cas" android:key="dont_trust_system_cas"