Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update to version 1.3.3 #32

Merged
merged 16 commits into from
Nov 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions BackupApp/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ android {
applicationId "org.secuso.privacyfriendlybackup"
minSdkVersion 21
targetSdkVersion 34
versionCode 6
versionName "1.3.2"
versionCode 7
versionName "1.3.3"

testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"

Expand Down Expand Up @@ -82,7 +82,7 @@ android {
androidTest.assets.srcDirs += files("$projectDir/schemas".toString())
}
lint {
abortOnError false
lintConfig = file("lint.xml")
}
namespace 'org.secuso.privacyfriendlybackup'
}
Expand Down
11 changes: 11 additions & 0 deletions BackupApp/lint.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<lint>
<!-- Disable the NotificationPermission check for glide -->
<issue id="NotificationPermission">
<ignore regexp="com.bumptech.glide.request.target.NotificationTarget" />
</issue>

<!-- Set the severity of missing translations to warning instead of error -->
<issue id="MissingTranslation" severity="warning" />
<issue id="MissingQuantity" severity="warning" />
</lint>
Original file line number Diff line number Diff line change
Expand Up @@ -7,25 +7,68 @@ import android.util.JsonWriter
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import org.secuso.privacyfriendlybackup.data.BackupDataStorageRepository
import java.io.BufferedWriter
import java.io.IOException
import java.io.OutputStreamWriter
import java.text.FieldPosition
import java.text.SimpleDateFormat
import java.util.Calendar
import java.util.Locale
import java.util.zip.ZipEntry
import java.util.zip.ZipOutputStream

class DataExporter {
companion object {
suspend fun exportData(context: Context, uri: Uri, data: BackupDataStorageRepository.BackupData) : Boolean{
suspend fun exportDataZip(context: Context, uri: Uri, data: Set<BackupDataStorageRepository.BackupData>): Boolean {
return withContext(Dispatchers.IO) {
try {
ParcelFileDescriptor.AutoCloseOutputStream(
context.contentResolver.openFileDescriptor(uri, "w")
).use { out ->
JsonWriter(OutputStreamWriter(out, Charsets.UTF_8)).use { writer ->
writer.setIndent(" ")
writer.beginObject()
writeData(writer, data)
writer.endObject()
ZipOutputStream(ParcelFileDescriptor.AutoCloseOutputStream(context.contentResolver.openFileDescriptor(uri, "w"))).apply {
setLevel(5)
setComment("PFA Backup Export")
}.use { zipOut ->
data.forEach { backupData ->
val zipEntry = ZipEntry(getSingleExportFileName(backupData, backupData.encrypted))
zipOut.putNextEntry(zipEntry)

val osw = OutputStreamWriter(zipOut, Charsets.UTF_8)
val bw = BufferedWriter(osw)
val jw = JsonWriter(bw)
jw.let { writer ->
writer.setIndent(" ")
writer.beginObject()
writeData(writer, backupData)
writer.endObject()
}
jw.flush()
bw.flush()
osw.flush()

zipOut.closeEntry()
}
}
} catch (e : IOException) {
} catch (e: IOException) {
e.printStackTrace()
return@withContext false
}
return@withContext true
}
}

suspend fun exportData(context: Context, uri: Uri, data: BackupDataStorageRepository.BackupData): Boolean {
return withContext(Dispatchers.IO) {
try {
JsonWriter(
OutputStreamWriter(
ParcelFileDescriptor.AutoCloseOutputStream(context.contentResolver.openFileDescriptor(uri, "w")),
Charsets.UTF_8
)
).use { writer ->
writer.setIndent(" ")
writer.beginObject()
writeData(writer, data)
writer.endObject()
}
} catch (e: IOException) {
return@withContext false
}
return@withContext true
Expand All @@ -39,5 +82,40 @@ class DataExporter {
writer.name("encrypted").value(metaData.encrypted)
writer.name("data").value(String(metaData.data!!))
}

/**
* Filename for export containing multiple backups (.zip)
*/
fun getMultipleExportFileName(): String {
val sb = StringBuffer()
sb.append("PfaBackup_")
val sdf = SimpleDateFormat("yyyyMMdd'T'HHmmss'Z'", Locale.getDefault())
sdf.format(Calendar.getInstance().time, sb, FieldPosition(SimpleDateFormat.DATE_FIELD))
sb.append(".zip")
return sb.toString()
}

/**
* Filename for single Backup export (.backup)
*/
fun getSingleExportFileName(backupMetaData: BackupDataStorageRepository.BackupData, encrypted: Boolean): String {
return if (backupMetaData.encrypted && encrypted) {
getEncryptedFilename(backupMetaData.filename)
} else {
getUnencryptedFilename(backupMetaData.filename)
}
}


private fun getUnencryptedFilename(filename: String) =
filename.replace("_encrypted.backup", ".backup")

private fun getEncryptedFilename(filename: String): String {
return if (filename.contains("_encrypted.backup", true)) {
filename
} else {
filename.replace(".backup", "_encrypted.backup", true)
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,66 @@ import org.secuso.privacyfriendlybackup.data.BackupDataStorageRepository
import org.secuso.privacyfriendlybackup.data.room.model.enums.StorageType
import java.io.IOException
import java.io.InputStreamReader
import java.util.*
import java.util.Date
import java.util.LinkedList
import java.util.zip.ZipInputStream


class DataImporter {

companion object {
suspend fun importData(context: Context, uri: Uri) : Pair<Boolean, BackupDataStorageRepository.BackupData?> {
suspend fun importDataZip(context: Context, uri: Uri): List<Pair<Boolean, Long?>>? {
return withContext(IO) {
var backupData : BackupDataStorageRepository.BackupData? = null
val descriptor = context.contentResolver.openFileDescriptor(uri, "r")
val inStream = ParcelFileDescriptor.AutoCloseInputStream(descriptor)
val zipInStream = ZipInputStream(inStream)
val result: MutableList<Pair<Boolean, Long?>> = LinkedList()

try {
zipInStream.use { zipInputStream ->
generateSequence { zipInputStream.nextEntry }
.filterNot { it.isDirectory }
.forEach { _ ->
var backupData: BackupDataStorageRepository.BackupData? = null
val isr = InputStreamReader(zipInputStream, Charsets.UTF_8)
val jr = JsonReader(isr)
jr.let { reader ->
reader.beginObject()
backupData = readData(reader)
reader.endObject()
}
if (backupData != null) {

if (!backupData!!.encrypted) {
// short validation check if the json is valid
if (!isValidJSON(String(backupData!!.data!!))) {
result.add(false to null)
}
}

result.add(
BackupDataStorageRepository.getInstance(context).storeFile(
context,
backupData!!
)
)
} else {
result.add(false to null)
}
}
}
} catch (e: MalformedJsonException) {
return@withContext null
} catch (e: IOException) {
return@withContext null
}
return@withContext result
}
}

suspend fun importData(context: Context, uri: Uri): Pair<Boolean, BackupDataStorageRepository.BackupData?> {
return withContext(IO) {
var backupData: BackupDataStorageRepository.BackupData? = null

val descriptor = context.contentResolver.openFileDescriptor(uri, "r")
val inStream = ParcelFileDescriptor.AutoCloseInputStream(descriptor)
Expand All @@ -40,13 +91,13 @@ class DataImporter {
return@withContext false to null
}

val result : Pair<Boolean, Long>
val result: Pair<Boolean, Long>

if(backupData != null) {
if (backupData != null) {

if(!backupData!!.encrypted) {
if (!backupData!!.encrypted) {
// short validation check if the json is valid
if(!isValidJSON(String(backupData!!.data!!))) {
if (!isValidJSON(String(backupData!!.data!!))) {
return@withContext false to null
}
}
Expand Down Expand Up @@ -75,17 +126,17 @@ class DataImporter {
}
}

private fun readData(reader: JsonReader) : BackupDataStorageRepository.BackupData? {
var filename : String? = null
var packageName : String? = null
var timestamp : Long? = null
var encrypted : Boolean? = null
var data : String? = null
private fun readData(reader: JsonReader): BackupDataStorageRepository.BackupData? {
var filename: String? = null
var packageName: String? = null
var timestamp: Long? = null
var encrypted: Boolean? = null
var data: String? = null

while (reader.hasNext()) {
val nextName = reader.nextName()

when(nextName) {
when (nextName) {
"filename" -> filename = reader.nextString()
"packageName" -> packageName = reader.nextString()
"timestamp" -> timestamp = reader.nextLong()
Expand All @@ -95,11 +146,12 @@ class DataImporter {
}
}

if(TextUtils.isEmpty(filename)
if (TextUtils.isEmpty(filename)
|| TextUtils.isEmpty(packageName)
|| TextUtils.isEmpty(data)
|| timestamp == null
|| encrypted == null) {
|| encrypted == null
) {
return null
}

Expand Down
Loading
Loading