|
|
@ -0,0 +1,163 @@ |
|
|
|
import 'dart:convert'; |
|
|
|
import 'dart:io'; |
|
|
|
|
|
|
|
import 'package:Envelope/utils/storage/session_cookie.dart'; |
|
|
|
import 'package:flutter/material.dart'; |
|
|
|
import 'package:flutter_dotenv/flutter_dotenv.dart'; |
|
|
|
import 'package:pointycastle/impl.dart'; |
|
|
|
import 'package:qr_code_scanner/qr_code_scanner.dart'; |
|
|
|
import 'package:sqflite/sqflite.dart'; |
|
|
|
import 'package:uuid/uuid.dart'; |
|
|
|
import 'package:http/http.dart' as http; |
|
|
|
|
|
|
|
import '/models/friends.dart'; |
|
|
|
import '/models/my_profile.dart'; |
|
|
|
import '/utils/encryption/aes_helper.dart'; |
|
|
|
import '/utils/encryption/crypto_utils.dart'; |
|
|
|
import '/utils/storage/database.dart'; |
|
|
|
import '/utils/strings.dart'; |
|
|
|
import 'flash_message.dart'; |
|
|
|
|
|
|
|
class QrReader extends StatefulWidget { |
|
|
|
const QrReader({ |
|
|
|
Key? key, |
|
|
|
}) : super(key: key); |
|
|
|
|
|
|
|
@override |
|
|
|
State<QrReader> createState() => _QrReaderState(); |
|
|
|
} |
|
|
|
|
|
|
|
class _QrReaderState extends State<QrReader> { |
|
|
|
final GlobalKey qrKey = GlobalKey(debugLabel: 'QR'); |
|
|
|
Barcode? result; |
|
|
|
QRViewController? controller; |
|
|
|
|
|
|
|
// In order to get hot reload to work we need to pause the camera if the platform |
|
|
|
// is android, or resume the camera if the platform is iOS. |
|
|
|
@override |
|
|
|
void reassemble() { |
|
|
|
super.reassemble(); |
|
|
|
if (Platform.isAndroid) { |
|
|
|
controller!.pauseCamera(); |
|
|
|
} else if (Platform.isIOS) { |
|
|
|
controller!.resumeCamera(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
@override |
|
|
|
Widget build(BuildContext context) { |
|
|
|
return Scaffold( |
|
|
|
body: Column( |
|
|
|
children: <Widget>[ |
|
|
|
Expanded( |
|
|
|
flex: 5, |
|
|
|
child: QRView( |
|
|
|
key: qrKey, |
|
|
|
onQRViewCreated: _onQRViewCreated, |
|
|
|
formatsAllowed: const [BarcodeFormat.qrcode], |
|
|
|
overlay: QrScannerOverlayShape(), |
|
|
|
), |
|
|
|
), |
|
|
|
], |
|
|
|
), |
|
|
|
); |
|
|
|
} |
|
|
|
|
|
|
|
void _onQRViewCreated(QRViewController controller) { |
|
|
|
this.controller = controller; |
|
|
|
controller.scannedDataStream.listen((scanData) { |
|
|
|
addFriend(scanData) |
|
|
|
.then((dynamic ret) { |
|
|
|
if (ret) { |
|
|
|
// Delay exit to prevent exit mid way through rendering |
|
|
|
Future.delayed(Duration.zero, () { |
|
|
|
Navigator.of(context).pop(); |
|
|
|
}); |
|
|
|
} |
|
|
|
}); |
|
|
|
}); |
|
|
|
} |
|
|
|
|
|
|
|
@override |
|
|
|
void dispose() { |
|
|
|
controller?.dispose(); |
|
|
|
super.dispose(); |
|
|
|
} |
|
|
|
|
|
|
|
Future<bool> addFriend(Barcode scanData) async { |
|
|
|
Map<String, dynamic> friendJson = jsonDecode(scanData.code!); |
|
|
|
|
|
|
|
RSAPublicKey publicKey = CryptoUtils.rsaPublicKeyFromPem( |
|
|
|
String.fromCharCodes( |
|
|
|
base64.decode( |
|
|
|
friendJson['k'] |
|
|
|
) |
|
|
|
) |
|
|
|
); |
|
|
|
|
|
|
|
MyProfile profile = await MyProfile.getProfile(); |
|
|
|
|
|
|
|
var uuid = const Uuid(); |
|
|
|
|
|
|
|
final symmetricKey1 = AesHelper.deriveKey(generateRandomString(32)); |
|
|
|
final symmetricKey2 = AesHelper.deriveKey(generateRandomString(32)); |
|
|
|
|
|
|
|
Friend request1 = Friend( |
|
|
|
id: uuid.v4(), |
|
|
|
userId: friendJson['i'], |
|
|
|
username: profile.username, |
|
|
|
friendId: profile.id, |
|
|
|
friendSymmetricKey: base64.encode(symmetricKey1), |
|
|
|
publicKey: profile.publicKey!, |
|
|
|
acceptedAt: DateTime.now(), |
|
|
|
); |
|
|
|
|
|
|
|
Friend request2 = Friend( |
|
|
|
id: uuid.v4(), |
|
|
|
userId: profile.id, |
|
|
|
friendId: friendJson['i'], |
|
|
|
username: friendJson['u'], |
|
|
|
friendSymmetricKey: base64.encode(symmetricKey2), |
|
|
|
publicKey: publicKey, |
|
|
|
acceptedAt: DateTime.now(), |
|
|
|
); |
|
|
|
|
|
|
|
String payload = jsonEncode([ |
|
|
|
request1.payloadJson(), |
|
|
|
request2.payloadJson(), |
|
|
|
]); |
|
|
|
|
|
|
|
var resp = await http.post( |
|
|
|
Uri.parse('${dotenv.env["SERVER_URL"]}api/v1/auth/friend_request/qr_code'), |
|
|
|
headers: <String, String>{ |
|
|
|
'Content-Type': 'application/json; charset=UTF-8', |
|
|
|
'cookie': await getSessionCookie(), |
|
|
|
}, |
|
|
|
body: payload, |
|
|
|
); |
|
|
|
|
|
|
|
if (resp.statusCode != 200) { |
|
|
|
showMessage( |
|
|
|
'Failed to add friend, please try again later', |
|
|
|
context |
|
|
|
); |
|
|
|
return false; |
|
|
|
} |
|
|
|
|
|
|
|
final db = await getDatabaseConnection(); |
|
|
|
|
|
|
|
await db.insert( |
|
|
|
'friends', |
|
|
|
request1.toMap(), |
|
|
|
conflictAlgorithm: ConflictAlgorithm.replace, |
|
|
|
); |
|
|
|
|
|
|
|
await db.insert( |
|
|
|
'friends', |
|
|
|
request2.toMap(), |
|
|
|
conflictAlgorithm: ConflictAlgorithm.replace, |
|
|
|
); |
|
|
|
|
|
|
|
return true; |
|
|
|
} |
|
|
|
} |