downloadAndDecryptAttachment method

Future<MatrixFile> downloadAndDecryptAttachment({
  1. bool getThumbnail = false,
  2. Future<Uint8List> downloadCallback(
    1. Uri
    )?,
  3. bool fromLocalStoreOnly = false,
})

Downloads (and decrypts if necessary) the attachment of this event and returns it as a MatrixFile. If this event doesn't contain an attachment, this throws an error. Set getThumbnail to true to download the thumbnail instead. Set fromLocalStoreOnly to true if you want to retrieve the attachment from the local store only without making http request.

Implementation

Future<MatrixFile> downloadAndDecryptAttachment(
    {bool getThumbnail = false,
    Future<Uint8List> Function(Uri)? downloadCallback,
    bool fromLocalStoreOnly = false}) async {
  if (![EventTypes.Message, EventTypes.Sticker].contains(type)) {
    throw ("This event has the type '$type' and so it can't contain an attachment.");
  }
  if (status.isSending) {
    final localFile = room.sendingFilePlaceholders[eventId];
    if (localFile != null) return localFile;
  }
  final database = room.client.database;
  final mxcUrl = attachmentOrThumbnailMxcUrl(getThumbnail: getThumbnail);
  if (mxcUrl == null) {
    throw "This event hasn't any attachment or thumbnail.";
  }
  getThumbnail = mxcUrl != attachmentMxcUrl;
  final isEncrypted =
      getThumbnail ? isThumbnailEncrypted : isAttachmentEncrypted;
  if (isEncrypted && !room.client.encryptionEnabled) {
    throw ('Encryption is not enabled in your Client.');
  }

  // Is this file storeable?
  final thisInfoMap = getThumbnail ? thumbnailInfoMap : infoMap;
  var storeable = database != null &&
      thisInfoMap['size'] is int &&
      thisInfoMap['size'] <= database.maxFileSize;

  Uint8List? uint8list;
  if (storeable) {
    uint8list = await room.client.database?.getFile(mxcUrl);
  }

  // Download the file
  final canDownloadFileFromServer = uint8list == null && !fromLocalStoreOnly;
  if (canDownloadFileFromServer) {
    final httpClient = room.client.httpClient;
    downloadCallback ??=
        (Uri url) async => (await httpClient.get(url)).bodyBytes;
    uint8list = await downloadCallback(mxcUrl.getDownloadLink(room.client));
    storeable = database != null &&
        storeable &&
        uint8list.lengthInBytes < database.maxFileSize;
    if (storeable) {
      await database.storeFile(
          mxcUrl, uint8list, DateTime.now().millisecondsSinceEpoch);
    }
  } else if (uint8list == null) {
    throw ('Unable to download file from local store.');
  }

  // Decrypt the file
  if (isEncrypted) {
    final fileMap =
        getThumbnail ? infoMap['thumbnail_file'] : content['file'];
    if (!fileMap['key']['key_ops'].contains('decrypt')) {
      throw ("Missing 'decrypt' in 'key_ops'.");
    }
    final encryptedFile = EncryptedFile(
      data: uint8list,
      iv: fileMap['iv'],
      k: fileMap['key']['k'],
      sha256: fileMap['hashes']['sha256'],
    );
    uint8list =
        await room.client.nativeImplementations.decryptFile(encryptedFile);
    if (uint8list == null) {
      throw ('Unable to decrypt file');
    }
  }
  return MatrixFile(bytes: uint8list, name: body);
}