BackupFile.java

  1package eu.siacs.conversations.utils;
  2
  3import android.content.Context;
  4import android.net.Uri;
  5import android.util.Log;
  6import com.google.common.collect.Collections2;
  7import com.google.common.collect.ComparisonChain;
  8import com.google.common.collect.ImmutableList;
  9import com.google.common.collect.ImmutableSet;
 10import com.google.common.collect.Ordering;
 11import com.google.common.util.concurrent.Futures;
 12import com.google.common.util.concurrent.ListenableFuture;
 13import eu.siacs.conversations.Config;
 14import eu.siacs.conversations.R;
 15import eu.siacs.conversations.persistance.DatabaseBackend;
 16import eu.siacs.conversations.persistance.FileBackend;
 17import eu.siacs.conversations.services.QuickConversationsService;
 18import eu.siacs.conversations.xmpp.Jid;
 19import java.io.DataInputStream;
 20import java.io.File;
 21import java.io.FileInputStream;
 22import java.io.FileNotFoundException;
 23import java.io.IOException;
 24import java.io.InputStream;
 25import java.util.ArrayList;
 26import java.util.List;
 27import java.util.concurrent.ExecutorService;
 28import java.util.concurrent.Executors;
 29
 30public class BackupFile implements Comparable<BackupFile> {
 31
 32    private static final ExecutorService BACKUP_FILE_READER_EXECUTOR =
 33            Executors.newSingleThreadExecutor();
 34
 35    private final Uri uri;
 36    private final BackupFileHeader header;
 37
 38    private BackupFile(Uri uri, BackupFileHeader header) {
 39        this.uri = uri;
 40        this.header = header;
 41    }
 42
 43    public static ListenableFuture<BackupFile> readAsync(final Context context, final Uri uri) {
 44        return Futures.submit(() -> read(context, uri), BACKUP_FILE_READER_EXECUTOR);
 45    }
 46
 47    private static BackupFile read(final File file) throws IOException {
 48        final FileInputStream fileInputStream = new FileInputStream(file);
 49        final DataInputStream dataInputStream = new DataInputStream(fileInputStream);
 50        BackupFileHeader backupFileHeader = BackupFileHeader.read(dataInputStream);
 51        fileInputStream.close();
 52        return new BackupFile(Uri.fromFile(file), backupFileHeader);
 53    }
 54
 55    public static BackupFile read(final Context context, final Uri uri) throws IOException {
 56        final InputStream inputStream = context.getContentResolver().openInputStream(uri);
 57        if (inputStream == null) {
 58            throw new FileNotFoundException();
 59        }
 60        final DataInputStream dataInputStream = new DataInputStream(inputStream);
 61        final BackupFileHeader backupFileHeader = BackupFileHeader.read(dataInputStream);
 62        inputStream.close();
 63        return new BackupFile(uri, backupFileHeader);
 64    }
 65
 66    public BackupFileHeader getHeader() {
 67        return header;
 68    }
 69
 70    public Uri getUri() {
 71        return uri;
 72    }
 73
 74    public static ListenableFuture<List<BackupFile>> listAsync(final Context context) {
 75        return Futures.submit(() -> list(context), BACKUP_FILE_READER_EXECUTOR);
 76    }
 77
 78    private static List<BackupFile> list(final Context context) {
 79        final var database = DatabaseBackend.getInstance(context);
 80        final List<Jid> accounts = database.getAccountJids(false);
 81        final var backupFiles = new ImmutableList.Builder<BackupFile>();
 82        final var apps =
 83                ImmutableSet.of("Conversations", "Quicksy", context.getString(R.string.app_name));
 84        final List<File> directories = new ArrayList<>();
 85        for (final String app : apps) {
 86            directories.add(FileBackend.getLegacyBackupDirectory(app));
 87        }
 88        directories.add(FileBackend.getBackupDirectory(context));
 89        for (final File directory : directories) {
 90            if (!directory.exists() || !directory.isDirectory()) {
 91                Log.d(Config.LOGTAG, "directory not found: " + directory.getAbsolutePath());
 92                continue;
 93            }
 94            final File[] files = directory.listFiles();
 95            if (files == null) {
 96                continue;
 97            }
 98            Log.d(Config.LOGTAG, "looking for backups in " + directory);
 99            for (final File file : files) {
100                if (file.isFile() && file.getName().endsWith(".ceb")) {
101                    try {
102                        final BackupFile backupFile = BackupFile.read(file);
103                        if (accounts.contains(backupFile.getHeader().getJid())) {
104                            Log.d(
105                                    Config.LOGTAG,
106                                    "skipping backup for " + backupFile.getHeader().getJid());
107                        } else {
108                            backupFiles.add(backupFile);
109                        }
110                    } catch (final IOException
111                            | IllegalArgumentException
112                            | BackupFileHeader.OutdatedBackupFileVersion e) {
113                        Log.d(Config.LOGTAG, "unable to read backup file ", e);
114                    }
115                }
116            }
117        }
118        final var list = backupFiles.build();
119        if (QuickConversationsService.isQuicksy()) {
120            return Ordering.natural()
121                    .immutableSortedCopy(
122                            Collections2.filter(
123                                    list,
124                                    b ->
125                                            b.header
126                                                    .getJid()
127                                                    .getDomain()
128                                                    .equals(Config.QUICKSY_DOMAIN)));
129        }
130        return Ordering.natural().immutableSortedCopy(backupFiles.build());
131    }
132
133    @Override
134    public int compareTo(final BackupFile o) {
135        return ComparisonChain.start()
136                .compare(header.getJid(), o.header.getJid())
137                .compare(header.getTimestamp(), o.header.getTimestamp())
138                .result();
139    }
140}