Encrypted messaging app
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

176 lines
4.5 KiB

  1. import 'dart:convert';
  2. import 'dart:io';
  3. import 'package:Envelope/models/messages.dart';
  4. import 'package:Envelope/utils/storage/write_file.dart';
  5. import 'package:http/http.dart' as http;
  6. import 'package:sqflite/sqflite.dart';
  7. import 'package:uuid/uuid.dart';
  8. import '/models/image_message.dart';
  9. import '/models/text_messages.dart';
  10. import '/models/conversation_users.dart';
  11. import '/models/conversations.dart';
  12. import '/models/my_profile.dart';
  13. import '/utils/storage/database.dart';
  14. import '/utils/storage/session_cookie.dart';
  15. Future<void> sendMessage(Conversation conversation, {
  16. String? data,
  17. List<File> files = const []
  18. }) async {
  19. MyProfile profile = await MyProfile.getProfile();
  20. var uuid = const Uuid();
  21. ConversationUser currentUser = await getConversationUser(conversation, profile.id);
  22. List<Message> messages = [];
  23. List<Map<String, dynamic>> messagesToSend = [];
  24. final db = await getDatabaseConnection();
  25. if (data != null) {
  26. TextMessage message = TextMessage(
  27. id: uuid.v4(),
  28. symmetricKey: '',
  29. userSymmetricKey: '',
  30. senderId: currentUser.userId,
  31. senderUsername: profile.username,
  32. associationKey: currentUser.associationKey,
  33. createdAt: DateTime.now().toIso8601String(),
  34. failedToSend: false,
  35. text: data,
  36. );
  37. messages.add(message);
  38. messagesToSend.add(await message.payloadJson(
  39. conversation,
  40. ));
  41. await db.insert(
  42. 'messages',
  43. message.toMap(),
  44. conflictAlgorithm: ConflictAlgorithm.replace,
  45. );
  46. }
  47. for (File file in files) {
  48. String messageId = uuid.v4();
  49. File writtenFile = await writeImage(
  50. messageId,
  51. file.readAsBytesSync(),
  52. );
  53. ImageMessage message = ImageMessage(
  54. id: messageId,
  55. symmetricKey: '',
  56. userSymmetricKey: '',
  57. senderId: currentUser.userId,
  58. senderUsername: profile.username,
  59. associationKey: currentUser.associationKey,
  60. createdAt: DateTime.now().toIso8601String(),
  61. failedToSend: false,
  62. file: writtenFile,
  63. );
  64. messages.add(message);
  65. messagesToSend.add(await message.payloadJson(
  66. conversation,
  67. ));
  68. await db.insert(
  69. 'messages',
  70. message.toMap(),
  71. conflictAlgorithm: ConflictAlgorithm.replace,
  72. );
  73. }
  74. String sessionCookie = await getSessionCookie();
  75. return http.post(
  76. await MyProfile.getServerUrl('api/v1/auth/message'),
  77. headers: <String, String>{
  78. 'Content-Type': 'application/json; charset=UTF-8',
  79. 'cookie': sessionCookie,
  80. },
  81. body: jsonEncode(messagesToSend),
  82. )
  83. .then((resp) {
  84. if (resp.statusCode != 200) {
  85. throw Exception('Unable to send message');
  86. }
  87. })
  88. .catchError((exception) {
  89. for (Message message in messages) {
  90. message.failedToSend = true;
  91. db.update(
  92. 'messages',
  93. message.toMap(),
  94. where: 'id = ?',
  95. whereArgs: [message.id],
  96. );
  97. }
  98. throw exception;
  99. });
  100. }
  101. Future<void> updateMessageThread(Conversation conversation, {MyProfile? profile}) async {
  102. profile ??= await MyProfile.getProfile();
  103. ConversationUser currentUser = await getConversationUser(conversation, profile.id);
  104. var resp = await http.get(
  105. await MyProfile.getServerUrl('api/v1/auth/messages/${currentUser.associationKey}'),
  106. headers: {
  107. 'cookie': await getSessionCookie(),
  108. }
  109. );
  110. if (resp.statusCode != 200) {
  111. throw Exception(resp.body);
  112. }
  113. List<dynamic> messageThreadJson = jsonDecode(resp.body);
  114. final db = await getDatabaseConnection();
  115. for (var i = 0; i < messageThreadJson.length; i++) {
  116. var messageJson = messageThreadJson[i] as Map<String, dynamic>;
  117. var message = messageJson['message_data']['attachment_id'] != null ?
  118. await ImageMessage.fromJson(
  119. messageJson,
  120. profile.privateKey!,
  121. ) :
  122. TextMessage.fromJson(
  123. messageJson,
  124. profile.privateKey!,
  125. );
  126. ConversationUser messageUser = await getConversationUser(conversation, message.senderId);
  127. message.senderUsername = messageUser.username;
  128. await db.insert(
  129. 'messages',
  130. message.toMap(),
  131. conflictAlgorithm: ConflictAlgorithm.replace,
  132. );
  133. }
  134. }
  135. Future<void> updateMessageThreads({List<Conversation>? conversations}) async {
  136. // try {
  137. MyProfile profile = await MyProfile.getProfile();
  138. conversations ??= await getConversations();
  139. for (var i = 0; i < conversations.length; i++) {
  140. await updateMessageThread(conversations[i], profile: profile);
  141. }
  142. // } catch(SocketException) {
  143. // return;
  144. // }
  145. }