@@ -14,6 +14,8 @@ import android.os.IBinder;
import android.support.v4.app.NotificationCompat;
import android.util.Log;
+import com.google.common.base.Strings;
+
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileOutputStream;
@@ -70,7 +72,7 @@ public class ExportBackupService extends Service {
if (Compatibility.runsAndTargetsTwentyFour(context)) {
openIntent.setType("resource/folder");
} else {
- openIntent.setDataAndType(Uri.parse("file://"+path),"resource/folder");
+ openIntent.setDataAndType(Uri.parse("file://" + path), "resource/folder");
}
openIntent.putExtra("org.openintents.extra.ABSOLUTE_PATH", path);
@@ -87,7 +89,7 @@ public class ExportBackupService extends Service {
}
- private static void accountExport(SQLiteDatabase db, String uuid, PrintWriter writer) {
+ private static void accountExport(final SQLiteDatabase db, final String uuid, final PrintWriter writer) {
final StringBuilder builder = new StringBuilder();
final Cursor accountCursor = db.query(Account.TABLENAME, null, Account.UUID + "=?", new String[]{uuid}, null, null, null);
while (accountCursor != null && accountCursor.moveToNext()) {
@@ -136,27 +138,28 @@ public class ExportBackupService extends Service {
}
}
- public static byte[] getKey(String password, byte[] salt) {
+ public static byte[] getKey(final String password, final byte[] salt) throws InvalidKeySpecException {
+ final SecretKeyFactory factory;
try {
- SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
- return factory.generateSecret(new PBEKeySpec(password.toCharArray(), salt, 1024, 128)).getEncoded();
- } catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
- throw new AssertionError(e);
+ factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
+ } catch (NoSuchAlgorithmException e) {
+ throw new IllegalStateException(e);
}
+ return factory.generateSecret(new PBEKeySpec(password.toCharArray(), salt, 1024, 128)).getEncoded();
}
- private static String cursorToString(String tablename, Cursor cursor, int max) {
- return cursorToString(tablename, cursor, max, false);
+ private static String cursorToString(final String table, final Cursor cursor, final int max) {
+ return cursorToString(table, cursor, max, false);
}
- private static String cursorToString(final String tablename, final Cursor cursor, int max, boolean ignore) {
- final boolean identities = SQLiteAxolotlStore.IDENTITIES_TABLENAME.equals(tablename);
+ private static String cursorToString(final String table, final Cursor cursor, int max, boolean ignore) {
+ final boolean identities = SQLiteAxolotlStore.IDENTITIES_TABLENAME.equals(table);
StringBuilder builder = new StringBuilder();
builder.append("INSERT ");
if (ignore) {
builder.append("OR IGNORE ");
}
- builder.append("INTO ").append(tablename).append("(");
+ builder.append("INTO ").append(table).append("(");
int skipColumn = -1;
for (int i = 0; i < cursor.getColumnCount(); ++i) {
final String name = cursor.getColumnName(i);
@@ -222,7 +225,8 @@ public class ExportBackupService extends Service {
try {
files = export();
success = true;
- } catch (Exception e) {
+ } catch (final Exception e) {
+ Log.d(Config.LOGTAG, "unable to create backup", e);
success = false;
files = Collections.emptyList();
}
@@ -234,6 +238,8 @@ public class ExportBackupService extends Service {
stopSelf();
}).start();
return START_STICKY;
+ } else {
+ Log.d(Config.LOGTAG, "ExportBackupService. ignoring start command because already running");
}
return START_NOT_STICKY;
}
@@ -241,7 +247,7 @@ public class ExportBackupService extends Service {
private void messageExport(SQLiteDatabase db, String uuid, PrintWriter writer, Progress progress) {
Cursor cursor = db.rawQuery("select messages.* from messages 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 + " messages");
+ Log.d(Config.LOGTAG, "exporting " + size + " messages for account " + uuid);
int i = 0;
int p = 0;
while (cursor != null && cursor.moveToNext()) {
@@ -272,7 +278,14 @@ public class ExportBackupService extends Service {
final int max = this.mAccounts.size();
final SecureRandom secureRandom = new SecureRandom();
final List<File> files = new ArrayList<>();
- for (Account account : this.mAccounts) {
+ 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);
@@ -281,8 +294,9 @@ public class ExportBackupService extends Service {
final Progress progress = new Progress(mBuilder, max, count);
final File file = new File(FileBackend.getBackupDirectory(this) + account.getJid().asBareJid().toEscapedString() + ".ceb");
files.add(file);
- if (file.getParentFile().mkdirs()) {
- Log.d(Config.LOGTAG, "created backup directory " + file.getParentFile().getAbsolutePath());
+ 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);
@@ -290,8 +304,7 @@ public class ExportBackupService extends Service {
dataOutputStream.flush();
final Cipher cipher = Compatibility.twentyEight() ? Cipher.getInstance(CIPHERMODE) : Cipher.getInstance(CIPHERMODE, PROVIDER);
- byte[] key = getKey(account.getPassword(), salt);
- Log.d(Config.LOGTAG, backupFileHeader.toString());
+ final byte[] key = getKey(password, salt);
SecretKeySpec keySpec = new SecretKeySpec(key, KEYTYPE);
IvParameterSpec ivSpec = new IvParameterSpec(IV);
cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec);
@@ -315,7 +328,7 @@ public class ExportBackupService extends Service {
return files;
}
- private void notifySuccess(List<File> files) {
+ private void notifySuccess(final List<File> files) {
final String path = FileBackend.getBackupDirectory(this);
PendingIntent openFolderIntent = null;
@@ -331,14 +344,14 @@ public class ExportBackupService extends Service {
if (files.size() > 0) {
final Intent intent = new Intent(Intent.ACTION_SEND_MULTIPLE);
ArrayList<Uri> uris = new ArrayList<>();
- for(File file : files) {
+ for (File file : files) {
uris.add(FileBackend.getUriForFile(this, file));
}
intent.putParcelableArrayListExtra(Intent.EXTRA_STREAM, uris);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.setType(MIME_TYPE);
final Intent chooser = Intent.createChooser(intent, getString(R.string.share_backup_files));
- shareFilesIntent = PendingIntent.getActivity(this,190, chooser, PendingIntent.FLAG_UPDATE_CURRENT);
+ shareFilesIntent = PendingIntent.getActivity(this, 190, chooser, PendingIntent.FLAG_UPDATE_CURRENT);
}
NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(getBaseContext(), "backup");
@@ -361,7 +374,7 @@ public class ExportBackupService extends Service {
return null;
}
- private class Progress {
+ private static class Progress {
private final NotificationCompat.Builder builder;
private final int max;
private final int count;