From d116a52ea92b313fac1a49e6476faa729bede816 Mon Sep 17 00:00:00 2001 From: Sorunome Date: Sat, 17 Oct 2020 12:03:54 +0200 Subject: [PATCH 1/3] fix: Better handle olm session recovery --- lib/encryption/olm_manager.dart | 15 ++++++++++++++- lib/src/client.dart | 4 ++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/lib/encryption/olm_manager.dart b/lib/encryption/olm_manager.dart index 52ce2d6..9c8bd49 100644 --- a/lib/encryption/olm_manager.dart +++ b/lib/encryption/olm_manager.dart @@ -28,6 +28,7 @@ import '../encryption/utils/json_signature_check_extension.dart'; import '../src/utils/logs.dart'; import 'encryption.dart'; import 'utils/olm_session.dart'; +import '../src/utils/run_in_root.dart'; class OlmManager { final Encryption encryption; @@ -344,6 +345,8 @@ class OlmManager { return res; } + final Map _restoredOlmSessionsTime = {}; + Future restoreOlmSession(String userId, String senderKey) async { if (!client.userDeviceKeys.containsKey(userId)) { return; @@ -353,6 +356,15 @@ class OlmManager { if (device == null) { return; } + // per device only one olm session per hour should be restored + final mapKey = '$userId;$senderKey'; + if (_restoredOlmSessionsTime.containsKey(mapKey) && + DateTime.now() + .subtract(Duration(hours: 1)) + .isBefore(_restoredOlmSessionsTime[mapKey])) { + return; + } + _restoredOlmSessionsTime[mapKey] = DateTime.now(); await startOutgoingOlmSessions([device]); await client.sendToDeviceEncrypted([device], 'm.dummy', {}); } @@ -386,7 +398,8 @@ class OlmManager { } catch (_) { // okay, the thing errored while decrypting. It is safe to assume that the olm session is corrupt and we should generate a new one if (client.enableE2eeRecovery) { - unawaited(restoreOlmSession(event.senderId, senderKey)); + unawaited( + runInRoot(() => restoreOlmSession(event.senderId, senderKey))); } rethrow; } diff --git a/lib/src/client.dart b/lib/src/client.dart index e75d5d0..b1b75b9 100644 --- a/lib/src/client.dart +++ b/lib/src/client.dart @@ -869,6 +869,8 @@ class Client extends MatrixApi { var update = RoomUpdate.fromSyncRoomUpdate(room, id); if (database != null) { + // TODO: This method seems to be rather slow for some updates + // Perhaps don't dynamically build that one query? await database.storeRoomUpdate(this.id, update, getRoomById(id)); } _updateRoomsByRoomUpdate(update); @@ -883,6 +885,7 @@ class Client extends MatrixApi { /// Handle now all room events and save them in the database if (room is JoinedRoomUpdate) { if (room.state?.isNotEmpty ?? false) { + // TODO: This method seems to be comperatively slow for some updates await _handleRoomEvents( id, room.state.map((i) => i.toJson()).toList(), 'state'); handledEvents = true; @@ -896,6 +899,7 @@ class Client extends MatrixApi { handledEvents = true; } if (room.ephemeral?.isNotEmpty ?? false) { + // TODO: This method seems to be comperatively slow for some updates await _handleEphemerals( id, room.ephemeral.map((i) => i.toJson()).toList()); } From 090f0c326cfda18c4a255c56acb72beaa5e1e339 Mon Sep 17 00:00:00 2001 From: Sorunome Date: Mon, 19 Oct 2020 17:27:00 +0200 Subject: [PATCH 2/3] fix: Do not require unrequired field in supported versions response --- lib/encryption/encryption.dart | 3 ++- lib/matrix_api/model/supported_versions.dart | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/encryption/encryption.dart b/lib/encryption/encryption.dart index 9741460..d487db5 100644 --- a/lib/encryption/encryption.dart +++ b/lib/encryption/encryption.dart @@ -118,7 +118,8 @@ class Encryption { runInRoot(() => keyVerificationManager.handleEventUpdate(update))); } if (update.content['sender'] == client.userID && - !update.content['unsigned'].containsKey('transaction_id')) { + (!update.content.containsKey('unsigned') || + !update.content['unsigned'].containsKey('transaction_id'))) { // maybe we need to re-try SSSS secrets unawaited(runInRoot(() => ssss.periodicallyRequestMissingCache())); } diff --git a/lib/matrix_api/model/supported_versions.dart b/lib/matrix_api/model/supported_versions.dart index e5beaf3..33ab8c4 100644 --- a/lib/matrix_api/model/supported_versions.dart +++ b/lib/matrix_api/model/supported_versions.dart @@ -22,7 +22,7 @@ class SupportedVersions { SupportedVersions.fromJson(Map json) { versions = json['versions'].cast(); - unstableFeatures = Map.from(json['unstable_features']); + unstableFeatures = Map.from(json['unstable_features'] ?? {}); } Map toJson() { From be6824b7465b2bda7e5b769254be5cddd207b479 Mon Sep 17 00:00:00 2001 From: Sorunome Date: Wed, 21 Oct 2020 11:20:19 +0200 Subject: [PATCH 3/3] feat: Add LaTeX markdown rendering as per MSC2191 --- lib/src/utils/markdown.dart | 58 ++++++++++++++++++++++++++++++++++--- test/markdown_test.dart | 6 ++++ 2 files changed, 60 insertions(+), 4 deletions(-) diff --git a/lib/src/utils/markdown.dart b/lib/src/utils/markdown.dart index 745601b..81a882c 100644 --- a/lib/src/utils/markdown.dart +++ b/lib/src/utils/markdown.dart @@ -76,6 +76,51 @@ class EmoteSyntax extends InlineSyntax { } } +class InlineLatexSyntax extends InlineSyntax { + InlineLatexSyntax() : super(r'(?<=\s|^)\$(?=\S)([^\n$]+)(?<=\S)\$(?=\s|$)'); + + @override + bool onMatch(InlineParser parser, Match match) { + final latex = htmlEscape.convert(match[1]); + final element = Element('span', [Element.text('code', latex)]); + element.attributes['data-mx-maths'] = latex; + parser.addNode(element); + return true; + } +} + +class BlockLatexSyntax extends BlockSyntax { + @override + RegExp get pattern => RegExp(r'^[ ]{0,3}\${2}\s*$'); + + @override + List parseChildLines(BlockParser parser) { + var childLines = []; + parser.advance(); + while (!parser.isDone) { + if (!pattern.hasMatch(parser.current)) { + childLines.add(parser.current); + parser.advance(); + } else { + parser.advance(); + break; + } + } + return childLines; + } + + @override + Node parse(BlockParser parser) { + final childLines = parseChildLines(parser); + final latex = htmlEscape.convert(childLines.join('\n')); + final element = Element('div', [ + Element('pre', [Element.text('code', latex)]) + ]); + element.attributes['data-mx-maths'] = latex; + return element; + } +} + class PillSyntax extends InlineSyntax { PillSyntax() : super(r'([@#!][^\s:]*:[^\s]+\.\w+)'); @@ -94,18 +139,22 @@ String markdown(String text, [Map> emotePacks]) { var ret = markdownToHtml( text, extensionSet: ExtensionSet.commonMark, + blockSyntaxes: [ + BlockLatexSyntax(), + ], inlineSyntaxes: [ StrikethroughSyntax(), LinebreakSyntax(), SpoilerSyntax(), EmoteSyntax(emotePacks), - PillSyntax() + PillSyntax(), + InlineLatexSyntax(), ], ); var stripPTags = '

'.allMatches(ret).length <= 1; if (stripPTags) { - final otherBlockTags = [ + const otherBlockTags = { 'table', 'pre', 'ol', @@ -116,8 +165,9 @@ String markdown(String text, [Map> emotePacks]) { 'h4', 'h5', 'h6', - 'blockquote' - ]; + 'blockquote', + 'div', + }; for (final tag in otherBlockTags) { // we check for the close tag as the opening one might have attributes if (ret.contains('')) { diff --git a/test/markdown_test.dart b/test/markdown_test.dart index 7690d4b..0b172d6 100644 --- a/test/markdown_test.dart +++ b/test/markdown_test.dart @@ -70,5 +70,11 @@ void main() { expect(markdown('!blah:example.org'), '!blah:example.org'); }); + test('latex', () { + expect(markdown('meep \$\\frac{2}{3}\$'), + 'meep \\frac{2}{3}'); + expect(markdown('hey\n\$\$\nbeep\nboop\n\$\$\nmeow'), + '

hey

\n
\n
beep\nboop
\n
\n

meow

'); + }); }); }