diff_fetcher.dart 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142
  1. import 'dart:convert';
  2. import 'dart:math';
  3. import 'package:dio/dio.dart';
  4. import 'package:flutter_sodium/flutter_sodium.dart';
  5. import 'package:logging/logging.dart';
  6. import 'package:photos/core/configuration.dart';
  7. import 'package:photos/core/network.dart';
  8. import 'package:photos/db/files_db.dart';
  9. import 'package:photos/models/file.dart';
  10. import 'package:photos/models/magic_metadata.dart';
  11. import 'package:photos/utils/crypto_util.dart';
  12. import 'package:photos/utils/file_download_util.dart';
  13. class DiffFetcher {
  14. final _logger = Logger("DiffFetcher");
  15. final _dio = Network.instance.getDio();
  16. Future<Diff> getEncryptedFilesDiff(int collectionID, int sinceTime) async {
  17. _logger.info(
  18. "Fetching diff in collection " +
  19. collectionID.toString() +
  20. " since " +
  21. sinceTime.toString(),
  22. );
  23. try {
  24. final response = await _dio.get(
  25. Configuration.instance.getHttpEndpoint() + "/collections/v2/diff",
  26. options: Options(
  27. headers: {"X-Auth-Token": Configuration.instance.getToken()},
  28. ),
  29. queryParameters: {
  30. "collectionID": collectionID,
  31. "sinceTime": sinceTime,
  32. },
  33. );
  34. final files = <File>[];
  35. int latestUpdatedAtTime = 0;
  36. if (response != null) {
  37. final diff = response.data["diff"] as List;
  38. final bool hasMore = response.data["hasMore"] as bool;
  39. final startTime = DateTime.now();
  40. final existingFiles =
  41. await FilesDB.instance.getUploadedFileIDs(collectionID);
  42. final deletedFiles = <File>[];
  43. for (final item in diff) {
  44. final file = File();
  45. file.uploadedFileID = item["id"];
  46. file.collectionID = item["collectionID"];
  47. file.updationTime = item["updationTime"];
  48. latestUpdatedAtTime = max(latestUpdatedAtTime, file.updationTime);
  49. if (item["isDeleted"]) {
  50. if (existingFiles.contains(file.uploadedFileID)) {
  51. deletedFiles.add(file);
  52. }
  53. continue;
  54. }
  55. if (existingFiles.contains(file.uploadedFileID)) {
  56. final existingFile = await FilesDB.instance
  57. .getUploadedFile(file.uploadedFileID, file.collectionID);
  58. if (existingFile != null) {
  59. file.generatedID = existingFile.generatedID;
  60. }
  61. }
  62. file.ownerID = item["ownerID"];
  63. file.encryptedKey = item["encryptedKey"];
  64. file.keyDecryptionNonce = item["keyDecryptionNonce"];
  65. file.fileDecryptionHeader = item["file"]["decryptionHeader"];
  66. file.thumbnailDecryptionHeader =
  67. item["thumbnail"]["decryptionHeader"];
  68. file.metadataDecryptionHeader = item["metadata"]["decryptionHeader"];
  69. final fileDecryptionKey = decryptFileKey(file);
  70. final encodedMetadata = await CryptoUtil.decryptChaCha(
  71. Sodium.base642bin(item["metadata"]["encryptedData"]),
  72. fileDecryptionKey,
  73. Sodium.base642bin(file.metadataDecryptionHeader),
  74. );
  75. final Map<String, dynamic> metadata =
  76. jsonDecode(utf8.decode(encodedMetadata));
  77. file.applyMetadata(metadata);
  78. if (item['magicMetadata'] != null) {
  79. final utfEncodedMmd = await CryptoUtil.decryptChaCha(
  80. Sodium.base642bin(item['magicMetadata']['data']),
  81. fileDecryptionKey,
  82. Sodium.base642bin(item['magicMetadata']['header']),
  83. );
  84. file.mMdEncodedJson = utf8.decode(utfEncodedMmd);
  85. file.mMdVersion = item['magicMetadata']['version'];
  86. file.magicMetadata =
  87. MagicMetadata.fromEncodedJson(file.mMdEncodedJson);
  88. }
  89. if (item['pubMagicMetadata'] != null) {
  90. final utfEncodedMmd = await CryptoUtil.decryptChaCha(
  91. Sodium.base642bin(item['pubMagicMetadata']['data']),
  92. fileDecryptionKey,
  93. Sodium.base642bin(item['pubMagicMetadata']['header']),
  94. );
  95. file.pubMmdEncodedJson = utf8.decode(utfEncodedMmd);
  96. file.pubMmdVersion = item['pubMagicMetadata']['version'];
  97. file.pubMagicMetadata =
  98. PubMagicMetadata.fromEncodedJson(file.pubMmdEncodedJson);
  99. }
  100. files.add(file);
  101. }
  102. final endTime = DateTime.now();
  103. _logger.info(
  104. "time for parsing " +
  105. files.length.toString() +
  106. " items within collection " +
  107. collectionID.toString() +
  108. ": " +
  109. Duration(
  110. microseconds: (endTime.microsecondsSinceEpoch -
  111. startTime.microsecondsSinceEpoch),
  112. ).inMilliseconds.toString(),
  113. );
  114. return Diff(files, deletedFiles, hasMore, latestUpdatedAtTime);
  115. } else {
  116. return Diff(<File>[], <File>[], false, 0);
  117. }
  118. } catch (e, s) {
  119. _logger.severe(e, s);
  120. rethrow;
  121. }
  122. }
  123. }
  124. class Diff {
  125. final List<File> updatedFiles;
  126. final List<File> deletedFiles;
  127. final bool hasMore;
  128. final int latestUpdatedAtTime;
  129. Diff(
  130. this.updatedFiles,
  131. this.deletedFiles,
  132. this.hasMore,
  133. this.latestUpdatedAtTime,
  134. );
  135. }