change aes, now more secure, beacuse no hardcode anymore
This commit is contained in:
@ -1,56 +1,73 @@
|
|||||||
|
// app/src/main/java/com/dano/test1/AES256Helper.kt
|
||||||
|
package com.dano.test1
|
||||||
|
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.FileInputStream
|
import java.security.SecureRandom
|
||||||
import java.io.FileOutputStream
|
|
||||||
import javax.crypto.Cipher
|
import javax.crypto.Cipher
|
||||||
import javax.crypto.CipherInputStream
|
import javax.crypto.Mac
|
||||||
import javax.crypto.CipherOutputStream
|
|
||||||
import javax.crypto.spec.IvParameterSpec
|
import javax.crypto.spec.IvParameterSpec
|
||||||
import javax.crypto.spec.SecretKeySpec
|
import javax.crypto.spec.SecretKeySpec
|
||||||
import kotlin.random.Random
|
import kotlin.math.min
|
||||||
|
|
||||||
object AES256Helper {
|
object AES256Helper {
|
||||||
|
|
||||||
private const val TRANSFORMATION = "AES/CBC/PKCS5Padding"
|
// HKDF-SHA256: IKM = tokenHex->bytes, salt="", info="qdb-aes", len=32
|
||||||
private const val ALGORITHM = "AES"
|
private fun hkdfFromToken(tokenHex: String, info: String = "qdb-aes", len: Int = 32): ByteArray {
|
||||||
private const val IV_SIZE = 16
|
val ikm = hexToBytes(tokenHex)
|
||||||
|
val mac = Mac.getInstance("HmacSHA256")
|
||||||
|
val zeroSalt = ByteArray(32) { 0 }
|
||||||
|
mac.init(SecretKeySpec(zeroSalt, "HmacSHA256"))
|
||||||
|
val prk = mac.doFinal(ikm)
|
||||||
|
|
||||||
// Beispiel-Key: 32 Bytes = 256 bit. Ersetze das durch deinen eigenen sicheren Schlüssel!
|
var previous = ByteArray(0)
|
||||||
private val keyBytes = "12345678901234567890123456789012".toByteArray(Charsets.UTF_8)
|
val okm = ByteArray(len)
|
||||||
private val secretKey = SecretKeySpec(keyBytes, ALGORITHM)
|
var generated = 0
|
||||||
|
var counter = 1
|
||||||
// Verschlüsseln: InputFile -> OutputFile (mit zufälligem IV vorne in der Datei)
|
while (generated < len) {
|
||||||
fun encryptFile(inputFile: File, outputFile: File) {
|
mac.init(SecretKeySpec(prk, "HmacSHA256"))
|
||||||
val iv = ByteArray(IV_SIZE)
|
mac.update(previous)
|
||||||
Random.nextBytes(iv)
|
mac.update(info.toByteArray(Charsets.UTF_8))
|
||||||
val ivSpec = IvParameterSpec(iv)
|
mac.update(counter.toByte())
|
||||||
val cipher = Cipher.getInstance(TRANSFORMATION)
|
val t = mac.doFinal()
|
||||||
cipher.init(Cipher.ENCRYPT_MODE, secretKey, ivSpec)
|
val toCopy = min(len - generated, t.size)
|
||||||
|
System.arraycopy(t, 0, okm, generated, toCopy)
|
||||||
FileOutputStream(outputFile).use { fileOut ->
|
previous = t
|
||||||
// IV vorne reinschreiben
|
generated += toCopy
|
||||||
fileOut.write(iv)
|
counter++
|
||||||
CipherOutputStream(fileOut, cipher).use { cipherOut ->
|
|
||||||
FileInputStream(inputFile).use { fileIn ->
|
|
||||||
fileIn.copyTo(cipherOut)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
return okm
|
||||||
}
|
}
|
||||||
|
|
||||||
// Entschlüsseln: InputFile (IV+Ciphertext) -> OutputFile (Klartext)
|
private fun hexToBytes(hex: String): ByteArray {
|
||||||
fun decryptFile(inputFile: File, outputFile: File) {
|
val clean = hex.trim()
|
||||||
FileInputStream(inputFile).use { fileIn ->
|
val len = clean.length
|
||||||
val iv = ByteArray(IV_SIZE)
|
val out = ByteArray(len / 2)
|
||||||
if (fileIn.read(iv) != IV_SIZE) throw IllegalArgumentException("Ungültige Datei oder IV fehlt")
|
var i = 0
|
||||||
val ivSpec = IvParameterSpec(iv)
|
while (i < len) {
|
||||||
val cipher = Cipher.getInstance(TRANSFORMATION)
|
out[i / 2] = ((Character.digit(clean[i], 16) shl 4) + Character.digit(clean[i + 1], 16)).toByte()
|
||||||
cipher.init(Cipher.DECRYPT_MODE, secretKey, ivSpec)
|
i += 2
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
CipherInputStream(fileIn, cipher).use { cipherIn ->
|
fun encryptFileWithToken(inFile: File, outFile: File, token: String) {
|
||||||
FileOutputStream(outputFile).use { fileOut ->
|
val key = hkdfFromToken(token)
|
||||||
cipherIn.copyTo(fileOut)
|
val iv = ByteArray(16).also { SecureRandom().nextBytes(it) }
|
||||||
}
|
val cipher = Cipher.getInstance("AES/CBC/PKCS5Padding")
|
||||||
}
|
cipher.init(Cipher.ENCRYPT_MODE, SecretKeySpec(key, "AES"), IvParameterSpec(iv))
|
||||||
}
|
val plain = inFile.readBytes()
|
||||||
|
val enc = cipher.doFinal(plain)
|
||||||
|
outFile.writeBytes(iv + enc)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun decryptFileWithToken(inFile: File, token: String): ByteArray {
|
||||||
|
val key = hkdfFromToken(token)
|
||||||
|
val data = inFile.readBytes()
|
||||||
|
require(data.size >= 16) { "cipher too short" }
|
||||||
|
val iv = data.copyOfRange(0, 16)
|
||||||
|
val ct = data.copyOfRange(16, data.size)
|
||||||
|
val cipher = Cipher.getInstance("AES/CBC/PKCS5Padding")
|
||||||
|
cipher.init(Cipher.DECRYPT_MODE, SecretKeySpec(key, "AES"), IvParameterSpec(iv))
|
||||||
|
return cipher.doFinal(ct)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
// app/src/main/java/com/dano/test1/DatabaseDownloader.kt
|
||||||
package com.dano.test1
|
package com.dano.test1
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
@ -9,28 +10,17 @@ import okhttp3.OkHttpClient
|
|||||||
import okhttp3.Request
|
import okhttp3.Request
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.FileOutputStream
|
import java.io.FileOutputStream
|
||||||
import javax.crypto.Cipher
|
|
||||||
import javax.crypto.spec.IvParameterSpec
|
|
||||||
import javax.crypto.spec.SecretKeySpec
|
|
||||||
|
|
||||||
object DatabaseDownloader {
|
object DatabaseDownloader {
|
||||||
|
|
||||||
private const val DB_NAME = "questionnaire_database"
|
private const val DB_NAME = "questionnaire_database"
|
||||||
private const val SERVER_DOWNLOAD_URL = "http://49.13.157.44/downloadFull.php"
|
private const val SERVER_DOWNLOAD_URL = "http://49.13.157.44/downloadFull.php"
|
||||||
|
|
||||||
// AES-256 Key (muss exakt 32 Bytes lang sein)
|
|
||||||
private const val AES_KEY = "12345678901234567890123456789012"
|
|
||||||
|
|
||||||
private val client = OkHttpClient()
|
private val client = OkHttpClient()
|
||||||
|
|
||||||
/**
|
|
||||||
* Startet den Download und Austausch der DB, benötigt gültiges Token
|
|
||||||
*/
|
|
||||||
fun downloadAndReplaceDatabase(context: Context, token: String) {
|
fun downloadAndReplaceDatabase(context: Context, token: String) {
|
||||||
CoroutineScope(Dispatchers.IO).launch {
|
CoroutineScope(Dispatchers.IO).launch {
|
||||||
try {
|
try {
|
||||||
Log.d("DOWNLOAD", "Download gestartet: $SERVER_DOWNLOAD_URL")
|
|
||||||
|
|
||||||
val request = Request.Builder()
|
val request = Request.Builder()
|
||||||
.url(SERVER_DOWNLOAD_URL)
|
.url(SERVER_DOWNLOAD_URL)
|
||||||
.header("Authorization", "Bearer $token")
|
.header("Authorization", "Bearer $token")
|
||||||
@ -38,47 +28,24 @@ object DatabaseDownloader {
|
|||||||
|
|
||||||
val response = client.newCall(request).execute()
|
val response = client.newCall(request).execute()
|
||||||
if (!response.isSuccessful) {
|
if (!response.isSuccessful) {
|
||||||
Log.e("DOWNLOAD", "Fehler beim Download: ${response.code}")
|
Log.e("DOWNLOAD", "HTTP ${response.code}")
|
||||||
return@launch
|
return@launch
|
||||||
}
|
}
|
||||||
|
|
||||||
// Zwischenspeichern der verschlüsselten Datei
|
val encFile = File(context.cacheDir, "downloaded_database.enc")
|
||||||
val downloadedFile = File(context.cacheDir, "downloaded_database.enc")
|
|
||||||
response.body?.byteStream()?.use { input ->
|
response.body?.byteStream()?.use { input ->
|
||||||
FileOutputStream(downloadedFile).use { output ->
|
FileOutputStream(encFile).use { output -> input.copyTo(output) }
|
||||||
input.copyTo(output)
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
Log.d("DOWNLOAD", "Datei gespeichert: ${downloadedFile.absolutePath}")
|
|
||||||
|
|
||||||
// Entschlüsselung
|
val decryptedBytes = AES256Helper.decryptFileWithToken(encFile, token)
|
||||||
val decryptedBytes = decryptFile(downloadedFile)
|
|
||||||
val dbFile = context.getDatabasePath(DB_NAME)
|
val dbFile = context.getDatabasePath(DB_NAME)
|
||||||
if (dbFile.exists()) dbFile.delete()
|
if (dbFile.exists()) dbFile.delete()
|
||||||
FileOutputStream(dbFile).use { fos ->
|
FileOutputStream(dbFile).use { it.write(decryptedBytes) }
|
||||||
fos.write(decryptedBytes)
|
|
||||||
}
|
|
||||||
|
|
||||||
Log.d("DOWNLOAD", "Neue DB erfolgreich entschlüsselt und eingesetzt")
|
|
||||||
|
|
||||||
|
Log.d("DOWNLOAD", "DB erfolgreich ersetzt")
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Log.e("DOWNLOAD", "Fehler beim Download oder Ersetzen der DB", e)
|
Log.e("DOWNLOAD", "Fehler", e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun decryptFile(file: File): ByteArray {
|
|
||||||
val fileBytes = file.readBytes()
|
|
||||||
if (fileBytes.size < 16) throw IllegalArgumentException("Datei zu kurz, kein IV vorhanden")
|
|
||||||
|
|
||||||
val iv = fileBytes.copyOfRange(0, 16)
|
|
||||||
val cipherBytes = fileBytes.copyOfRange(16, fileBytes.size)
|
|
||||||
|
|
||||||
val cipher = Cipher.getInstance("AES/CBC/PKCS5Padding")
|
|
||||||
val keySpec = SecretKeySpec(AES_KEY.toByteArray(Charsets.UTF_8), "AES")
|
|
||||||
val ivSpec = IvParameterSpec(iv)
|
|
||||||
cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec)
|
|
||||||
|
|
||||||
return cipher.doFinal(cipherBytes)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
// app/src/main/java/com/dano/test1/DatabaseUploader.kt
|
||||||
package com.dano.test1
|
package com.dano.test1
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
@ -25,181 +26,111 @@ object DatabaseUploader {
|
|||||||
|
|
||||||
private val client = OkHttpClient()
|
private val client = OkHttpClient()
|
||||||
|
|
||||||
/**
|
|
||||||
* Startet den Upload mit Login über LoginManager.
|
|
||||||
* @param context Android Context
|
|
||||||
* @param password Vom User eingegebenes Passwort
|
|
||||||
*/
|
|
||||||
fun uploadDatabaseWithLogin(context: Context, password: String) {
|
fun uploadDatabaseWithLogin(context: Context, password: String) {
|
||||||
LoginManager.loginUser(context, password,
|
LoginManager.loginUser(context, password,
|
||||||
onSuccess = { token ->
|
onSuccess = { token ->
|
||||||
Log.d("UPLOAD", "Login erfolgreich, Token erhalten")
|
Log.d("UPLOAD", "Login OK")
|
||||||
uploadDatabase(context, token)
|
uploadDatabase(context, token)
|
||||||
},
|
},
|
||||||
onError = { errorMsg ->
|
onError = { msg -> Log.e("UPLOAD", "Login fehlgeschlagen: $msg") }
|
||||||
Log.e("UPLOAD", "Login fehlgeschlagen: $errorMsg")
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Interner Upload, benötigt gültiges Token
|
|
||||||
*/
|
|
||||||
private fun uploadDatabase(context: Context, token: String) {
|
private fun uploadDatabase(context: Context, token: String) {
|
||||||
CoroutineScope(Dispatchers.IO).launch {
|
CoroutineScope(Dispatchers.IO).launch {
|
||||||
try {
|
try {
|
||||||
val dbFile = context.getDatabasePath(DB_NAME)
|
val dbFile = context.getDatabasePath(DB_NAME)
|
||||||
if (!dbFile.exists()) {
|
if (!dbFile.exists()) {
|
||||||
Log.e("UPLOAD", "Datenbankdatei existiert nicht: ${dbFile.absolutePath}")
|
Log.e("UPLOAD", "DB fehlt: ${dbFile.absolutePath}")
|
||||||
return@launch
|
return@launch
|
||||||
}
|
}
|
||||||
|
|
||||||
// WAL-Checkpoint
|
|
||||||
try {
|
try {
|
||||||
val db = SQLiteDatabase.openDatabase(
|
val db = SQLiteDatabase.openDatabase(dbFile.absolutePath, null, SQLiteDatabase.OPEN_READWRITE)
|
||||||
dbFile.absolutePath,
|
db.rawQuery("PRAGMA wal_checkpoint(FULL);", null).use { /* ignore */ }
|
||||||
null,
|
|
||||||
SQLiteDatabase.OPEN_READWRITE
|
|
||||||
)
|
|
||||||
db.rawQuery("PRAGMA wal_checkpoint(FULL);", null).use { cursor ->
|
|
||||||
if (cursor.moveToFirst()) {
|
|
||||||
try { Log.d("UPLOAD", "WAL-Checkpoint result: ${cursor.getInt(0)}") } catch (_: Exception) {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
db.close()
|
db.close()
|
||||||
Log.d("UPLOAD", "WAL-Checkpoint erfolgreich.")
|
} catch (_: Exception) { }
|
||||||
} catch (e: Exception) {
|
|
||||||
Log.e("UPLOAD", "Fehler beim WAL-Checkpoint", e)
|
|
||||||
}
|
|
||||||
|
|
||||||
val exists = checkDatabaseExists()
|
|
||||||
if (exists) {
|
|
||||||
Log.d("UPLOAD", "Server-Datenbank vorhanden → Delta-Upload")
|
|
||||||
} else {
|
|
||||||
Log.d("UPLOAD", "Keine Server-Datenbank → Delta-Upload")
|
|
||||||
}
|
|
||||||
|
|
||||||
|
checkDatabaseExists() // nur Logging
|
||||||
uploadPseudoDelta(context, dbFile, token)
|
uploadPseudoDelta(context, dbFile, token)
|
||||||
|
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Log.e("UPLOAD", "Fehler beim Hochladen der DB", e)
|
Log.e("UPLOAD", "Fehler", e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun checkDatabaseExists(): Boolean {
|
private fun checkDatabaseExists(): Boolean {
|
||||||
return try {
|
return try {
|
||||||
val request = Request.Builder()
|
val req = Request.Builder().url(SERVER_CHECK_URL).get().build()
|
||||||
.url(SERVER_CHECK_URL)
|
client.newCall(req).execute().use { resp ->
|
||||||
.get()
|
if (!resp.isSuccessful) return false
|
||||||
.build()
|
val body = resp.body?.string() ?: return false
|
||||||
|
try { JSONObject(body).optBoolean("exists", false) } catch (_: Exception) { false }
|
||||||
client.newCall(request).execute().use { response ->
|
|
||||||
if (!response.isSuccessful) {
|
|
||||||
Log.e("UPLOAD", "checkDatabaseExists HTTP error: ${response.code}")
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
val body = response.body?.string() ?: return false
|
|
||||||
try {
|
|
||||||
val j = JSONObject(body)
|
|
||||||
j.optBoolean("exists", false)
|
|
||||||
} catch (e: Exception) {
|
|
||||||
body.contains("exists", ignoreCase = true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (e: Exception) {
|
|
||||||
Log.e("UPLOAD", "Fehler bei Server-Prüfung", e)
|
|
||||||
false
|
|
||||||
}
|
}
|
||||||
|
} catch (e: Exception) { false }
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun uploadPseudoDelta(context: Context, file: File, token: String) {
|
private fun uploadPseudoDelta(context: Context, file: File, token: String) {
|
||||||
try {
|
try {
|
||||||
val db = SQLiteDatabase.openDatabase(file.absolutePath, null, SQLiteDatabase.OPEN_READONLY)
|
val db = SQLiteDatabase.openDatabase(file.absolutePath, null, SQLiteDatabase.OPEN_READONLY)
|
||||||
|
|
||||||
val data = JSONObject().apply {
|
val data = JSONObject().apply {
|
||||||
put("clients", queryToJsonArray(db, "SELECT clientCode FROM clients"))
|
put("clients", queryToJsonArray(db, "SELECT clientCode FROM clients"))
|
||||||
put("questionnaires", queryToJsonArray(db, "SELECT id FROM questionnaires"))
|
put("questionnaires", queryToJsonArray(db, "SELECT id FROM questionnaires"))
|
||||||
put("questions", queryToJsonArray(db, "SELECT questionId, questionnaireId, question FROM questions"))
|
put("questions", queryToJsonArray(db, "SELECT questionId, questionnaireId, question FROM questions"))
|
||||||
put("answers", queryToJsonArray(db, "SELECT clientCode, questionId, answerValue FROM answers"))
|
put("answers", queryToJsonArray(db, "SELECT clientCode, questionId, answerValue FROM answers"))
|
||||||
put(
|
put("completed_questionnaires",
|
||||||
"completed_questionnaires",
|
queryToJsonArray(db, "SELECT clientCode, questionnaireId, timestamp, isDone, sumPoints FROM completed_questionnaires"))
|
||||||
queryToJsonArray(
|
|
||||||
db,
|
|
||||||
"SELECT clientCode, questionnaireId, timestamp, isDone, sumPoints FROM completed_questionnaires"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
db.close()
|
db.close()
|
||||||
|
|
||||||
val tmpJson = File(context.cacheDir, "payload.json")
|
val tmpJson = File(context.cacheDir, "payload.json").apply { writeText(data.toString()) }
|
||||||
tmpJson.writeText(data.toString())
|
|
||||||
|
|
||||||
val tmpEnc = File(context.cacheDir, "payload.enc")
|
val tmpEnc = File(context.cacheDir, "payload.enc")
|
||||||
try {
|
try {
|
||||||
AES256Helper.encryptFile(tmpJson, tmpEnc)
|
AES256Helper.encryptFileWithToken(tmpJson, tmpEnc, token)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Log.e("UPLOAD", "Fehler bei der Verschlüsselung der JSON-Datei", e)
|
Log.e("UPLOAD", "Verschlüsselung fehlgeschlagen", e)
|
||||||
tmpJson.delete()
|
tmpJson.delete(); return
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val requestBody = MultipartBody.Builder()
|
val body = MultipartBody.Builder()
|
||||||
.setType(MultipartBody.FORM)
|
.setType(MultipartBody.FORM)
|
||||||
.addFormDataPart("token", token) // Token vom Login
|
.addFormDataPart("token", token)
|
||||||
.addFormDataPart(
|
.addFormDataPart("file", "payload.enc", tmpEnc.asRequestBody("application/octet-stream".toMediaType()))
|
||||||
"file",
|
|
||||||
"payload.enc",
|
|
||||||
tmpEnc.asRequestBody("application/octet-stream".toMediaType())
|
|
||||||
)
|
|
||||||
.build()
|
|
||||||
|
|
||||||
val request = Request.Builder()
|
|
||||||
.url(SERVER_DELTA_URL)
|
|
||||||
.post(requestBody)
|
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
|
val request = Request.Builder().url(SERVER_DELTA_URL).post(body).build()
|
||||||
client.newCall(request).enqueue(object : Callback {
|
client.newCall(request).enqueue(object : Callback {
|
||||||
override fun onFailure(call: Call, e: IOException) {
|
override fun onFailure(call: Call, e: IOException) {
|
||||||
Log.e("UPLOAD", "Delta-Upload fehlgeschlagen: ${e.message}")
|
Log.e("UPLOAD", "Fehlgeschlagen: ${e.message}")
|
||||||
tmpJson.delete()
|
tmpJson.delete(); tmpEnc.delete()
|
||||||
tmpEnc.delete()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onResponse(call: Call, response: Response) {
|
override fun onResponse(call: Call, response: Response) {
|
||||||
val body = try { response.body?.string() ?: "Keine Response" } catch (e: Exception) {
|
val respBody = try { response.body?.string() ?: "" } catch (_: Exception) { "" }
|
||||||
"Fehler beim Lesen der Response: ${e.message}"
|
|
||||||
}
|
|
||||||
if (response.isSuccessful) {
|
if (response.isSuccessful) {
|
||||||
Log.d("UPLOAD", "Delta-Upload erfolgreich: $body")
|
Log.d("UPLOAD", "OK: $respBody")
|
||||||
if (file.delete()) Log.d("UPLOAD", "Lokale DB gelöscht.") else Log.e("UPLOAD", "Löschen der lokalen DB fehlgeschlagen.")
|
if (!file.delete()) Log.w("UPLOAD", "Lokale DB nicht gelöscht.")
|
||||||
val journalFile = File(file.parent, file.name + "-journal")
|
File(file.parent, file.name + "-journal").delete()
|
||||||
if (journalFile.exists() && journalFile.delete()) Log.d("UPLOAD", "Journal-Datei gelöscht.")
|
|
||||||
tmpJson.delete()
|
|
||||||
tmpEnc.delete()
|
|
||||||
exitProcess(0)
|
|
||||||
} else {
|
} else {
|
||||||
Log.e("UPLOAD", "Delta-Upload fehlgeschlagen: ${response.code} $body")
|
Log.e("UPLOAD", "HTTP ${response.code}: $respBody")
|
||||||
tmpJson.delete()
|
|
||||||
tmpEnc.delete()
|
|
||||||
}
|
}
|
||||||
|
tmpJson.delete(); tmpEnc.delete()
|
||||||
|
try { exitProcess(0) } catch (_: Exception) {}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Log.e("UPLOAD", "Fehler beim Delta-Upload", e)
|
Log.e("UPLOAD", "Exception", e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun queryToJsonArray(db: SQLiteDatabase, query: String): JSONArray {
|
private fun queryToJsonArray(db: SQLiteDatabase, query: String): JSONArray {
|
||||||
val cursor = db.rawQuery(query, null)
|
val c = db.rawQuery(query, null)
|
||||||
val jsonArray = JSONArray()
|
val arr = JSONArray()
|
||||||
cursor.use {
|
c.use {
|
||||||
val columnNames = it.columnNames
|
val cols = it.columnNames
|
||||||
while (it.moveToNext()) {
|
while (it.moveToNext()) {
|
||||||
val obj = JSONObject()
|
val obj = JSONObject()
|
||||||
for (col in columnNames) {
|
for (col in cols) {
|
||||||
val idx = it.getColumnIndex(col)
|
val idx = it.getColumnIndex(col)
|
||||||
if (idx >= 0) {
|
if (idx >= 0) {
|
||||||
when (it.getType(idx)) {
|
when (it.getType(idx)) {
|
||||||
@ -207,17 +138,14 @@ object DatabaseUploader {
|
|||||||
Cursor.FIELD_TYPE_FLOAT -> obj.put(col, it.getDouble(idx))
|
Cursor.FIELD_TYPE_FLOAT -> obj.put(col, it.getDouble(idx))
|
||||||
Cursor.FIELD_TYPE_STRING -> obj.put(col, it.getString(idx))
|
Cursor.FIELD_TYPE_STRING -> obj.put(col, it.getString(idx))
|
||||||
Cursor.FIELD_TYPE_NULL -> obj.put(col, JSONObject.NULL)
|
Cursor.FIELD_TYPE_NULL -> obj.put(col, JSONObject.NULL)
|
||||||
Cursor.FIELD_TYPE_BLOB -> {
|
Cursor.FIELD_TYPE_BLOB -> obj.put(col, Base64.encodeToString(it.getBlob(idx), Base64.NO_WRAP))
|
||||||
val blob = it.getBlob(idx)
|
|
||||||
obj.put(col, Base64.encodeToString(blob, Base64.NO_WRAP))
|
|
||||||
}
|
|
||||||
else -> obj.put(col, it.getString(idx))
|
else -> obj.put(col, it.getString(idx))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
jsonArray.put(obj)
|
arr.put(obj)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return jsonArray
|
return arr
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
// app/src/main/java/com/dano/test1/LoginManager.kt
|
||||||
package com.dano.test1
|
package com.dano.test1
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
@ -13,18 +14,9 @@ import okhttp3.RequestBody.Companion.toRequestBody
|
|||||||
import org.json.JSONObject
|
import org.json.JSONObject
|
||||||
|
|
||||||
object LoginManager {
|
object LoginManager {
|
||||||
|
|
||||||
private const val SERVER_LOGIN_URL = "http://49.13.157.44/login.php"
|
private const val SERVER_LOGIN_URL = "http://49.13.157.44/login.php"
|
||||||
private val client = OkHttpClient()
|
private val client = OkHttpClient()
|
||||||
|
|
||||||
/**
|
|
||||||
* Startet den Login-Prozess.
|
|
||||||
*
|
|
||||||
* @param context Android Context
|
|
||||||
* @param password Vom User eingegebenes Passwort
|
|
||||||
* @param onSuccess Callback mit dem Token wenn Login erfolgreich
|
|
||||||
* @param onError Callback mit Fehlermeldung
|
|
||||||
*/
|
|
||||||
fun loginUser(
|
fun loginUser(
|
||||||
context: Context,
|
context: Context,
|
||||||
password: String,
|
password: String,
|
||||||
@ -46,26 +38,18 @@ object LoginManager {
|
|||||||
|
|
||||||
if (response.isSuccessful && responseText != null) {
|
if (response.isSuccessful && responseText != null) {
|
||||||
val json = JSONObject(responseText)
|
val json = JSONObject(responseText)
|
||||||
if (json.getBoolean("success")) {
|
if (json.optBoolean("success")) {
|
||||||
val token = json.getString("token")
|
val token = json.getString("token")
|
||||||
withContext(Dispatchers.Main) {
|
withContext(Dispatchers.Main) { onSuccess(token) }
|
||||||
onSuccess(token)
|
} else {
|
||||||
|
withContext(Dispatchers.Main) { onError("Login fehlgeschlagen") }
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
withContext(Dispatchers.Main) {
|
withContext(Dispatchers.Main) { onError("Fehler beim Login (${response.code})") }
|
||||||
onError("Login fehlgeschlagen")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
withContext(Dispatchers.Main) {
|
|
||||||
onError("Fehler beim Login (${response.code})")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Log.e("LOGIN", "Exception beim Login", e)
|
Log.e("LOGIN", "Exception", e)
|
||||||
withContext(Dispatchers.Main) {
|
withContext(Dispatchers.Main) { onError("Exception: ${e.message}") }
|
||||||
onError("Exception: ${e.message}")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user