Search in sources :

Example 1 with BackupFileHeader

use of eu.siacs.conversations.utils.BackupFileHeader in project Conversations by siacs.

the class BackupFileAdapter method onBindViewHolder.

@Override
public void onBindViewHolder(@NonNull BackupFileViewHolder backupFileViewHolder, int position) {
    final ImportBackupService.BackupFile backupFile = files.get(position);
    final BackupFileHeader header = backupFile.getHeader();
    backupFileViewHolder.binding.accountJid.setText(header.getJid().asBareJid().toString());
    backupFileViewHolder.binding.accountStatus.setText(String.format("%s ยท %s", header.getApp(), DateUtils.formatDateTime(backupFileViewHolder.binding.getRoot().getContext(), header.getTimestamp(), DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_YEAR)));
    backupFileViewHolder.binding.tglAccountStatus.setVisibility(View.GONE);
    backupFileViewHolder.binding.getRoot().setOnClickListener(v -> {
        if (listener != null) {
            listener.onClick(backupFile);
        }
    });
    loadAvatar(header.getJid(), backupFileViewHolder.binding.accountImage);
}
Also used : BackupFileHeader(eu.siacs.conversations.utils.BackupFileHeader) ImportBackupService(eu.siacs.conversations.services.ImportBackupService)

Example 2 with BackupFileHeader

use of eu.siacs.conversations.utils.BackupFileHeader in project Conversations by siacs.

the class ImportBackupService method importBackup.

private boolean importBackup(final Uri uri, final String password) {
    Log.d(Config.LOGTAG, "importing backup from " + uri);
    final Stopwatch stopwatch = Stopwatch.createStarted();
    try {
        final SQLiteDatabase db = mDatabaseBackend.getWritableDatabase();
        final InputStream inputStream;
        final String path = uri.getPath();
        final long fileSize;
        if ("file".equals(uri.getScheme()) && path != null) {
            final File file = new File(path);
            inputStream = new FileInputStream(file);
            fileSize = file.length();
        } else {
            final Cursor returnCursor = getContentResolver().query(uri, null, null, null, null);
            if (returnCursor == null) {
                fileSize = 0;
            } else {
                returnCursor.moveToFirst();
                fileSize = returnCursor.getLong(returnCursor.getColumnIndex(OpenableColumns.SIZE));
                returnCursor.close();
            }
            inputStream = getContentResolver().openInputStream(uri);
        }
        if (inputStream == null) {
            synchronized (mOnBackupProcessedListeners) {
                for (final OnBackupProcessed l : mOnBackupProcessedListeners) {
                    l.onBackupRestoreFailed();
                }
            }
            return false;
        }
        final CountingInputStream countingInputStream = new CountingInputStream(inputStream);
        final DataInputStream dataInputStream = new DataInputStream(countingInputStream);
        final BackupFileHeader backupFileHeader = BackupFileHeader.read(dataInputStream);
        Log.d(Config.LOGTAG, backupFileHeader.toString());
        if (mDatabaseBackend.getAccountJids(false).contains(backupFileHeader.getJid())) {
            synchronized (mOnBackupProcessedListeners) {
                for (OnBackupProcessed l : mOnBackupProcessedListeners) {
                    l.onAccountAlreadySetup();
                }
            }
            return false;
        }
        final byte[] key = ExportBackupService.getKey(password, backupFileHeader.getSalt());
        final AEADBlockCipher cipher = new GCMBlockCipher(new AESEngine());
        cipher.init(false, new AEADParameters(new KeyParameter(key), 128, backupFileHeader.getIv()));
        final CipherInputStream cipherInputStream = new CipherInputStream(countingInputStream, cipher);
        final GZIPInputStream gzipInputStream = new GZIPInputStream(cipherInputStream);
        final BufferedReader reader = new BufferedReader(new InputStreamReader(gzipInputStream, Charsets.UTF_8));
        db.beginTransaction();
        String line;
        StringBuilder multiLineQuery = null;
        while ((line = reader.readLine()) != null) {
            int count = count(line, '\'');
            if (multiLineQuery != null) {
                multiLineQuery.append('\n');
                multiLineQuery.append(line);
                if (count % 2 == 1) {
                    db.execSQL(multiLineQuery.toString());
                    multiLineQuery = null;
                    updateImportBackupNotification(fileSize, countingInputStream.getCount());
                }
            } else {
                if (count % 2 == 0) {
                    db.execSQL(line);
                    updateImportBackupNotification(fileSize, countingInputStream.getCount());
                } else {
                    multiLineQuery = new StringBuilder(line);
                }
            }
        }
        db.setTransactionSuccessful();
        db.endTransaction();
        final Jid jid = backupFileHeader.getJid();
        final Cursor countCursor = db.rawQuery("select count(messages.uuid) from messages join conversations on conversations.uuid=messages.conversationUuid join accounts on conversations.accountUuid=accounts.uuid where accounts.username=? and accounts.server=?", new String[] { jid.getEscapedLocal(), jid.getDomain().toEscapedString() });
        countCursor.moveToFirst();
        final int count = countCursor.getInt(0);
        Log.d(Config.LOGTAG, String.format("restored %d messages in %s", count, stopwatch.stop().toString()));
        countCursor.close();
        stopBackgroundService();
        synchronized (mOnBackupProcessedListeners) {
            for (OnBackupProcessed l : mOnBackupProcessedListeners) {
                l.onBackupRestored();
            }
        }
        return true;
    } catch (final Exception e) {
        final Throwable throwable = e.getCause();
        final boolean reasonWasCrypto = throwable instanceof BadPaddingException || e instanceof ZipException;
        synchronized (mOnBackupProcessedListeners) {
            for (OnBackupProcessed l : mOnBackupProcessedListeners) {
                if (reasonWasCrypto) {
                    l.onBackupDecryptionFailed();
                } else {
                    l.onBackupRestoreFailed();
                }
            }
        }
        Log.d(Config.LOGTAG, "error restoring backup " + uri, e);
        return false;
    }
}
Also used : KeyParameter(org.bouncycastle.crypto.params.KeyParameter) Stopwatch(com.google.common.base.Stopwatch) BadPaddingException(javax.crypto.BadPaddingException) Cursor(android.database.Cursor) GZIPInputStream(java.util.zip.GZIPInputStream) AEADBlockCipher(org.bouncycastle.crypto.modes.AEADBlockCipher) AESEngine(org.bouncycastle.crypto.engines.AESEngine) CipherInputStream(org.bouncycastle.crypto.io.CipherInputStream) InputStreamReader(java.io.InputStreamReader) Jid(eu.siacs.conversations.xmpp.Jid) GZIPInputStream(java.util.zip.GZIPInputStream) CountingInputStream(com.google.common.io.CountingInputStream) CipherInputStream(org.bouncycastle.crypto.io.CipherInputStream) DataInputStream(java.io.DataInputStream) FileInputStream(java.io.FileInputStream) InputStream(java.io.InputStream) CountingInputStream(com.google.common.io.CountingInputStream) ZipException(java.util.zip.ZipException) DataInputStream(java.io.DataInputStream) FileInputStream(java.io.FileInputStream) ZipException(java.util.zip.ZipException) FileNotFoundException(java.io.FileNotFoundException) IOException(java.io.IOException) BadPaddingException(javax.crypto.BadPaddingException) BackupFileHeader(eu.siacs.conversations.utils.BackupFileHeader) AEADParameters(org.bouncycastle.crypto.params.AEADParameters) SQLiteDatabase(android.database.sqlite.SQLiteDatabase) BufferedReader(java.io.BufferedReader) File(java.io.File) GCMBlockCipher(org.bouncycastle.crypto.modes.GCMBlockCipher)

Example 3 with BackupFileHeader

use of eu.siacs.conversations.utils.BackupFileHeader in project Conversations by siacs.

the class ExportBackupService method export.

private List<File> export() throws Exception {
    NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(getBaseContext(), "backup");
    mBuilder.setContentTitle(getString(R.string.notification_create_backup_title)).setSmallIcon(R.drawable.ic_archive_white_24dp).setProgress(1, 0, false);
    startForeground(NOTIFICATION_ID, mBuilder.build());
    int count = 0;
    final int max = this.mAccounts.size();
    final SecureRandom secureRandom = new SecureRandom();
    final List<File> files = new ArrayList<>();
    Log.d(Config.LOGTAG, "starting backup for " + max + " accounts");
    for (final Account account : this.mAccounts) {
        final String password = account.getPassword();
        if (Strings.nullToEmpty(password).trim().isEmpty()) {
            Log.d(Config.LOGTAG, String.format("skipping backup for %s because password is empty. unable to encrypt", account.getJid().asBareJid()));
            continue;
        }
        Log.d(Config.LOGTAG, String.format("exporting data for account %s (%s)", account.getJid().asBareJid(), account.getUuid()));
        final byte[] IV = new byte[12];
        final byte[] salt = new byte[16];
        secureRandom.nextBytes(IV);
        secureRandom.nextBytes(salt);
        final BackupFileHeader backupFileHeader = new BackupFileHeader(getString(R.string.app_name), account.getJid(), System.currentTimeMillis(), IV, salt);
        final Progress progress = new Progress(mBuilder, max, count);
        final File file = new File(FileBackend.getBackupDirectory(this) + account.getJid().asBareJid().toEscapedString() + ".ceb");
        files.add(file);
        final File directory = file.getParentFile();
        if (directory != null && directory.mkdirs()) {
            Log.d(Config.LOGTAG, "created backup directory " + directory.getAbsolutePath());
        }
        final FileOutputStream fileOutputStream = new FileOutputStream(file);
        final DataOutputStream dataOutputStream = new DataOutputStream(fileOutputStream);
        backupFileHeader.write(dataOutputStream);
        dataOutputStream.flush();
        final Cipher cipher = Compatibility.twentyEight() ? Cipher.getInstance(CIPHERMODE) : Cipher.getInstance(CIPHERMODE, PROVIDER);
        final byte[] key = getKey(password, salt);
        SecretKeySpec keySpec = new SecretKeySpec(key, KEYTYPE);
        IvParameterSpec ivSpec = new IvParameterSpec(IV);
        cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec);
        CipherOutputStream cipherOutputStream = new CipherOutputStream(fileOutputStream, cipher);
        GZIPOutputStream gzipOutputStream = new GZIPOutputStream(cipherOutputStream);
        PrintWriter writer = new PrintWriter(gzipOutputStream);
        SQLiteDatabase db = this.mDatabaseBackend.getReadableDatabase();
        final String uuid = account.getUuid();
        accountExport(db, uuid, writer);
        simpleExport(db, Conversation.TABLENAME, Conversation.ACCOUNT, uuid, writer);
        messageExport(db, uuid, writer, progress);
        for (String table : Arrays.asList(SQLiteAxolotlStore.PREKEY_TABLENAME, SQLiteAxolotlStore.SIGNED_PREKEY_TABLENAME, SQLiteAxolotlStore.SESSION_TABLENAME, SQLiteAxolotlStore.IDENTITIES_TABLENAME)) {
            simpleExport(db, table, SQLiteAxolotlStore.ACCOUNT, uuid, writer);
        }
        writer.flush();
        writer.close();
        mediaScannerScanFile(file);
        Log.d(Config.LOGTAG, "written backup to " + file.getAbsoluteFile());
        count++;
    }
    return files;
}
Also used : Account(eu.siacs.conversations.entities.Account) CipherOutputStream(javax.crypto.CipherOutputStream) DataOutputStream(java.io.DataOutputStream) ArrayList(java.util.ArrayList) SecureRandom(java.security.SecureRandom) BackupFileHeader(eu.siacs.conversations.utils.BackupFileHeader) GZIPOutputStream(java.util.zip.GZIPOutputStream) SQLiteDatabase(android.database.sqlite.SQLiteDatabase) SecretKeySpec(javax.crypto.spec.SecretKeySpec) FileOutputStream(java.io.FileOutputStream) NotificationCompat(androidx.core.app.NotificationCompat) IvParameterSpec(javax.crypto.spec.IvParameterSpec) Cipher(javax.crypto.Cipher) File(java.io.File) PrintWriter(java.io.PrintWriter)

Aggregations

BackupFileHeader (eu.siacs.conversations.utils.BackupFileHeader)3 SQLiteDatabase (android.database.sqlite.SQLiteDatabase)2 File (java.io.File)2 Cursor (android.database.Cursor)1 NotificationCompat (androidx.core.app.NotificationCompat)1 Stopwatch (com.google.common.base.Stopwatch)1 CountingInputStream (com.google.common.io.CountingInputStream)1 Account (eu.siacs.conversations.entities.Account)1 ImportBackupService (eu.siacs.conversations.services.ImportBackupService)1 Jid (eu.siacs.conversations.xmpp.Jid)1 BufferedReader (java.io.BufferedReader)1 DataInputStream (java.io.DataInputStream)1 DataOutputStream (java.io.DataOutputStream)1 FileInputStream (java.io.FileInputStream)1 FileNotFoundException (java.io.FileNotFoundException)1 FileOutputStream (java.io.FileOutputStream)1 IOException (java.io.IOException)1 InputStream (java.io.InputStream)1 InputStreamReader (java.io.InputStreamReader)1 PrintWriter (java.io.PrintWriter)1