diff --git a/lib/src/event.dart b/lib/src/event.dart index 97a5391..a82c2b6 100644 --- a/lib/src/event.dart +++ b/lib/src/event.dart @@ -372,7 +372,8 @@ class Event extends MatrixEvent { /// contain an attachment, this throws an error. Set [getThumbnail] to /// true to download the thumbnail instead. Future downloadAndDecryptAttachment( - {bool getThumbnail = false}) async { + {bool getThumbnail = false, + Future Function(String) downloadCallback}) async { if (![EventTypes.Message, EventTypes.Sticker].contains(type)) { throw ("This event has the type '$type' and so it can't contain an attachment."); } @@ -413,8 +414,11 @@ class Event extends MatrixEvent { // Download the file if (uint8list == null) { + downloadCallback ??= (String url) async { + return (await http.get(url)).bodyBytes; + }; uint8list = - (await http.get(mxContent.getDownloadLink(room.client))).bodyBytes; + await downloadCallback(mxContent.getDownloadLink(room.client)); if (storeable) { await room.client.database .storeFile(mxContent.toString(), uint8list, DateTime.now()); diff --git a/test/event_test.dart b/test/event_test.dart index f40f8ec..c2b3fcd 100644 --- a/test/event_test.dart +++ b/test/event_test.dart @@ -17,19 +17,33 @@ */ import 'dart:convert'; +import 'dart:typed_data'; import 'package:famedlysdk/famedlysdk.dart'; import 'package:famedlysdk/matrix_api.dart'; import 'package:famedlysdk/encryption.dart'; import 'package:famedlysdk/src/event.dart'; +import 'package:famedlysdk/src/utils/logs.dart'; import 'package:test/test.dart'; +import 'package:olm/olm.dart' as olm; +import 'fake_client.dart'; import 'fake_matrix_api.dart'; import 'fake_matrix_localizations.dart'; void main() { /// All Tests related to the Event group('Event', () { + var olmEnabled = true; + try { + olm.init(); + olm.Account(); + } catch (_) { + olmEnabled = false; + Logs.warning('[LibOlm] Failed to load LibOlm: ' + _.toString()); + } + Logs.success('[LibOlm] Enabled: $olmEnabled'); + final timestamp = DateTime.now().millisecondsSinceEpoch; final id = '!4fsdfjisjf:server.abc'; final senderID = '@alice:server.abc'; @@ -960,5 +974,185 @@ void main() { Timeline(events: [event, edit1, edit2, edit3], room: room)); expect(displayEvent.body, 'edit 2'); }); + test('downloadAndDecryptAttachment', () async { + final FILE_BUFF = Uint8List.fromList([0]); + final THUMBNAIL_BUFF = Uint8List.fromList([2]); + final downloadCallback = (String url) async { + return { + 'https://fakeserver.notexisting/_matrix/media/r0/download/example.org/file': + FILE_BUFF, + 'https://fakeserver.notexisting/_matrix/media/r0/download/example.org/thumb': + THUMBNAIL_BUFF, + }[url]; + }; + await client.checkServer('https://fakeServer.notExisting'); + final room = Room(id: '!localpart:server.abc', client: client); + var event = Event.fromJson({ + 'type': EventTypes.Message, + 'content': { + 'body': 'image', + 'msgtype': 'm.image', + 'url': 'mxc://example.org/file', + }, + 'event_id': '\$edit2', + 'sender': '@alice:example.org', + }, room); + var buffer = await event.downloadAndDecryptAttachment( + downloadCallback: downloadCallback); + expect(buffer.bytes, FILE_BUFF); + + event = Event.fromJson({ + 'type': EventTypes.Message, + 'content': { + 'body': 'image', + 'msgtype': 'm.image', + 'url': 'mxc://example.org/file', + 'info': { + 'thumbnail_url': 'mxc://example.org/thumb', + }, + }, + 'event_id': '\$edit2', + 'sender': '@alice:example.org', + }, room); + buffer = await event.downloadAndDecryptAttachment( + downloadCallback: downloadCallback); + expect(buffer.bytes, FILE_BUFF); + + buffer = await event.downloadAndDecryptAttachment( + getThumbnail: true, downloadCallback: downloadCallback); + expect(buffer.bytes, THUMBNAIL_BUFF); + }); + test('downloadAndDecryptAttachment encrypted', () async { + if (!olmEnabled) return; + + final FILE_BUFF_ENC = Uint8List.fromList([0x3B, 0x6B, 0xB2, 0x8C, 0xAF]); + final FILE_BUFF_DEC = Uint8List.fromList([0x74, 0x65, 0x73, 0x74, 0x0A]); + final THUMB_BUFF_ENC = + Uint8List.fromList([0x55, 0xD7, 0xEB, 0x72, 0x05, 0x13]); + final THUMB_BUFF_DEC = + Uint8List.fromList([0x74, 0x68, 0x75, 0x6D, 0x62, 0x0A]); + final downloadCallback = (String url) async { + return { + 'https://fakeserver.notexisting/_matrix/media/r0/download/example.com/file': + FILE_BUFF_ENC, + 'https://fakeserver.notexisting/_matrix/media/r0/download/example.com/thumb': + THUMB_BUFF_ENC, + }[url]; + }; + final room = Room(id: '!localpart:server.abc', client: await getClient()); + var event = Event.fromJson({ + 'type': EventTypes.Message, + 'content': { + 'body': 'image', + 'msgtype': 'm.image', + 'file': { + 'v': 'v2', + 'key': { + 'alg': 'A256CTR', + 'ext': true, + 'k': '7aPRNIDPeUAUqD6SPR3vVX5W9liyMG98NexVJ9udnCc', + 'key_ops': ['encrypt', 'decrypt'], + 'kty': 'oct' + }, + 'iv': 'Wdsf+tnOHIoAAAAAAAAAAA', + 'hashes': {'sha256': 'WgC7fw2alBC5t+xDx+PFlZxfFJXtIstQCg+j0WDaXxE'}, + 'url': 'mxc://example.com/file', + 'mimetype': 'text/plain' + }, + }, + 'event_id': '\$edit2', + 'sender': '@alice:example.org', + }, room); + var buffer = await event.downloadAndDecryptAttachment( + downloadCallback: downloadCallback); + expect(buffer.bytes, FILE_BUFF_DEC); + + event = Event.fromJson({ + 'type': EventTypes.Message, + 'content': { + 'body': 'image', + 'msgtype': 'm.image', + 'file': { + 'v': 'v2', + 'key': { + 'alg': 'A256CTR', + 'ext': true, + 'k': '7aPRNIDPeUAUqD6SPR3vVX5W9liyMG98NexVJ9udnCc', + 'key_ops': ['encrypt', 'decrypt'], + 'kty': 'oct' + }, + 'iv': 'Wdsf+tnOHIoAAAAAAAAAAA', + 'hashes': {'sha256': 'WgC7fw2alBC5t+xDx+PFlZxfFJXtIstQCg+j0WDaXxE'}, + 'url': 'mxc://example.com/file', + 'mimetype': 'text/plain' + }, + 'info': { + 'thumbnail_file': { + 'v': 'v2', + 'key': { + 'alg': 'A256CTR', + 'ext': true, + 'k': 'TmF-rZYetZbxpL5yjDPE21UALQJcpEE6X-nvUDD5rA0', + 'key_ops': ['encrypt', 'decrypt'], + 'kty': 'oct' + }, + 'iv': '41ZqNRZSLFUAAAAAAAAAAA', + 'hashes': { + 'sha256': 'zccOwXiOTAYhGXyk0Fra7CRreBF6itjiCKdd+ov8mO4' + }, + 'url': 'mxc://example.com/thumb', + 'mimetype': 'text/plain' + } + }, + }, + 'event_id': '\$edit2', + 'sender': '@alice:example.org', + }, room); + buffer = await event.downloadAndDecryptAttachment( + downloadCallback: downloadCallback); + expect(buffer.bytes, FILE_BUFF_DEC); + + buffer = await event.downloadAndDecryptAttachment( + getThumbnail: true, downloadCallback: downloadCallback); + expect(buffer.bytes, THUMB_BUFF_DEC); + + await room.client.dispose(closeDatabase: true); + }); + test('downloadAndDecryptAttachment store', () async { + final FILE_BUFF = Uint8List.fromList([0]); + var serverHits = 0; + final downloadCallback = (String url) async { + serverHits++; + return { + 'https://fakeserver.notexisting/_matrix/media/r0/download/example.org/newfile': + FILE_BUFF, + }[url]; + }; + await client.checkServer('https://fakeServer.notExisting'); + final room = Room(id: '!localpart:server.abc', client: await getClient()); + var event = Event.fromJson({ + 'type': EventTypes.Message, + 'content': { + 'body': 'image', + 'msgtype': 'm.image', + 'url': 'mxc://example.org/newfile', + 'info': { + 'size': 5, + }, + }, + 'event_id': '\$edit2', + 'sender': '@alice:example.org', + }, room); + var buffer = await event.downloadAndDecryptAttachment( + downloadCallback: downloadCallback); + expect(buffer.bytes, FILE_BUFF); + expect(serverHits, 1); + buffer = await event.downloadAndDecryptAttachment( + downloadCallback: downloadCallback); + expect(buffer.bytes, FILE_BUFF); + expect(serverHits, 1); + + await room.client.dispose(closeDatabase: true); + }); }); }