@@ -2,18 +2,20 @@ package org.schabi.newpipe.settings.export
22
33import android.content.SharedPreferences
44import android.util.Log
5- import org.schabi.newpipe.MainActivity.DEBUG
6- import org.schabi.newpipe.settings.NewPipeFileLocator
5+ import com.grack.nanojson.JsonArray
6+ import com.grack.nanojson.JsonParser
7+ import com.grack.nanojson.JsonParserException
8+ import com.grack.nanojson.JsonWriter
79import org.schabi.newpipe.streams.io.SharpOutputStream
810import org.schabi.newpipe.streams.io.StoredFileHelper
911import org.schabi.newpipe.util.ZipHelper
1012import java.io.IOException
1113import java.io.ObjectOutputStream
1214import java.util.zip.ZipOutputStream
1315
14- class ImportExportManager (private val fileLocator : NewPipeFileLocator ) {
16+ class ImportExportManager (private val fileLocator : BackupFileLocator ) {
1517 companion object {
16- const val TAG = " ContentSetManager "
18+ const val TAG = " ImportExportManager "
1719 }
1820
1921 /* *
@@ -23,27 +25,41 @@ class ImportExportManager(private val fileLocator: NewPipeFileLocator) {
2325 @Throws(Exception ::class )
2426 fun exportDatabase (preferences : SharedPreferences , file : StoredFileHelper ) {
2527 file.create()
26- ZipOutputStream (SharpOutputStream (file.stream).buffered())
27- .use { outZip ->
28- ZipHelper .addFileToZip(outZip, fileLocator.db.path, " newpipe.db" )
28+ ZipOutputStream (SharpOutputStream (file.stream).buffered()).use { outZip ->
29+ try {
30+ // add the database
31+ ZipHelper .addFileToZip(
32+ outZip,
33+ BackupFileLocator .FILE_NAME_DB ,
34+ fileLocator.db.path,
35+ )
2936
30- try {
31- ObjectOutputStream (fileLocator.settings.outputStream()).use { output ->
37+ // add the legacy vulnerable serialized preferences (will be removed in the future)
38+ ZipHelper .addFileToZip(
39+ outZip,
40+ BackupFileLocator .FILE_NAME_SERIALIZED_PREFS
41+ ) { byteOutput ->
42+ ObjectOutputStream (byteOutput).use { output ->
3243 output.writeObject(preferences.all)
3344 output.flush()
3445 }
35- } catch (e: IOException ) {
36- if (DEBUG ) {
37- Log .e(TAG , " Unable to exportDatabase" , e)
38- }
3946 }
4047
41- ZipHelper .addFileToZip(outZip, fileLocator.settings.path, " newpipe.settings" )
48+ // add the JSON preferences
49+ ZipHelper .addFileToZip(
50+ outZip,
51+ BackupFileLocator .FILE_NAME_JSON_PREFS
52+ ) { byteOutput ->
53+ JsonWriter
54+ .indent(" " )
55+ .on(byteOutput)
56+ .`object `(preferences.all)
57+ .done()
58+ }
59+ } catch (e: Exception ) {
60+ Log .e(TAG , " Unable to export serialized settings" , e)
4261 }
43- }
44-
45- fun deleteSettingsFile () {
46- fileLocator.settings.delete()
62+ }
4763 }
4864
4965 /* *
@@ -56,7 +72,12 @@ class ImportExportManager(private val fileLocator: NewPipeFileLocator) {
5672 }
5773
5874 fun extractDb (file : StoredFileHelper ): Boolean {
59- val success = ZipHelper .extractFileFromZip(file, fileLocator.db.path, " newpipe.db" )
75+ val success = ZipHelper .extractFileFromZip(
76+ file,
77+ BackupFileLocator .FILE_NAME_DB ,
78+ fileLocator.db.path,
79+ )
80+
6081 if (success) {
6182 fileLocator.dbJournal.delete()
6283 fileLocator.dbWal.delete()
@@ -66,48 +87,81 @@ class ImportExportManager(private val fileLocator: NewPipeFileLocator) {
6687 return success
6788 }
6889
69- fun extractSettings (file : StoredFileHelper ): Boolean {
70- return ZipHelper .extractFileFromZip(file, fileLocator.settings.path, " newpipe.settings" )
90+ @Deprecated(
91+ " Serializing preferences with Java's ObjectOutputStream is vulnerable to injections" ,
92+ replaceWith = ReplaceWith (" exportHasJsonPrefs" )
93+ )
94+ fun exportHasSerializedPrefs (zipFile : StoredFileHelper ): Boolean {
95+ return ZipHelper .zipContainsFile(zipFile, BackupFileLocator .FILE_NAME_SERIALIZED_PREFS )
96+ }
97+
98+ fun exportHasJsonPrefs (zipFile : StoredFileHelper ): Boolean {
99+ return ZipHelper .zipContainsFile(zipFile, BackupFileLocator .FILE_NAME_JSON_PREFS )
71100 }
72101
73102 /* *
74103 * Remove all shared preferences from the app and load the preferences supplied to the manager.
75104 */
105+ @Deprecated(
106+ " Serializing preferences with Java's ObjectOutputStream is vulnerable to injections" ,
107+ replaceWith = ReplaceWith (" loadJsonPrefs" )
108+ )
76109 @Throws(IOException ::class , ClassNotFoundException ::class )
77- fun loadSharedPreferences ( preferences : SharedPreferences ) {
78- val preferenceEditor = preferences.edit()
79-
80- PreferencesObjectInputStream (
81- fileLocator.settings.inputStream ()
82- ).use { input ->
83- preferenceEditor.clear()
84- @Suppress( " UNCHECKED_CAST " )
85- val entries = input.readObject() as Map < String , * >
86- for (( key, value) in entries) {
87- when ( value) {
88- is Boolean -> {
89- preferenceEditor.putBoolean (key, value)
90- }
91- is Float -> {
92- preferenceEditor.putFloat(key, value)
93- }
94- is Int -> {
95- preferenceEditor.putInt(key, value)
110+ fun loadSerializedPrefs ( zipFile : StoredFileHelper , preferences : SharedPreferences ) {
111+ ZipHelper .extractFileFromZip(zipFile, BackupFileLocator . FILE_NAME_SERIALIZED_PREFS ) {
112+ PreferencesObjectInputStream (it).use { input ->
113+ val editor = preferences.edit()
114+ editor.clear ()
115+ @Suppress( " UNCHECKED_CAST " )
116+ val entries = input.readObject() as Map < String , * >
117+ for ((key, value) in entries) {
118+ when (value) {
119+ is Boolean -> editor.putBoolean( key, value)
120+ is Float -> editor.putFloat(key, value)
121+ is Int -> editor.putInt(key, value)
122+ is Long -> editor.putLong (key, value)
123+ is String -> editor.putString(key, value)
124+ is Set < * > -> {
125+ // There are currently only Sets with type String possible
126+ @Suppress( " UNCHECKED_CAST " )
127+ editor.putStringSet(key, value as Set < String > ? )
128+ }
96129 }
97- is Long -> {
98- preferenceEditor.putLong(key, value)
99- }
100- is String -> {
101- preferenceEditor.putString(key, value)
102- }
103- is Set <* > -> {
104- // There are currently only Sets with type String possible
105- @Suppress(" UNCHECKED_CAST" )
106- preferenceEditor.putStringSet(key, value as Set <String >? )
130+ }
131+
132+ if (! editor.commit()) {
133+ Log .e(TAG , " Unable to loadSerializedPrefs" )
134+ }
135+ }
136+ }
137+ }
138+
139+ /* *
140+ * Remove all shared preferences from the app and load the preferences supplied to the manager.
141+ */
142+ @Throws(JsonParserException ::class )
143+ fun loadJsonPrefs (zipFile : StoredFileHelper , preferences : SharedPreferences ) {
144+ ZipHelper .extractFileFromZip(zipFile, BackupFileLocator .FILE_NAME_JSON_PREFS ) {
145+ val editor = preferences.edit()
146+ editor.clear()
147+
148+ val jsonObject = JsonParser .`object `().from(it)
149+ for ((key, value) in jsonObject) {
150+ when (value) {
151+ is Boolean -> editor.putBoolean(key, value)
152+ is Float -> editor.putFloat(key, value)
153+ is Int -> editor.putInt(key, value)
154+ is Long -> editor.putLong(key, value)
155+ is String -> editor.putString(key, value)
156+ is JsonArray -> {
157+ editor.putStringSet(key, value.mapNotNull { e -> e as ? String }.toSet())
107158 }
108159 }
109160 }
110- preferenceEditor.commit()
161+
162+ if (! editor.commit()) {
163+ Log .e(TAG , " Unable to loadJsonPrefs" )
164+ }
111165 }
112166 }
113167}
0 commit comments