twonly-app/lib/src/services/backup/restore.backup.dart
otsmr cc0b88718b
Some checks are pending
Flutter analyze & test / flutter_analyze_and_test (push) Waiting to run
fix: media file appears as a white square and is not listed.
2026-03-14 22:18:29 +01:00

117 lines
3.3 KiB
Dart

// ignore_for_file: avoid_dynamic_calls
import 'dart:convert';
import 'dart:io';
import 'package:cryptography_flutter_plus/cryptography_flutter_plus.dart';
import 'package:cryptography_plus/cryptography_plus.dart';
import 'package:drift/drift.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:http/http.dart' as http;
import 'package:path/path.dart';
import 'package:twonly/globals.dart';
import 'package:twonly/src/constants/secure_storage_keys.dart';
import 'package:twonly/src/model/json/userdata.dart';
import 'package:twonly/src/model/protobuf/client/generated/backup.pb.dart';
import 'package:twonly/src/services/backup/common.backup.dart';
import 'package:twonly/src/utils/log.dart';
import 'package:twonly/src/utils/storage.dart';
Future<void> recoverBackup(
String username,
String password,
BackupServer? server,
) async {
final (backupId, encryptionKey) = await getMasterKey(password, username);
final backupServerUrl = await getTwonlySafeBackupUrlFromServer(
backupId,
server,
);
if (backupServerUrl == null) {
Log.error('Could not create backup url');
throw Exception('Could not create backup server url');
}
late Uint8List backupData;
late http.Response response;
try {
response = await http.get(
Uri.parse(backupServerUrl),
headers: {
HttpHeaders.acceptHeader: 'application/octet-stream',
},
);
} catch (e) {
Log.error('Error fetching backup: $e');
throw Exception('Backup server could not be reached. ($e)');
}
switch (response.statusCode) {
case 200:
backupData = response.bodyBytes;
case 400:
throw Exception('Bad Request: Validation failed.');
case 404:
throw Exception('No backup was found.');
case 429:
throw Exception('Too Many Requests: Rate limit reached.');
default:
throw Exception('Unexpected error: ${response.statusCode}');
}
return handleBackupData(encryptionKey, backupData);
}
Future<void> handleBackupData(
Uint8List encryptionKey,
Uint8List backupData,
) async {
final encryptedBackup = TwonlySafeBackupEncrypted.fromBuffer(
backupData,
);
final secretBox = SecretBox(
encryptedBackup.cipherText,
nonce: encryptedBackup.nonce,
mac: Mac(encryptedBackup.mac),
);
final compressedBytes = await FlutterChacha20.poly1305Aead().decrypt(
secretBox,
secretKey: SecretKeyData(encryptionKey),
);
final plaintextBytes = gzip.decode(compressedBytes);
final backupContent = TwonlySafeBackupContent.fromBuffer(
plaintextBytes,
);
final originalDatabase = File(
join(globalApplicationSupportDirectory, 'twonly.sqlite'),
);
await originalDatabase.writeAsBytes(backupContent.twonlyDatabase);
const storage = FlutterSecureStorage();
final secureStorage = jsonDecode(backupContent.secureStorageJson);
await storage.write(
key: SecureStorageKeys.signalIdentity,
value: secureStorage[SecureStorageKeys.signalIdentity] as String,
);
await storage.write(
key: SecureStorageKeys.signalSignedPreKey,
value: secureStorage[SecureStorageKeys.signalSignedPreKey] as String,
);
await storage.write(
key: SecureStorageKeys.userData,
value: secureStorage[SecureStorageKeys.userData] as String,
);
await updateUserdata((u) {
u.deviceId += 1;
return u;
});
}