|
@ -11,14 +11,10 @@ import '/utils/encryption/aes_helper.dart'; |
|
|
import '/utils/storage/database.dart'; |
|
|
import '/utils/storage/database.dart'; |
|
|
import '/utils/strings.dart'; |
|
|
import '/utils/strings.dart'; |
|
|
|
|
|
|
|
|
Conversation findConversationByDetailId(List<Conversation> conversations, String id) { |
|
|
|
|
|
for (var conversation in conversations) { |
|
|
|
|
|
if (conversation.conversationDetailId == id) { |
|
|
|
|
|
return conversation; |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
// Or return `null`. |
|
|
|
|
|
throw ArgumentError.value(id, "id", "No element with that id"); |
|
|
|
|
|
|
|
|
enum ConversationStatus { |
|
|
|
|
|
complete, |
|
|
|
|
|
pending, |
|
|
|
|
|
error, |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
class Conversation { |
|
|
class Conversation { |
|
@ -28,6 +24,7 @@ class Conversation { |
|
|
String symmetricKey; |
|
|
String symmetricKey; |
|
|
bool admin; |
|
|
bool admin; |
|
|
String name; |
|
|
String name; |
|
|
|
|
|
ConversationStatus status; |
|
|
|
|
|
|
|
|
Conversation({ |
|
|
Conversation({ |
|
|
required this.id, |
|
|
required this.id, |
|
@ -36,35 +33,86 @@ class Conversation { |
|
|
required this.symmetricKey, |
|
|
required this.symmetricKey, |
|
|
required this.admin, |
|
|
required this.admin, |
|
|
required this.name, |
|
|
required this.name, |
|
|
|
|
|
required this.status, |
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
factory Conversation.fromJson(Map<String, dynamic> json, RSAPrivateKey privKey) { |
|
|
factory Conversation.fromJson(Map<String, dynamic> json, RSAPrivateKey privKey) { |
|
|
var symmetricKeyDecrypted = CryptoUtils.rsaDecrypt( |
|
|
var symmetricKeyDecrypted = CryptoUtils.rsaDecrypt( |
|
|
base64.decode(json['symmetric_key']), |
|
|
|
|
|
privKey, |
|
|
|
|
|
|
|
|
base64.decode(json['symmetric_key']), |
|
|
|
|
|
privKey, |
|
|
); |
|
|
); |
|
|
|
|
|
|
|
|
var detailId = AesHelper.aesDecrypt( |
|
|
var detailId = AesHelper.aesDecrypt( |
|
|
symmetricKeyDecrypted, |
|
|
|
|
|
base64.decode(json['conversation_detail_id']), |
|
|
|
|
|
|
|
|
symmetricKeyDecrypted, |
|
|
|
|
|
base64.decode(json['conversation_detail_id']), |
|
|
); |
|
|
); |
|
|
|
|
|
|
|
|
var admin = AesHelper.aesDecrypt( |
|
|
var admin = AesHelper.aesDecrypt( |
|
|
symmetricKeyDecrypted, |
|
|
|
|
|
base64.decode(json['admin']), |
|
|
|
|
|
|
|
|
symmetricKeyDecrypted, |
|
|
|
|
|
base64.decode(json['admin']), |
|
|
); |
|
|
); |
|
|
|
|
|
|
|
|
return Conversation( |
|
|
return Conversation( |
|
|
id: json['id'], |
|
|
|
|
|
userId: json['user_id'], |
|
|
|
|
|
conversationDetailId: detailId, |
|
|
|
|
|
symmetricKey: base64.encode(symmetricKeyDecrypted), |
|
|
|
|
|
admin: admin == 'true', |
|
|
|
|
|
name: 'Unknown', |
|
|
|
|
|
|
|
|
id: json['id'], |
|
|
|
|
|
userId: json['user_id'], |
|
|
|
|
|
conversationDetailId: detailId, |
|
|
|
|
|
symmetricKey: base64.encode(symmetricKeyDecrypted), |
|
|
|
|
|
admin: admin == 'true', |
|
|
|
|
|
name: 'Unknown', |
|
|
|
|
|
status: ConversationStatus.complete, |
|
|
); |
|
|
); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
Future<Map<String, dynamic>> toJson() async { |
|
|
|
|
|
MyProfile profile = await MyProfile.getProfile(); |
|
|
|
|
|
|
|
|
|
|
|
var symKey = base64.decode(symmetricKey); |
|
|
|
|
|
|
|
|
|
|
|
List<ConversationUser> users = await getConversationUsers(this); |
|
|
|
|
|
List<Object> userConversations = []; |
|
|
|
|
|
|
|
|
|
|
|
for (var x in users) { |
|
|
|
|
|
print(x.toMap()); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
for (ConversationUser user in users) { |
|
|
|
|
|
if (profile.id == user.userId) { |
|
|
|
|
|
userConversations.add({ |
|
|
|
|
|
'id': user.id, |
|
|
|
|
|
'user_id': profile.id, |
|
|
|
|
|
'conversation_detail_id': AesHelper.aesEncrypt(symKey, Uint8List.fromList(id.codeUnits)), |
|
|
|
|
|
'admin': AesHelper.aesEncrypt(symKey, Uint8List.fromList((admin ? 'true' : 'false').codeUnits)), |
|
|
|
|
|
'symmetric_key': base64.encode(CryptoUtils.rsaEncrypt(symKey, profile.publicKey!)), |
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
continue; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
Friend friend = await getFriendByFriendId(user.userId); |
|
|
|
|
|
RSAPublicKey pubKey = CryptoUtils.rsaPublicKeyFromPem(friend.asymmetricPublicKey); |
|
|
|
|
|
|
|
|
|
|
|
userConversations.add({ |
|
|
|
|
|
'id': user.id, |
|
|
|
|
|
'user_id': friend.userId, |
|
|
|
|
|
'conversation_detail_id': AesHelper.aesEncrypt(symKey, Uint8List.fromList(id.codeUnits)), |
|
|
|
|
|
'admin': AesHelper.aesEncrypt(symKey, Uint8List.fromList((admin ? 'true' : 'false').codeUnits)), |
|
|
|
|
|
'symmetric_key': base64.encode(CryptoUtils.rsaEncrypt(symKey, pubKey)), |
|
|
|
|
|
}); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
for (var x in userConversations) { |
|
|
|
|
|
print(x); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
return { |
|
|
|
|
|
'id': id, |
|
|
|
|
|
'name': AesHelper.aesEncrypt(symKey, Uint8List.fromList(name.codeUnits)), |
|
|
|
|
|
'users': AesHelper.aesEncrypt(symKey, Uint8List.fromList(jsonEncode(users).codeUnits)), |
|
|
|
|
|
'user_conversations': userConversations, |
|
|
|
|
|
}; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
@override |
|
|
@override |
|
|
String toString() { |
|
|
String toString() { |
|
|
return ''' |
|
|
return ''' |
|
@ -84,6 +132,7 @@ class Conversation { |
|
|
'symmetric_key': symmetricKey, |
|
|
'symmetric_key': symmetricKey, |
|
|
'admin': admin ? 1 : 0, |
|
|
'admin': admin ? 1 : 0, |
|
|
'name': name, |
|
|
'name': name, |
|
|
|
|
|
'status': status.index, |
|
|
}; |
|
|
}; |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
@ -107,43 +156,57 @@ Future<Conversation> createConversation(String title, List<Friend> friends) asyn |
|
|
symmetricKey: base64.encode(symmetricKey), |
|
|
symmetricKey: base64.encode(symmetricKey), |
|
|
admin: true, |
|
|
admin: true, |
|
|
name: title, |
|
|
name: title, |
|
|
|
|
|
status: ConversationStatus.pending, |
|
|
); |
|
|
); |
|
|
|
|
|
|
|
|
await db.insert( |
|
|
await db.insert( |
|
|
'conversations', |
|
|
|
|
|
conversation.toMap(), |
|
|
|
|
|
conflictAlgorithm: ConflictAlgorithm.replace, |
|
|
|
|
|
|
|
|
'conversations', |
|
|
|
|
|
conversation.toMap(), |
|
|
|
|
|
conflictAlgorithm: ConflictAlgorithm.replace, |
|
|
); |
|
|
); |
|
|
|
|
|
|
|
|
await db.insert( |
|
|
await db.insert( |
|
|
'conversation_users', |
|
|
|
|
|
ConversationUser( |
|
|
|
|
|
id: uuid.v4(), |
|
|
|
|
|
conversationId: conversationId, |
|
|
|
|
|
username: profile.username, |
|
|
|
|
|
associationKey: associationKey, |
|
|
|
|
|
admin: false, |
|
|
|
|
|
).toMap(), |
|
|
|
|
|
conflictAlgorithm: ConflictAlgorithm.replace, |
|
|
|
|
|
|
|
|
'conversation_users', |
|
|
|
|
|
ConversationUser( |
|
|
|
|
|
id: uuid.v4(), |
|
|
|
|
|
userId: profile.id, |
|
|
|
|
|
conversationId: conversationId, |
|
|
|
|
|
username: profile.username, |
|
|
|
|
|
associationKey: associationKey, |
|
|
|
|
|
admin: true, |
|
|
|
|
|
).toMap(), |
|
|
|
|
|
conflictAlgorithm: ConflictAlgorithm.replace, |
|
|
); |
|
|
); |
|
|
|
|
|
|
|
|
for (Friend friend in friends) { |
|
|
for (Friend friend in friends) { |
|
|
await db.insert( |
|
|
await db.insert( |
|
|
'conversation_users', |
|
|
|
|
|
ConversationUser( |
|
|
|
|
|
id: uuid.v4(), |
|
|
|
|
|
conversationId: conversationId, |
|
|
|
|
|
username: friend.username, |
|
|
|
|
|
associationKey: associationKey, |
|
|
|
|
|
admin: false, |
|
|
|
|
|
).toMap(), |
|
|
|
|
|
conflictAlgorithm: ConflictAlgorithm.replace, |
|
|
|
|
|
|
|
|
'conversation_users', |
|
|
|
|
|
ConversationUser( |
|
|
|
|
|
id: uuid.v4(), |
|
|
|
|
|
userId: friend.friendId, |
|
|
|
|
|
conversationId: conversationId, |
|
|
|
|
|
username: friend.username, |
|
|
|
|
|
associationKey: associationKey, |
|
|
|
|
|
admin: false, |
|
|
|
|
|
).toMap(), |
|
|
|
|
|
conflictAlgorithm: ConflictAlgorithm.replace, |
|
|
); |
|
|
); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
return conversation; |
|
|
return conversation; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
Conversation findConversationByDetailId(List<Conversation> conversations, String id) { |
|
|
|
|
|
for (var conversation in conversations) { |
|
|
|
|
|
if (conversation.conversationDetailId == id) { |
|
|
|
|
|
return conversation; |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
// Or return `null`. |
|
|
|
|
|
throw ArgumentError.value(id, "id", "No element with that id"); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// A method that retrieves all the dogs from the dogs table. |
|
|
// A method that retrieves all the dogs from the dogs table. |
|
|
Future<List<Conversation>> getConversations() async { |
|
|
Future<List<Conversation>> getConversations() async { |
|
|
final db = await getDatabaseConnection(); |
|
|
final db = await getDatabaseConnection(); |
|
@ -152,12 +215,38 @@ Future<List<Conversation>> getConversations() async { |
|
|
|
|
|
|
|
|
return List.generate(maps.length, (i) { |
|
|
return List.generate(maps.length, (i) { |
|
|
return Conversation( |
|
|
return Conversation( |
|
|
id: maps[i]['id'], |
|
|
|
|
|
userId: maps[i]['user_id'], |
|
|
|
|
|
conversationDetailId: maps[i]['conversation_detail_id'], |
|
|
|
|
|
symmetricKey: maps[i]['symmetric_key'], |
|
|
|
|
|
admin: maps[i]['admin'] == 1, |
|
|
|
|
|
name: maps[i]['name'], |
|
|
|
|
|
|
|
|
id: maps[i]['id'], |
|
|
|
|
|
userId: maps[i]['user_id'], |
|
|
|
|
|
conversationDetailId: maps[i]['conversation_detail_id'], |
|
|
|
|
|
symmetricKey: maps[i]['symmetric_key'], |
|
|
|
|
|
admin: maps[i]['admin'] == 1, |
|
|
|
|
|
name: maps[i]['name'], |
|
|
|
|
|
status: ConversationStatus.values[maps[i]['status']], |
|
|
); |
|
|
); |
|
|
}); |
|
|
}); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Future<Conversation> getConversationById(String id) async { |
|
|
|
|
|
final db = await getDatabaseConnection(); |
|
|
|
|
|
|
|
|
|
|
|
final List<Map<String, dynamic>> maps = await db.query( |
|
|
|
|
|
'conversations', |
|
|
|
|
|
where: 'id = ?', |
|
|
|
|
|
whereArgs: [id], |
|
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
|
|
if (maps.length != 1) { |
|
|
|
|
|
throw ArgumentError('Invalid user id'); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
return Conversation( |
|
|
|
|
|
id: maps[0]['id'], |
|
|
|
|
|
userId: maps[0]['user_id'], |
|
|
|
|
|
conversationDetailId: maps[0]['conversation_detail_id'], |
|
|
|
|
|
symmetricKey: maps[0]['symmetric_key'], |
|
|
|
|
|
admin: maps[0]['admin'] == 1, |
|
|
|
|
|
name: maps[0]['name'], |
|
|
|
|
|
status: ConversationStatus.values[maps[0]['status']], |
|
|
|
|
|
); |
|
|
|
|
|
} |