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 createState() => _QrReaderState(); } class _QrReaderState extends State { 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: [ 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 addFriend(Barcode scanData) async { Map 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: { '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; } }