Skip to content

Commit 57d9aa9

Browse files
committed
added password for encrypting backups
1 parent 5932e80 commit 57d9aa9

9 files changed

Lines changed: 208 additions & 101 deletions

File tree

app/build.gradle

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,8 @@ ext {
5555
stethoLibVersion = '1.5.0'
5656
markwonVersion = '4.2.1'
5757
work_version = '2.3.2'
58+
zip4j_version = '2.3.2'
59+
preference_version = '1.1.0'
5860
}
5961

6062
dependencies {
@@ -117,4 +119,7 @@ dependencies {
117119
implementation "androidx.work:work-runtime:${work_version}"
118120
implementation "androidx.work:work-rxjava2:${work_version}"
119121

122+
implementation "net.lingala.zip4j:zip4j:${zip4j_version}"
123+
124+
implementation "androidx.preference:preference:${preference_version}"
120125
}

app/src/main/java/org/schabi/newpipe/database/BackupRestoreHelper.java

Lines changed: 15 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
import org.schabi.newpipe.R;
1313
import org.schabi.newpipe.util.ZipHelper;
1414

15-
import java.io.BufferedOutputStream;
1615
import java.io.File;
1716
import java.io.FileInputStream;
1817
import java.io.FileNotFoundException;
@@ -21,8 +20,6 @@
2120
import java.io.ObjectInputStream;
2221
import java.io.ObjectOutputStream;
2322
import java.util.Map;
24-
import java.util.zip.ZipFile;
25-
import java.util.zip.ZipOutputStream;
2623

2724
public class BackupRestoreHelper {
2825

@@ -57,25 +54,23 @@ public String getAutoBackupPath(){
5754
return autoBackupPath;
5855
}
5956

60-
public void exportDatabase(String path) throws Exception {
61-
ZipOutputStream outZip = new ZipOutputStream(
62-
new BufferedOutputStream(
63-
new FileOutputStream(path)));
64-
ZipHelper.addFileToZip(outZip, newpipe_db.getPath(), "newpipe.db");
65-
57+
public void exportDatabase(String path, char[] password) throws Exception {
58+
ZipHelper.addFileToZip(path, newpipe_db.getPath(), "newpipe.db", password);
6659
saveSharedPreferencesToFile(newpipe_settings);
67-
ZipHelper.addFileToZip(outZip, newpipe_settings.getPath(), "newpipe.settings");
68-
69-
outZip.close();
60+
ZipHelper.addFileToZip(path, newpipe_settings.getPath(), "newpipe.settings", password);
7061
}
7162

7263
private void saveSharedPreferencesToFile(File dst) {
7364
ObjectOutputStream output = null;
65+
SharedPreferences pref = null;
66+
String password = null;
7467
try {
7568
output = new ObjectOutputStream(new FileOutputStream(dst));
76-
SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(ctx);
69+
pref = PreferenceManager.getDefaultSharedPreferences(ctx);
70+
// remove password from shared prefs before taking backup
71+
password = pref.getString(ctx.getString(R.string.backup_password_key), null);
72+
pref.edit().remove(ctx.getString(R.string.backup_password_key)).apply();
7773
output.writeObject(pref.getAll());
78-
7974
} catch (FileNotFoundException e) {
8075
e.printStackTrace();
8176
} catch (IOException e) {
@@ -88,33 +83,21 @@ private void saveSharedPreferencesToFile(File dst) {
8883
}
8984
} catch (IOException ex) {
9085
ex.printStackTrace();
86+
} finally {
87+
// add password again after taking backup
88+
if(pref != null) pref.edit().putString(ctx.getString(R.string.backup_password_key), password).apply();
9189
}
9290
}
9391
}
9492

9593
@MainThread
96-
public void importDatabase(String filePath) throws Exception {
97-
// check if file is supported
98-
ZipFile zipFile = null;
99-
try {
100-
zipFile = new ZipFile(filePath);
101-
} catch (IOException ioe) {
102-
Toast.makeText(ctx, R.string.no_valid_zip_file, Toast.LENGTH_SHORT)
103-
.show();
104-
return;
105-
} finally {
106-
try {
107-
zipFile.close();
108-
} catch (Exception ignored) {
109-
}
110-
}
94+
public void importDatabase(String filePath, char[] password) throws Exception {
11195

11296
if (!databasesDir.exists() && !databasesDir.mkdir()) {
11397
throw new Exception("Could not create databases dir");
11498
}
11599

116-
final boolean isDbFileExtracted = ZipHelper.extractFileFromZip(filePath,
117-
newpipe_db.getPath(), "newpipe.db");
100+
final boolean isDbFileExtracted = ZipHelper.extractFileFromZip(filePath, "newpipe.db", databasesDir.getPath(), password);
118101

119102
if (isDbFileExtracted) {
120103
newpipe_db_journal.delete();
@@ -128,7 +111,7 @@ public void importDatabase(String filePath) throws Exception {
128111
}
129112

130113
//If settings file exist, ask if it should be imported.
131-
if (ZipHelper.extractFileFromZip(filePath, newpipe_settings.getPath(), "newpipe.settings")) {
114+
if (ZipHelper.extractFileFromZip(filePath, "newpipe.settings", databasesDir.getPath(), password)) {
132115
AlertDialog.Builder alert = new AlertDialog.Builder(ctx);
133116
alert.setTitle(R.string.import_settings);
134117

app/src/main/java/org/schabi/newpipe/settings/AutoBackupWorker.java

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,14 @@
22

33
import android.content.Context;
44
import android.os.Build;
5+
import android.text.TextUtils;
56

67
import androidx.annotation.NonNull;
8+
import androidx.preference.PreferenceManager;
79
import androidx.work.Worker;
810
import androidx.work.WorkerParameters;
911

12+
import org.schabi.newpipe.R;
1013
import org.schabi.newpipe.database.BackupRestoreHelper;
1114

1215
import java.io.File;
@@ -20,12 +23,15 @@ public AutoBackupWorker(@NonNull Context context, @NonNull WorkerParameters work
2023
@NonNull
2124
@Override
2225
public Result doWork() {
26+
Context ctx = getApplicationContext();
27+
BackupRestoreHelper backupRestoreHelper = new BackupRestoreHelper(ctx);
28+
String autoBackupPath = backupRestoreHelper.getAutoBackupPath();
2329
try {
24-
BackupRestoreHelper backupRestoreHelper = new BackupRestoreHelper(getApplicationContext());
25-
String autoBackupPath = backupRestoreHelper.getAutoBackupPath();
2630
new File(autoBackupPath).mkdirs();
2731
String path = autoBackupPath + File.separator + "NewPipeData-" + Build.MODEL + ".zip";
28-
backupRestoreHelper.exportDatabase(path);
32+
String password = PreferenceManager.getDefaultSharedPreferences(ctx).getString(ctx.getString(R.string.backup_password_key), null);
33+
if(TextUtils.isEmpty(password)) return Result.failure();
34+
backupRestoreHelper.exportDatabase(path, password.toCharArray());
2935
} catch (Exception e) {
3036
return Result.failure();
3137
}

app/src/main/java/org/schabi/newpipe/settings/BackupSettingsFragment.java

Lines changed: 102 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@
88
import android.content.Intent;
99
import android.net.Uri;
1010
import android.os.Bundle;
11+
import android.text.TextUtils;
12+
import android.view.LayoutInflater;
13+
import android.view.View;
1114
import android.widget.Toast;
1215

1316
import androidx.annotation.NonNull;
@@ -19,8 +22,12 @@
1922
import androidx.work.PeriodicWorkRequest;
2023
import androidx.work.WorkManager;
2124

25+
import com.google.android.material.textfield.TextInputEditText;
2226
import com.nononsenseapps.filepicker.Utils;
2327

28+
import net.lingala.zip4j.ZipFile;
29+
import net.lingala.zip4j.exception.ZipException;
30+
2431
import org.schabi.newpipe.R;
2532
import org.schabi.newpipe.database.BackupRestoreHelper;
2633
import org.schabi.newpipe.report.ErrorActivity;
@@ -34,6 +41,7 @@
3441
import java.net.URLDecoder;
3542
import java.nio.charset.StandardCharsets;
3643
import java.text.SimpleDateFormat;
44+
import java.util.Arrays;
3745
import java.util.Date;
3846
import java.util.Locale;
3947
import java.util.concurrent.TimeUnit;
@@ -110,8 +118,7 @@ public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
110118
});
111119
autoBackupPathPreference.setSummary(backupRestoreHelper.getAutoBackupPath());
112120

113-
114-
autoBackupSwitchPreference = (SwitchPreference) findPreference(getString(R.string.scheduled_backups_key));
121+
autoBackupSwitchPreference = findPreference(getString(R.string.scheduled_backups_key));
115122
autoBackupSwitchPreference.setOnPreferenceChangeListener((preference, newValue) -> {
116123
if((Boolean) newValue) PermissionHelper.checkStoragePermissions(getActivity(), BACKUP_STORAGE_PERMISSION_REQUEST_CODE);
117124
return true;
@@ -206,17 +213,88 @@ public void onActivityResult(int requestCode, int resultCode, @NonNull Intent da
206213
}
207214

208215
private void exportDatabase(String path) {
209-
try {
210-
backupRestoreHelper.exportDatabase(path);
211-
Toast.makeText(ctx, R.string.export_complete_toast, Toast.LENGTH_SHORT).show();
212-
} catch (Exception e) {
213-
onError(e);
214-
}
216+
217+
AlertDialog.Builder alert = new AlertDialog.Builder(ctx);
218+
LayoutInflater inflater = LayoutInflater.from(ctx);
219+
View view = inflater.inflate(R.layout.dialog_password, null);
220+
TextInputEditText editText = view.findViewById(android.R.id.edit);
221+
alert.setView(view);
222+
alert.setTitle(R.string.auto_backup_password_title);
223+
alert.setMessage(R.string.backup_password_message);
224+
225+
alert.setNegativeButton(R.string.backup_no_password, (dialog, which) -> {
226+
dialog.dismiss();
227+
try {
228+
backupRestoreHelper.exportDatabase(path, null);
229+
Toast.makeText(ctx, R.string.export_complete_toast, Toast.LENGTH_SHORT).show();
230+
} catch (Exception e) {
231+
onError(e);
232+
}
233+
});
234+
alert.setPositiveButton(R.string.finish, (dialog, which) -> {
235+
dialog.dismiss();
236+
char[] password = getPassword(editText);
237+
try {
238+
backupRestoreHelper.exportDatabase(path, password);
239+
Toast.makeText(ctx, R.string.export_complete_toast, Toast.LENGTH_SHORT).show();
240+
} catch (Exception e) {
241+
onError(e);
242+
} finally {
243+
clearPassword(password);
244+
}
245+
});
246+
247+
alert.show();
248+
}
249+
250+
private char[] getPassword(TextInputEditText editText) {
251+
int length = editText.length();
252+
char[] password = new char[length];
253+
editText.getText().getChars(0, length, password, 0);
254+
return password;
255+
}
256+
257+
private void clearPassword(char[] password){
258+
Arrays.fill(password,'0');
215259
}
216260

217261
private void importDatabase(String filePath) {
262+
263+
ZipFile zipFile = new ZipFile(filePath);
264+
if(!zipFile.isValidZipFile()){
265+
Toast.makeText(ctx, R.string.no_valid_zip_file, Toast.LENGTH_SHORT).show();
266+
return;
267+
}
268+
218269
try {
219-
backupRestoreHelper.importDatabase(filePath);
270+
if(zipFile.isEncrypted()){
271+
AlertDialog.Builder alert = new AlertDialog.Builder(ctx);
272+
LayoutInflater inflater = LayoutInflater.from(ctx);
273+
View view = inflater.inflate(R.layout.dialog_password, null);
274+
TextInputEditText editText = view.findViewById(android.R.id.edit);
275+
alert.setView(view);
276+
277+
alert.setTitle(R.string.auto_backup_password_title);
278+
279+
alert.setNegativeButton(android.R.string.no, (dialog, which) -> {
280+
dialog.dismiss();
281+
});
282+
alert.setPositiveButton(R.string.finish, (dialog, which) -> {
283+
dialog.dismiss();
284+
char[] password = getPassword(editText);
285+
try {
286+
backupRestoreHelper.importDatabase(filePath, password);
287+
} catch (Exception e) {
288+
onError(e);
289+
} finally {
290+
clearPassword(password);
291+
}
292+
});
293+
294+
alert.show();
295+
}else{
296+
backupRestoreHelper.importDatabase(filePath, null);
297+
}
220298
} catch (Exception e) {
221299
onError(e);
222300
}
@@ -225,9 +303,13 @@ private void importDatabase(String filePath) {
225303
private void scheduleWork(String tag) {
226304
Boolean autoBackup = defaultPreferences.getBoolean(getString(R.string.scheduled_backups_key), false);
227305
if(autoBackup){
306+
if(TextUtils.isEmpty(defaultPreferences.getString(ctx.getString(R.string.backup_password_key), null))){
307+
Toast.makeText(ctx, R.string.auto_backup_password_mandatory, Toast.LENGTH_LONG).show();
308+
}
228309
Integer interval = Integer.valueOf(defaultPreferences.getString(getString(R.string.backup_frequency_key), "24"));
229310
PeriodicWorkRequest.Builder autoBackupRequestBuilder =
230311
new PeriodicWorkRequest.Builder(AutoBackupWorker.class, interval, TimeUnit.HOURS);
312+
autoBackupRequestBuilder.setInitialDelay(15, TimeUnit.MINUTES);
231313
PeriodicWorkRequest request = autoBackupRequestBuilder.build();
232314
WorkManager.getInstance(ctx).enqueueUniquePeriodicWork(tag, ExistingPeriodicWorkPolicy.REPLACE , request);
233315
}else{
@@ -246,12 +328,17 @@ public void onPause() {
246328
//////////////////////////////////////////////////////////////////////////*/
247329

248330
protected void onError(Throwable e) {
249-
final Activity activity = getActivity();
250-
ErrorActivity.reportError(activity, e,
251-
activity.getClass(),
252-
null,
253-
ErrorActivity.ErrorInfo.make(UserAction.UI_ERROR,
254-
"none", "", R.string.app_ui_crash));
331+
332+
if(e instanceof ZipException && ((ZipException)e).getType() == ZipException.Type.WRONG_PASSWORD){
333+
Toast.makeText(ctx, R.string.no_valid_password, Toast.LENGTH_SHORT).show();
334+
}else{
335+
final Activity activity = getActivity();
336+
ErrorActivity.reportError(activity, e,
337+
activity.getClass(),
338+
null,
339+
ErrorActivity.ErrorInfo.make(UserAction.UI_ERROR,
340+
"none", "", R.string.app_ui_crash));
341+
}
255342
}
256343

257344
}

0 commit comments

Comments
 (0)