From 9b6ae6d75fdf07933427774f72310ceecc5f01bd Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Mon, 23 Jan 2017 17:14:30 +0100 Subject: configurable local message retention period. (untested) --- src/main/java/eu/siacs/conversations/Config.java | 2 + .../siacs/conversations/entities/Conversation.java | 12 +++++ .../siacs/conversations/parser/MessageParser.java | 10 ++-- .../conversations/persistance/DatabaseBackend.java | 7 +++ .../services/MessageArchiveService.java | 12 +++-- .../services/XmppConnectionService.java | 53 +++++++++++++++++----- .../siacs/conversations/ui/SettingsActivity.java | 1 + .../conversations/ui/adapter/MessageAdapter.java | 9 +++- src/main/res/values/arrays.xml | 14 ++++++ src/main/res/values/strings.xml | 7 +++ src/main/res/xml/preferences.xml | 12 +++-- 11 files changed, 114 insertions(+), 25 deletions(-) diff --git a/src/main/java/eu/siacs/conversations/Config.java b/src/main/java/eu/siacs/conversations/Config.java index 48d2620a..d9ecac5d 100644 --- a/src/main/java/eu/siacs/conversations/Config.java +++ b/src/main/java/eu/siacs/conversations/Config.java @@ -114,6 +114,8 @@ public final class Config { public static final ChatState DEFAULT_CHATSTATE = ChatState.ACTIVE; public static final int TYPING_TIMEOUT = 8; + public static final int EXPIRY_INTERVAL = 30 * 60 * 1000; // 30 minutes + public static final String ENABLED_CIPHERS[] = { "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA384", diff --git a/src/main/java/eu/siacs/conversations/entities/Conversation.java b/src/main/java/eu/siacs/conversations/entities/Conversation.java index fa74c418..c686a676 100644 --- a/src/main/java/eu/siacs/conversations/entities/Conversation.java +++ b/src/main/java/eu/siacs/conversations/entities/Conversation.java @@ -20,6 +20,7 @@ import java.util.Collections; import java.util.Comparator; import java.util.Iterator; import java.util.List; +import java.util.ListIterator; import java.util.Locale; import eu.siacs.conversations.Config; @@ -930,6 +931,17 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl account.getPgpDecryptionService().decrypt(messages); } + public void expireOldMessages(long timestamp) { + synchronized (this.messages) { + for(ListIterator iterator = this.messages.listIterator(); iterator.hasNext();) { + if (iterator.next().getTimeSent() < timestamp) { + iterator.remove(); + } + } + untieMessages(); + } + } + public void sort() { synchronized (this.messages) { Collections.sort(this.messages, new Comparator() { diff --git a/src/main/java/eu/siacs/conversations/parser/MessageParser.java b/src/main/java/eu/siacs/conversations/parser/MessageParser.java index 9a52ff80..86ed2836 100644 --- a/src/main/java/eu/siacs/conversations/parser/MessageParser.java +++ b/src/main/java/eu/siacs/conversations/parser/MessageParser.java @@ -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 || (isTypeGroupChat && packet.hasChild("delay","urn:xmpp:delay")) || message.getType() == Message.TYPE_PRIVATE; @@ -570,9 +576,7 @@ public class MessageParser extends AbstractParser implements OnMessagePacketRece conversation.endOtrIfNeeded(); } - if (message.getEncryption() == Message.ENCRYPTION_NONE || mXmppConnectionService.saveEncryptedMessages()) { - mXmppConnectionService.databaseBackend.createMessage(message); - } + mXmppConnectionService.databaseBackend.createMessage(message); final HttpConnectionManager manager = this.mXmppConnectionService.getHttpConnectionManager(); if (message.trusted() && message.treatAsDownloadable() != Message.Decision.NEVER && manager.getAutoAcceptFileSize() > 0) { manager.createNewDownloadConnection(message); diff --git a/src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java b/src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java index 63d5782b..4d5c4cb7 100644 --- a/src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java +++ b/src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java @@ -769,6 +769,13 @@ public class DatabaseBackend extends SQLiteOpenHelper { db.delete(Message.TABLENAME, Message.CONVERSATION + "=?", args); } + public boolean expireOldMessages(long timestamp) { + String where = Message.TIME_SENT+" 0; + } + public Pair getLastMessageReceived(Account account) { Cursor cursor = null; try { diff --git a/src/main/java/eu/siacs/conversations/services/MessageArchiveService.java b/src/main/java/eu/siacs/conversations/services/MessageArchiveService.java index a352ea8a..92ba7591 100644 --- a/src/main/java/eu/siacs/conversations/services/MessageArchiveService.java +++ b/src/main/java/eu/siacs/conversations/services/MessageArchiveService.java @@ -56,6 +56,7 @@ public class MessageArchiveService implements OnAdvancedStreamFeaturesLoaded { startCatchup = lastClearDate.first; reference = null; } + startCatchup = Math.max(startCatchup,mXmppConnectionService.getAutomaticMessageDeletionDate()); long endCatchup = account.getXmppConnection().getLastSessionEstablished(); final Query query; if (startCatchup == 0) { @@ -107,12 +108,15 @@ public class MessageArchiveService implements OnAdvancedStreamFeaturesLoaded { public Query query(Conversation conversation, long start, long end) { synchronized (this.queries) { - if (start > end) { - return null; - } final Query query = new Query(conversation, start, end,PagingOrder.REVERSE); if (start==0) { 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.execute(query); @@ -222,7 +226,7 @@ public class MessageArchiveService implements OnAdvancedStreamFeaturesLoaded { query.getConversation().setFirstMamReference(first == null ? null : first.getContent()); } 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); 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) { diff --git a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java index 5f949821..537bb55e 100644 --- a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java +++ b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java @@ -59,6 +59,7 @@ import java.util.ListIterator; import java.util.Locale; import java.util.Map; import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.atomic.AtomicLong; import de.duenndns.ssl.MemorizingTrustManager; import eu.siacs.conversations.Config; @@ -265,6 +266,7 @@ public class XmppConnectionService extends Service { private int mucRosterChangedListenerCount = 0; private OnKeyStatusUpdated mOnKeyStatusUpdated = null; private int keyStatusUpdatedListenerCount = 0; + private AtomicLong mLastExpiryRun = new AtomicLong(0); private SecureRandom mRandom; private LruCache,ServiceDiscoveryResult> discoCache = new LruCache<>(20); 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; } @@ -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() { ConnectivityManager cm = (ConnectivityManager) getApplicationContext() .getSystemService(Context.CONNECTIVITY_SERVICE); @@ -1232,12 +1251,10 @@ public class XmppConnectionService extends Service { if (addToConversation) { conversation.add(message); } - if (message.getEncryption() == Message.ENCRYPTION_NONE || saveEncryptedMessages()) { - if (saveInDb) { - databaseBackend.createMessage(message); - } else if (message.edited()) { - databaseBackend.updateMessage(message, message.getEditedId()); - } + if (saveInDb) { + databaseBackend.createMessage(message); + } else if (message.edited()) { + databaseBackend.updateMessage(message, message.getEditedId()); } updateConversationUi(); } @@ -1347,6 +1364,12 @@ public class XmppConnectionService extends Service { Runnable runnable = new Runnable() { @Override 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"); for (Account account : accounts) { databaseBackend.readRoster(account.getRoster()); @@ -1518,8 +1541,11 @@ public class XmppConnectionService extends Service { MessageArchiveService.Query query = getMessageArchiveService().query(conversation, 0, timestamp); if (query != null) { query.setCallback(callback); + callback.informUser(R.string.fetching_history_from_server); + } else { + callback.informUser(R.string.not_fetching_history_retention_period); } - callback.informUser(R.string.fetching_history_from_server); + } } } @@ -3046,6 +3072,15 @@ public class XmppConnectionService extends Service { .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() { return getPreferences().getBoolean("confirm_messages", true); } @@ -3058,10 +3093,6 @@ public class XmppConnectionService extends Service { return getPreferences().getBoolean("chat_states", false); } - public boolean saveEncryptedMessages() { - return !getPreferences().getBoolean("dont_save_encrypted", false); - } - private boolean respectAutojoin() { return getPreferences().getBoolean("autojoin", true); } diff --git a/src/main/java/eu/siacs/conversations/ui/SettingsActivity.java b/src/main/java/eu/siacs/conversations/ui/SettingsActivity.java index 9a4e9f8e..4126bb3a 100644 --- a/src/main/java/eu/siacs/conversations/ui/SettingsActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/SettingsActivity.java @@ -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 MANUALLY_CHANGE_PRESENCE = "manually_change_presence"; 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; private SettingsFragment mSettingsFragment; diff --git a/src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java b/src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java index e8b3459a..4f58059e 100644 --- a/src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java +++ b/src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java @@ -52,6 +52,7 @@ import eu.siacs.conversations.entities.Message; import eu.siacs.conversations.entities.Message.FileParams; import eu.siacs.conversations.entities.Transferable; import eu.siacs.conversations.persistance.FileBackend; +import eu.siacs.conversations.services.MessageArchiveService; import eu.siacs.conversations.services.NotificationService; import eu.siacs.conversations.ui.ConversationActivity; import eu.siacs.conversations.ui.text.DividerSpan; @@ -565,8 +566,12 @@ public class MessageAdapter extends ArrayAdapter implements CopyTextVie timestamp = System.currentTimeMillis(); } activity.setMessagesLoaded(); - activity.xmppConnectionService.getMessageArchiveService().query(conversation, 0, timestamp); - Toast.makeText(activity, R.string.fetching_history_from_server,Toast.LENGTH_LONG).show(); + 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(); + } else { + Toast.makeText(activity,R.string.not_fetching_history_retention_period, Toast.LENGTH_SHORT).show(); + } } @Override diff --git a/src/main/res/values/arrays.xml b/src/main/res/values/arrays.xml index 0695d1cd..3ce79dee 100644 --- a/src/main/res/values/arrays.xml +++ b/src/main/res/values/arrays.xml @@ -103,4 +103,18 @@ 610 2584 + + 0 + 86400 + 604800 + 2592000 + 15811200 + + + @string/never + @string/timeout_24_hours + @string/timeout_7_days + @string/timeout_30_days + @string/timeout_6_months + diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml index 9ad0d63d..b56ec72c 100644 --- a/src/main/res/values/strings.xml +++ b/src/main/res/values/strings.xml @@ -724,5 +724,12 @@ Hide inactive devices Distrust device 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. + 24 hours + 7 days + 30 days + 6 months + Automatic message deletion + Automatically delete messages from this device that are older than the configured time frame. Encrypting message + Overstepping local retention period. diff --git a/src/main/res/xml/preferences.xml b/src/main/res/xml/preferences.xml index 751f3bec..31014d85 100644 --- a/src/main/res/xml/preferences.xml +++ b/src/main/res/xml/preferences.xml @@ -170,11 +170,13 @@ android:key="btbv" android:title="@string/pref_blind_trust_before_verification" android:summary="@string/pref_blind_trust_before_verification_summary"/> - +