diff --git a/src/main/java/eu/siacs/conversations/entities/Message.java b/src/main/java/eu/siacs/conversations/entities/Message.java index 3766d4b3d..51a50dec9 100644 --- a/src/main/java/eu/siacs/conversations/entities/Message.java +++ b/src/main/java/eu/siacs/conversations/entities/Message.java @@ -132,6 +132,13 @@ public class Message extends AbstractEntity implements AvatarService.Avatarable public static final String OCCUPANT_ID = "occupantId"; public static final String REACTIONS = "reactions"; public static final String ME_COMMAND = "/me "; + public static final String PAYLOADS = "payloads"; + public static final String TIME_RECEIVED = "timeReceived"; + public static final String SUBJECT = "subject"; + public static final String FILE_PARAMS = "fileParams"; + public static final String OCCUPANTID = "occupant_id"; + public static final String NOTIFICATION_DISMISSED = "notificationDismissed"; + public static final String ERROR_MESSAGE_CANCELLED = "eu.siacs.conversations.cancelled"; @@ -288,7 +295,7 @@ public class Message extends AbstractEntity implements AvatarService.Avatarable } public static Message fromCursor(Cursor cursor, Conversation conversation) throws IOException { - String payloadsStr = cursor.getString(cursor.getColumnIndex("payloads")); + String payloadsStr = cursor.getString(cursor.getColumnIndexOrThrow(PAYLOADS)); List payloads = new ArrayList<>(); if (payloadsStr != null) { final XmlReader xmlReader = new XmlReader(); @@ -302,40 +309,39 @@ public class Message extends AbstractEntity implements AvatarService.Avatarable Log.e(Config.LOGTAG, "Failed to parse: " + payloadsStr, e); } } - Message m = new Message(conversation, - cursor.getString(cursor.getColumnIndex(UUID)), - cursor.getString(cursor.getColumnIndex(CONVERSATION)), - fromString(cursor.getString(cursor.getColumnIndex(COUNTERPART))), - fromString(cursor.getString(cursor.getColumnIndex(TRUE_COUNTERPART))), - cursor.getString(cursor.getColumnIndex(BODY)), - cursor.getLong(cursor.getColumnIndex(TIME_SENT)), - cursor.getInt(cursor.getColumnIndex(ENCRYPTION)), - cursor.getInt(cursor.getColumnIndex(STATUS)), - cursor.getInt(cursor.getColumnIndex(TYPE)), - cursor.getInt(cursor.getColumnIndex(CARBON)) > 0, - cursor.getString(cursor.getColumnIndex(REMOTE_MSG_ID)), - cursor.getString(cursor.getColumnIndex(RELATIVE_FILE_PATH)), - cursor.getString(cursor.getColumnIndex(SERVER_MSG_ID)), - cursor.getString(cursor.getColumnIndex(FINGERPRINT)), - cursor.getInt(cursor.getColumnIndex(READ)) > 0, - cursor.getString(cursor.getColumnIndex(EDITED)), - cursor.getInt(cursor.getColumnIndex(OOB)) > 0, - cursor.getString(cursor.getColumnIndex(ERROR_MESSAGE)), - ReadByMarker.fromJsonString(cursor.getString(cursor.getColumnIndex(READ_BY_MARKERS))), - cursor.getInt(cursor.getColumnIndex(MARKABLE)) > 0, - cursor.getInt(cursor.getColumnIndex(DELETED)) > 0, - cursor.getString(cursor.getColumnIndex(BODY_LANGUAGE)), + cursor.getString(cursor.getColumnIndexOrThrow(UUID)), + cursor.getString(cursor.getColumnIndexOrThrow(CONVERSATION)), + fromString(cursor.getString(cursor.getColumnIndexOrThrow(COUNTERPART))), + fromString(cursor.getString(cursor.getColumnIndexOrThrow(TRUE_COUNTERPART))), + cursor.getString(cursor.getColumnIndexOrThrow(BODY)), + cursor.getLong(cursor.getColumnIndexOrThrow(TIME_SENT)), + cursor.getInt(cursor.getColumnIndexOrThrow(ENCRYPTION)), + cursor.getInt(cursor.getColumnIndexOrThrow(STATUS)), + cursor.getInt(cursor.getColumnIndexOrThrow(TYPE)), + cursor.getInt(cursor.getColumnIndexOrThrow(CARBON)) > 0, + cursor.getString(cursor.getColumnIndexOrThrow(REMOTE_MSG_ID)), + cursor.getString(cursor.getColumnIndexOrThrow(RELATIVE_FILE_PATH)), + cursor.getString(cursor.getColumnIndexOrThrow(SERVER_MSG_ID)), + cursor.getString(cursor.getColumnIndexOrThrow(FINGERPRINT)), + cursor.getInt(cursor.getColumnIndexOrThrow(READ)) > 0, + cursor.getString(cursor.getColumnIndexOrThrow(EDITED)), + cursor.getInt(cursor.getColumnIndexOrThrow(OOB)) > 0, + cursor.getString(cursor.getColumnIndexOrThrow(ERROR_MESSAGE)), + ReadByMarker.fromJsonString(cursor.getString(cursor.getColumnIndexOrThrow(READ_BY_MARKERS))), + cursor.getInt(cursor.getColumnIndexOrThrow(MARKABLE)) > 0, + cursor.getInt(cursor.getColumnIndexOrThrow(DELETED)) > 0, + cursor.getString(cursor.getColumnIndexOrThrow(BODY_LANGUAGE)), cursor.getString(cursor.getColumnIndexOrThrow(OCCUPANT_ID)), Reaction.fromString(cursor.getString(cursor.getColumnIndexOrThrow(REACTIONS))), - cursor.getLong(cursor.getColumnIndex(cursor.isNull(cursor.getColumnIndex("timeReceived")) ? TIME_SENT : "timeReceived")), - cursor.getString(cursor.getColumnIndex("subject")), - cursor.getString(cursor.getColumnIndex("fileParams")), + cursor.getLong(cursor.getColumnIndexOrThrow(cursor.isNull(cursor.getColumnIndexOrThrow(TIME_RECEIVED)) ? TIME_SENT : TIME_RECEIVED)), + cursor.getString(cursor.getColumnIndexOrThrow(SUBJECT)), + cursor.getString(cursor.getColumnIndexOrThrow(FILE_PARAMS)), payloads ); - final var legacyOccupant = cursor.getString(cursor.getColumnIndex("occupant_id")); + final var legacyOccupant = cursor.getString(cursor.getColumnIndexOrThrow(OCCUPANTID)); if (legacyOccupant != null) m.setOccupantId(legacyOccupant); - if (cursor.getInt(cursor.getColumnIndex("notificationDismissed")) > 0) m.markNotificationDismissed(); + if (cursor.getInt(cursor.getColumnIndexOrThrow(NOTIFICATION_DISMISSED)) > 0) m.markNotificationDismissed(); return m; } @@ -365,29 +371,9 @@ public class Message extends AbstractEntity implements AvatarService.Avatarable return message; } - public ContentValues getmonoclesContentValues() { - final FileParams fp = fileParams; - ContentValues values = new ContentValues(); - values.put(UUID, uuid); - values.put("subject", subject); - values.put("fileParams", fp == null ? null : fp.toString()); - if (fp != null && !fp.isEmpty()) { - List sims = getSims(); - if (sims.isEmpty()) { - addPayload(fp.toSims()); - } else { - sims.get(0).replaceChildren(fp.toSims().getChildren()); - } - } - values.put("payloads", payloads.size() < 1 ? null : payloads.stream().map(Object::toString).collect(Collectors.joining())); - values.put("occupant_id", occupantId); - values.put("timeReceived", timeReceived); - values.put("notificationDismissed", notificationDismissed ? 1 : 0); - return values; - } - @Override public ContentValues getContentValues() { + final FileParams fp = fileParams; final var values = new ContentValues(); values.put(UUID, uuid); values.put(CONVERSATION, conversationUuid); @@ -425,6 +411,20 @@ public class Message extends AbstractEntity implements AvatarService.Avatarable values.put(BODY_LANGUAGE, bodyLanguage); values.put(OCCUPANT_ID, occupantId); values.put(REACTIONS, Reaction.toString(this.reactions)); + values.put(SUBJECT, subject); + values.put(FILE_PARAMS, fp == null ? null : fp.toString()); + if (fp != null && !fp.isEmpty()) { + List sims = getSims(); + if (sims.isEmpty()) { + addPayload(fp.toSims()); + } else { + sims.get(0).replaceChildren(fp.toSims().getChildren()); + } + } + values.put(PAYLOADS, payloads.size() < 1 ? null : payloads.stream().map(Object::toString).collect(Collectors.joining())); + values.put(OCCUPANTID, occupantId); + values.put(TIME_RECEIVED, timeReceived); + values.put(NOTIFICATION_DISMISSED, notificationDismissed ? 1 : 0); return values; } diff --git a/src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java b/src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java index 02f6fa7f8..83f7d0a8d 100644 --- a/src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java +++ b/src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java @@ -41,8 +41,10 @@ import java.util.Collection; import java.util.HashMap; import java.util.Hashtable; import java.util.HashSet; +import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.NoSuchElementException; import java.util.Set; import java.util.UUID; import java.util.concurrent.CopyOnWriteArrayList; @@ -76,7 +78,7 @@ import eu.siacs.conversations.xmpp.mam.MamReference; public class DatabaseBackend extends SQLiteOpenHelper { private static final String DATABASE_NAME = "history"; - private static final int DATABASE_VERSION = 60; + private static final int DATABASE_VERSION = 61; private static boolean requiresMessageIndexRebuild = false; private static DatabaseBackend instance = null; @@ -225,164 +227,10 @@ public class DatabaseBackend extends SQLiteOpenHelper { return instance; } - protected void monoclesMigrate(SQLiteDatabase db) { - db.beginTransaction(); - - try { - Cursor cursor = db.rawQuery("PRAGMA monocles.user_version", null); - cursor.moveToNext(); - int monoclesVersion = cursor.getInt(0); - cursor.close(); - - if(monoclesVersion < 1) { - // No cross-DB foreign keys unfortunately - db.execSQL( - "CREATE TABLE monocles." + Message.TABLENAME + "(" + - Message.UUID + " TEXT PRIMARY KEY, " + - "subject TEXT" + - ")" - ); - db.execSQL("PRAGMA monocles.user_version = 1"); - } - - if(monoclesVersion < 2) { - db.execSQL( - "ALTER TABLE monocles." + Message.TABLENAME + " " + - "ADD COLUMN oobUri TEXT" - ); - db.execSQL( - "ALTER TABLE monocles." + Message.TABLENAME + " " + - "ADD COLUMN fileParams TEXT" - ); - db.execSQL("PRAGMA monocles.user_version = 2"); - } - - if(monoclesVersion < 3) { - db.execSQL( - "ALTER TABLE monocles." + Message.TABLENAME + " " + - "ADD COLUMN payloads TEXT" - ); - db.execSQL("PRAGMA monocles.user_version = 3"); - } - - if(monoclesVersion < 4) { - db.execSQL( - "CREATE TABLE monocles.cids (" + - "cid TEXT NOT NULL PRIMARY KEY," + - "path TEXT NOT NULL" + - ")" - ); - db.execSQL("PRAGMA monocles.user_version = 4"); - } - - if(monoclesVersion < 5) { - db.execSQL( - "ALTER TABLE monocles." + Message.TABLENAME + " " + - "ADD COLUMN timeReceived NUMBER" - ); - db.execSQL("CREATE INDEX monocles.message_time_received_index ON " + Message.TABLENAME + " (timeReceived)"); - db.execSQL("PRAGMA monocles.user_version = 5"); - } - - if(monoclesVersion < 6) { - db.execSQL( - "CREATE TABLE monocles.blocked_media (" + - "cid TEXT NOT NULL PRIMARY KEY" + - ")" - ); - db.execSQL("PRAGMA monocles.user_version = 6"); - } - - if(monoclesVersion < 7) { - db.execSQL( - "ALTER TABLE monocles.cids " + - "ADD COLUMN url TEXT" - ); - db.execSQL("PRAGMA monocles.user_version = 7"); - } - - if(monoclesVersion < 8) { - db.execSQL( - "CREATE TABLE monocles.webxdc_updates (" + - "serial INTEGER PRIMARY KEY AUTOINCREMENT, " + - Message.CONVERSATION + " TEXT NOT NULL, " + - "sender TEXT NOT NULL, " + - "thread TEXT NOT NULL, " + - "threadParent TEXT, " + - "info TEXT, " + - "document TEXT, " + - "summary TEXT, " + - "payload TEXT" + - ")" - ); - db.execSQL("CREATE INDEX monocles.webxdc_index ON webxdc_updates (" + Message.CONVERSATION + ", thread)"); - db.execSQL("PRAGMA monocles.user_version = 8"); - } - - if(monoclesVersion < 9) { - db.execSQL( - "ALTER TABLE monocles.webxdc_updates " + - "ADD COLUMN message_id TEXT" - ); - db.execSQL("CREATE UNIQUE INDEX monocles.webxdc_message_id_index ON webxdc_updates (" + Message.CONVERSATION + ", message_id)"); - db.execSQL("PRAGMA monocles.user_version = 9"); - } - - if(monoclesVersion < 10) { - db.execSQL( - "CREATE TABLE monocles.muted_participants (" + - "muc_jid TEXT NOT NULL, " + - "occupant_id TEXT NOT NULL, " + - "nick TEXT NOT NULL," + - "PRIMARY KEY (muc_jid, occupant_id)" + - ")" - ); - db.execSQL( - "ALTER TABLE monocles." + Message.TABLENAME + " " + - "ADD COLUMN occupant_id TEXT" - ); - db.execSQL("PRAGMA monocles.user_version = 10"); - } - - if(monoclesVersion < 11) { - if (Build.VERSION.SDK_INT >= 34) { - db.execSQL( - "ALTER TABLE monocles.muted_participants " + - "DROP COLUMN nick" - ); - } else { - db.execSQL("DROP TABLE monocles.muted_participants"); - db.execSQL( - "CREATE TABLE monocles.muted_participants (" + - "muc_jid TEXT NOT NULL, " + - "occupant_id TEXT NOT NULL, " + - "PRIMARY KEY (muc_jid, occupant_id)" + - ")" - ); - } - db.execSQL("PRAGMA monocles.user_version = 11"); - } - - if(monoclesVersion < 12) { - db.execSQL( - "ALTER TABLE monocles." + Message.TABLENAME + " " + - "ADD COLUMN notificationDismissed NUMBER DEFAULT 0" - ); - db.execSQL("PRAGMA monocles.user_version = 12"); - } - - db.setTransactionSuccessful(); - } finally { - db.endTransaction(); - } - } - @Override public void onConfigure(SQLiteDatabase db) { db.execSQL("PRAGMA foreign_keys=ON"); db.rawQuery("PRAGMA secure_delete=ON", null).close(); - db.execSQL("ATTACH DATABASE ? AS monocles", new Object[]{context.getDatabasePath("monocles").getPath()}); - monoclesMigrate(db); } @Override @@ -777,7 +625,96 @@ public class DatabaseBackend extends SQLiteOpenHelper { db.execSQL("ALTER TABLE " + Message.TABLENAME + " ADD COLUMN " + Message.OCCUPANT_ID + " TEXT"); db.execSQL("ALTER TABLE " + Message.TABLENAME + " ADD COLUMN " + Message.REACTIONS + " TEXT"); } - } + if (oldVersion < 61 && newVersion >= 61) { + db.execSQL( + "ALTER TABLE " + Message.TABLENAME + " ADD COLUMN " + + Message.SUBJECT + " TEXT" + ); + db.execSQL( + "ALTER TABLE " + Message.TABLENAME + " " + + "ADD COLUMN oobUri TEXT" + ); + db.execSQL( + "ALTER TABLE " + Message.TABLENAME + " " + + "ADD COLUMN fileParams TEXT" + ); + db.execSQL( + "ALTER TABLE " + Message.TABLENAME + " " + + "ADD COLUMN payloads TEXT" + ); + db.execSQL( + "CREATE TABLE cids (" + + "cid TEXT NOT NULL PRIMARY KEY," + + "path TEXT NOT NULL" + + ")" + ); + db.execSQL( + "ALTER TABLE " + Message.TABLENAME + " " + + "ADD COLUMN timeReceived NUMBER" + ); + db.execSQL("CREATE INDEX message_time_received_index ON " + Message.TABLENAME + " (timeReceived)"); + db.execSQL( + "CREATE TABLE blocked_media (" + + "cid TEXT NOT NULL PRIMARY KEY" + + ")" + ); + db.execSQL( + "ALTER TABLE cids " + + "ADD COLUMN url TEXT" + ); + db.execSQL( + "CREATE TABLE webxdc_updates (" + + "serial INTEGER PRIMARY KEY AUTOINCREMENT, " + + Message.CONVERSATION + " TEXT NOT NULL, " + + "sender TEXT NOT NULL, " + + "thread TEXT NOT NULL, " + + "threadParent TEXT, " + + "info TEXT, " + + "document TEXT, " + + "summary TEXT, " + + "payload TEXT" + + ")" + ); + db.execSQL("CREATE INDEX webxdc_index ON webxdc_updates (" + Message.CONVERSATION + ", thread)"); + db.execSQL( + "ALTER TABLE webxdc_updates " + + "ADD COLUMN message_id TEXT" + ); + db.execSQL("CREATE UNIQUE INDEX webxdc_message_id_index ON webxdc_updates (" + Message.CONVERSATION + ", message_id)"); + db.execSQL( + "CREATE TABLE muted_participants (" + + "muc_jid TEXT NOT NULL, " + + "occupant_id TEXT NOT NULL, " + + "nick TEXT NOT NULL," + + "PRIMARY KEY (muc_jid, occupant_id)" + + ")" + ); + db.execSQL( + "ALTER TABLE " + Message.TABLENAME + " " + + "ADD COLUMN occupant_id TEXT" + ); + if (Build.VERSION.SDK_INT >= 34) { + db.execSQL( + "ALTER TABLE muted_participants " + + "DROP COLUMN nick" + ); + } else { + db.execSQL("DROP TABLE muted_participants"); + db.execSQL( + "CREATE TABLE muted_participants (" + + "muc_jid TEXT NOT NULL, " + + "occupant_id TEXT NOT NULL, " + + "PRIMARY KEY (muc_jid, occupant_id)" + + ")" + ); + } + db.execSQL( + "ALTER TABLE " + Message.TABLENAME + " " + + "ADD COLUMN notificationDismissed NUMBER DEFAULT 0" + ); + requiresMessageIndexRebuild = true; + } + } private void canonicalizeJids(SQLiteDatabase db) { // migrate db to new, canonicalized JID domainpart representation @@ -862,7 +799,7 @@ public class DatabaseBackend extends SQLiteOpenHelper { if (cid == null) return null; SQLiteDatabase db = this.getReadableDatabase(); - Cursor cursor = db.query("monocles.cids", new String[]{"path"}, "cid=?", new String[]{cid.toString()}, null, null, null); + Cursor cursor = db.query("cids", new String[]{"path"}, "cid=?", new String[]{cid.toString()}, null, null, null); DownloadableFile f = null; if (cursor.moveToNext()) { f = new DownloadableFile(cursor.getString(0)); @@ -873,7 +810,7 @@ public class DatabaseBackend extends SQLiteOpenHelper { public String getUrlForCid(Cid cid) { SQLiteDatabase db = this.getReadableDatabase(); - Cursor cursor = db.query("monocles.cids", new String[]{"url"}, "cid=?", new String[]{cid.toString()}, null, null, null); + Cursor cursor = db.query("cids", new String[]{"url"}, "cid=?", new String[]{cid.toString()}, null, null, null); String url = null; if (cursor.moveToNext()) { url = cursor.getString(0); @@ -892,8 +829,8 @@ public class DatabaseBackend extends SQLiteOpenHelper { cv.put("cid", cid.toString()); if (file != null) cv.put("path", file.getAbsolutePath()); if (url != null) cv.put("url", url); - if (db.update("monocles.cids", cv, "cid=?", new String[]{cid.toString()}) < 1) { - db.insertWithOnConflict("monocles.cids", null, cv, SQLiteDatabase.CONFLICT_REPLACE); + if (db.update("cids", cv, "cid=?", new String[]{cid.toString()}) < 1) { + db.insertWithOnConflict("cids", null, cv, SQLiteDatabase.CONFLICT_REPLACE); } } @@ -901,12 +838,12 @@ public class DatabaseBackend extends SQLiteOpenHelper { SQLiteDatabase db = this.getWritableDatabase(); ContentValues cv = new ContentValues(); cv.put("cid", cid.toString()); - db.insertWithOnConflict("monocles.blocked_media", null, cv, SQLiteDatabase.CONFLICT_REPLACE); + db.insertWithOnConflict("blocked_media", null, cv, SQLiteDatabase.CONFLICT_REPLACE); } public boolean isBlockedMedia(Cid cid) { SQLiteDatabase db = this.getReadableDatabase(); - Cursor cursor = db.query("monocles.blocked_media", new String[]{"count(*)"}, "cid=?", new String[]{cid.toString()}, null, null, null); + Cursor cursor = db.query("blocked_media", new String[]{"count(*)"}, "cid=?", new String[]{cid.toString()}, null, null, null); boolean is = false; if (cursor.moveToNext()) { is = cursor.getInt(0) > 0; @@ -917,13 +854,13 @@ public class DatabaseBackend extends SQLiteOpenHelper { public void clearBlockedMedia() { SQLiteDatabase db = this.getWritableDatabase(); - db.execSQL("DELETE FROM monocles.blocked_media"); + db.execSQL("DELETE FROM blocked_media"); } public Multimap loadMutedMucUsers() { Multimap result = HashMultimap.create(); SQLiteDatabase db = this.getReadableDatabase(); - Cursor cursor = db.query("monocles.muted_participants", new String[]{"muc_jid", "occupant_id"}, null, null, null, null, null); + Cursor cursor = db.query("muted_participants", new String[]{"muc_jid", "occupant_id"}, null, null, null, null, null); while (cursor.moveToNext()) { result.put(cursor.getString(0), cursor.getString(1)); } @@ -938,7 +875,7 @@ public class DatabaseBackend extends SQLiteOpenHelper { ContentValues cv = new ContentValues(); cv.put("muc_jid", user.getMuc().toString()); cv.put("occupant_id", user.getOccupantId()); - db.insertWithOnConflict("monocles.muted_participants", null, cv, SQLiteDatabase.CONFLICT_REPLACE); + db.insertWithOnConflict("muted_participants", null, cv, SQLiteDatabase.CONFLICT_REPLACE); return true; } @@ -949,14 +886,14 @@ public class DatabaseBackend extends SQLiteOpenHelper { SQLiteDatabase db = this.getWritableDatabase(); String where = "muc_jid=? AND occupant_id=?"; String[] whereArgs = {user.getMuc().toString(), user.getOccupantId()}; - db.delete("monocles.muted_participants", where, whereArgs); + db.delete("muted_participants", where, whereArgs); return true; } public void insertWebxdcUpdate(final WebxdcUpdate update) { SQLiteDatabase db = this.getWritableDatabase(); - db.insertWithOnConflict("monocles.webxdc_updates", null, update.getContentValues(), SQLiteDatabase.CONFLICT_IGNORE); + db.insertWithOnConflict("webxdc_updates", null, update.getContentValues(), SQLiteDatabase.CONFLICT_IGNORE); } public WebxdcUpdate findLastWebxdcUpdate(Message message) { @@ -967,7 +904,7 @@ public class DatabaseBackend extends SQLiteOpenHelper { SQLiteDatabase db = this.getReadableDatabase(); String[] selectionArgs = {message.getConversation().getUuid(), message.getThread().getContent()}; - Cursor cursor = db.query("monocles.webxdc_updates", null, + Cursor cursor = db.query("webxdc_updates", null, Message.CONVERSATION + "=? AND thread=?", selectionArgs, null, null, "serial ASC"); WebxdcUpdate update = null; @@ -981,7 +918,7 @@ public class DatabaseBackend extends SQLiteOpenHelper { public List findWebxdcUpdates(Message message, long serial) { SQLiteDatabase db = this.getReadableDatabase(); String[] selectionArgs = {message.getConversation().getUuid(), message.getThread().getContent(), String.valueOf(serial)}; - Cursor cursor = db.query("monocles.webxdc_updates", null, + Cursor cursor = db.query("webxdc_updates", null, Message.CONVERSATION + "=? AND thread=? AND serial>?", selectionArgs, null, null, "serial ASC"); long maxSerial = 0; @@ -1007,7 +944,6 @@ public class DatabaseBackend extends SQLiteOpenHelper { public void createMessage(Message message) { SQLiteDatabase db = this.getWritableDatabase(); db.insert(Message.TABLENAME, null, message.getContentValues()); - db.insert("monocles." + Message.TABLENAME, null, message.getmonoclesContentValues()); } public void createAccount(Account account) { @@ -1113,8 +1049,6 @@ public class DatabaseBackend extends SQLiteOpenHelper { Cursor cursor; cursor = db.rawQuery( "SELECT * FROM " + Message.TABLENAME + " " + - "LEFT JOIN monocles." + Message.TABLENAME + - " USING (" + Message.UUID + ")" + "WHERE " + Message.UUID + "=?", new String[]{uuid} ); @@ -1149,8 +1083,6 @@ public class DatabaseBackend extends SQLiteOpenHelper { Cursor cursor; cursor = db.rawQuery( "SELECT * FROM " + Message.TABLENAME + " " + - "LEFT JOIN monocles." + Message.TABLENAME + - " USING (" + Message.UUID + ")" + "WHERE " + Message.UUID + " IN (" + TextUtils.join(",", template) + ") OR " + Message.SERVER_MSG_ID + " IN (" + TextUtils.join(",", template) + ") OR " + Message.REMOTE_MSG_ID + " IN (" + TextUtils.join(",", template) + ")", params.toArray(new String[0]) ); @@ -1177,9 +1109,7 @@ public class DatabaseBackend extends SQLiteOpenHelper { String[] selectionArgs = {conversation.getUuid()}; cursor = db.rawQuery( "SELECT * FROM " + Message.TABLENAME + " " + - "LEFT JOIN monocles." + Message.TABLENAME + - " USING (" + Message.UUID + ")" + - " WHERE " + Message.UUID + " IN (" + + "WHERE " + Message.UUID + " IN (" + "SELECT " + Message.UUID + " FROM " + Message.TABLENAME + " WHERE " + Message.CONVERSATION + "=? " + "ORDER BY " + Message.TIME_SENT + " DESC " + @@ -1192,9 +1122,7 @@ public class DatabaseBackend extends SQLiteOpenHelper { Long.toString(timestamp)}; cursor = db.rawQuery( "SELECT * FROM " + Message.TABLENAME + " " + - "LEFT JOIN monocles." + Message.TABLENAME + - " USING (" + Message.UUID + ")" + - " WHERE " + Message.UUID + " IN (" + + "WHERE " + Message.UUID + " IN (" + "SELECT " + Message.UUID + " FROM " + Message.TABLENAME + " WHERE " + Message.CONVERSATION + "=? AND " + Message.TIME_SENT + " getMessagesIterable(final Conversation conversation) { + return () -> new Iterator() { + @Override + public boolean hasNext() { + if (messageCursor == null) return false; + return !messageCursor.isAfterLast(); + } + + final SQLiteDatabase database = getReadableDatabase(); + final String[] queryArgs = {conversation.getUuid(), "1"}; + Cursor messageCursor = null; + + { + messageCursor = database.query(Message.TABLENAME, null, Message.CONVERSATION + + "=? and " + Message.DELETED + " mAccounts; + + public ExportBackupWorker(@NonNull Context context, @NonNull WorkerParameters workerParams) { super(context, workerParams); final var inputData = workerParams.getInputData(); @@ -234,6 +247,25 @@ public class ExportBackupWorker extends Worker { Log.d(Config.LOGTAG, "written backup to " + file.getAbsoluteFile()); count++; } + + mDatabaseBackend = DatabaseBackend.getInstance(Conversations.getContext()); + mAccounts = mDatabaseBackend.getAccounts(); + final SharedPreferences ReadableLogs = PreferenceManager.getDefaultSharedPreferences(getApplicationContext()); + ReadableLogsEnabled = ReadableLogs.getBoolean("export_plain_text_logs", getApplicationContext().getResources().getBoolean(R.bool.plain_text_logs)); + + try { + if (ReadableLogsEnabled) { // todo + List conversations = mDatabaseBackend.getConversations(Conversation.STATUS_AVAILABLE); + conversations.addAll(mDatabaseBackend.getConversations(Conversation.STATUS_ARCHIVED)); + for (Conversation conversation : conversations) { + writeToFile(conversation); + Log.d(Config.LOGTAG, "Exporting readable logs for " + conversation.getJid()); + } + } + } catch (Exception e) { + e.printStackTrace(); + } + return files; } @@ -245,7 +277,7 @@ public class ExportBackupWorker extends Worker { private void messageExportmonocles(final SQLiteDatabase db, final String uuid, final JsonWriter writer, final Progress progress) throws IOException { final var notificationManager = getApplicationContext().getSystemService(NotificationManager.class); - Cursor cursor = db.rawQuery("select mmessages.* from messages join monocles.messages mmessages using (uuid) join conversations on conversations.uuid=messages.conversationUuid where conversations.accountUuid=?", new String[]{uuid}); + Cursor cursor = db.rawQuery("select mmessages.* from messages join messages mmessages using (uuid) join conversations on conversations.uuid=messages.conversationUuid where conversations.accountUuid=?", new String[]{uuid}); int size = cursor != null ? cursor.getCount() : 0; Log.d(Config.LOGTAG, "exporting " + size + " monocles messages for account " + uuid); int i = 0; @@ -253,7 +285,7 @@ public class ExportBackupWorker extends Worker { while (cursor != null && cursor.moveToNext()) { writer.beginObject(); writer.name("table"); - writer.value("monocles." + Message.TABLENAME); + writer.value("" + Message.TABLENAME); writer.name("values"); writer.beginObject(); for (int j = 0; j < cursor.getColumnCount(); ++j) { @@ -275,13 +307,13 @@ public class ExportBackupWorker extends Worker { cursor.close(); } - cursor = db.rawQuery("select webxdc_updates.* from " + Conversation.TABLENAME + " join monocles.webxdc_updates webxdc_updates on " + Conversation.TABLENAME + ".uuid=webxdc_updates." + Message.CONVERSATION + " where conversations.accountUuid=?", new String[]{uuid}); + cursor = db.rawQuery("select webxdc_updates.* from " + Conversation.TABLENAME + " join webxdc_updates webxdc_updates on " + Conversation.TABLENAME + ".uuid=webxdc_updates." + Message.CONVERSATION + " where conversations.accountUuid=?", new String[]{uuid}); size = cursor != null ? cursor.getCount() : 0; Log.d(Config.LOGTAG, "exporting " + size + " WebXDC updates for account " + uuid); while (cursor != null && cursor.moveToNext()) { writer.beginObject(); writer.name("table"); - writer.value("monocles.webxdc_updates"); + writer.value("webxdc_updates"); writer.name("values"); writer.beginObject(); for (int j = 0; j < cursor.getColumnCount(); ++j) { @@ -537,4 +569,70 @@ public class ExportBackupWorker extends Worker { return notification.build(); } } + + private void writeToFile(Conversation conversation) { + Jid accountJid = resolveAccountUuid(conversation.getAccountUuid()); + Jid contactJid = conversation.getJid(); + final File dir = new File(FileBackend.getBackupDirectory(getApplicationContext()), accountJid.asBareJid().toString()); + dir.mkdirs(); + + BufferedWriter bw = null; + try { + for (Message message : mDatabaseBackend.getMessagesIterable(conversation)) { + if (message == null) + continue; + if (message.getType() == Message.TYPE_TEXT || message.hasFileOnRemoteHost()) { + String date = DATE_FORMAT.format(new Date(message.getTimeSent())); + if (bw == null) { + bw = new BufferedWriter(new FileWriter( + new File(dir, contactJid.asBareJid().toString() + ".txt"))); + } + String jid = null; + switch (message.getStatus()) { + case Message.STATUS_RECEIVED: + jid = getMessageCounterpart(message); + break; + case Message.STATUS_SEND: + case Message.STATUS_SEND_RECEIVED: + case Message.STATUS_SEND_DISPLAYED: + case Message.STATUS_SEND_FAILED: + jid = accountJid.asBareJid().toString(); + break; + } + if (jid != null) { + String body = message.hasFileOnRemoteHost() ? message.getFileParams().url.toString() : message.getBody(); + bw.write(String.format(MESSAGE_STRING_FORMAT, date, jid, body.replace("\\\n", "\\ \n").replace("\n", "\\ \n"))); + } + } + } + } catch (IOException e) { + e.printStackTrace(); + } finally { + try { + if (bw != null) { + bw.close(); + } + } catch (IOException e1) { + e1.printStackTrace(); + } + } + } + + private Jid resolveAccountUuid(String accountUuid) { + for (Account account : mAccounts) { + if (account.getUuid().equals(accountUuid)) { + return account.getJid(); + } + } + return null; + } + + private String getMessageCounterpart(Message message) { + String trueCounterpart = (String) message.getContentValues().get(Message.TRUE_COUNTERPART); + if (trueCounterpart != null) { + return trueCounterpart; + } else { + return message.getCounterpart().toString(); + } + } } diff --git a/src/main/res/values/bools.xml b/src/main/res/values/bools.xml index 38f2372a3..fb9b03c96 100644 --- a/src/main/res/values/bools.xml +++ b/src/main/res/values/bools.xml @@ -14,4 +14,5 @@ false false false + false diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml index 932bc0a12..a67c55323 100644 --- a/src/main/res/values/strings.xml +++ b/src/main/res/values/strings.xml @@ -1412,4 +1412,6 @@ Show to contacts only Settings successfully exported Error while exporting settings + Export plain text + Export messages unencrypted in human readable plain text \ No newline at end of file diff --git a/src/main/res/xml/preferences_backup.xml b/src/main/res/xml/preferences_backup.xml index 1c50248e8..e64b9cffb 100644 --- a/src/main/res/xml/preferences_backup.xml +++ b/src/main/res/xml/preferences_backup.xml @@ -27,6 +27,12 @@ android:key="backup_directory" android:summary="@string/pref_create_backup_summary" /> + +