| 
						
						
						
					 | 
				
				 | 
				
					@ -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; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					  } | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					} |