Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions lib/matrix_api_lite.dart
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ library;
export 'matrix_api_lite/generated/model.dart';
export 'matrix_api_lite/matrix_api.dart';
export 'matrix_api_lite/model/algorithm_types.dart';
export 'matrix_api_lite/model/matrix_id.dart';
export 'matrix_api_lite/model/auth/authentication_data.dart';
export 'matrix_api_lite/model/auth/authentication_identifier.dart';
export 'matrix_api_lite/model/auth/authentication_password.dart';
Expand Down
152 changes: 152 additions & 0 deletions lib/matrix_api_lite/model/matrix_id.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
const Set<String> matrixIdSigils = {'@', '!', '#', '\$', '+'};

const int matrixIdMaxLength = 255;

extension on String {
String get _localpart => substring(1).split(':').first;
String? get _domain {
final colonIndex = indexOf(':');
if (colonIndex == -1) return null;
return substring(indexOf(':') + 1);
}

void _validate(String expectedSigil) {
if (this != trim()) {
throw Exception(
'Invalid matrix id: String must not have leading or trailing whitespaces!',
);
}
if (isEmpty) {
throw Exception('Invalid matrix id: String is empty!');
}

// Check total length (including sigil and domain)
if (length > matrixIdMaxLength) {
throw Exception('Invalid matrix id: Must not exceed 255 bytes!');
}

// Validate localpart is not empty
if (_localpart.isEmpty) {
throw Exception('Invalid matrix id: Localpart must not be empty!');
}

// Validate localpart doesn't contain invalid characters
if (_localpart.contains('\u0000')) {
throw Exception(
'Invalid matrix id: Localpart must not contain NUL character!',
);
}

final sigil = this[0];

if (sigil != expectedSigil) {
throw Exception(
'Invalid matrix id: Sigil was $sigil but expected $expectedSigil',
);
}

if (!matrixIdSigils.contains(sigil)) {
throw Exception('Invalid matrix id: Unknown sigil $sigil');
}

if ({'@', '#'}.contains(sigil) && _domain == null) {
throw Exception(
'Invalid matrix ID: Domain is required for User IDs and Room Aliases!',
);
}

return; // Valid Matrix ID
}
}

extension type UserId._(String matrixId) {
UserId(this.matrixId) {
matrixId._validate(sigil);
}

UserId.from(String localpart, String domain)
: matrixId = '@$localpart:$domain';

static const String sigil = '@';

static UserId? tryParse(String string) {
try {
return UserId(string);
} catch (_) {
return null;
}
}

void validate() => matrixId._validate(sigil);
String get localpart => matrixId._localpart;
String get domain => matrixId._domain!;
}

extension type RoomId._(String matrixId) {
RoomId(this.matrixId) {
matrixId._validate(sigil);
}

RoomId.from(String localpart, [String? domain])
: matrixId = '$sigil$localpart${domain == null ? '' : ':$domain'}';

static const String sigil = '!';

static RoomId? tryParse(String string) {
try {
return RoomId(string);
} catch (_) {
return null;
}
}

void validate() => matrixId._validate(sigil);
String get localpart => matrixId._localpart;
String? get domain => matrixId._domain;
}

extension type RoomAlias._(String matrixId) {
RoomAlias(this.matrixId) {
matrixId._validate(sigil);
}

RoomAlias.from(String localpart, String domain)
: matrixId = '$sigil$localpart:$domain';

static const String sigil = '#';

static RoomAlias? tryParse(String string) {
try {
return RoomAlias(string);
} catch (_) {
return null;
}
}

void validate() => matrixId._validate(sigil);
String get localpart => matrixId._localpart;
String get domain => matrixId._domain!;
}

extension type EventId._(String matrixId) {
EventId(this.matrixId) {
matrixId._validate(sigil);
}

EventId.from(String localpart, [String? domain])
: matrixId = '$sigil$localpart${domain == null ? '' : ':$domain'}';

static const String sigil = '\$';

static EventId? tryParse(String string) {
try {
return EventId(string);
} catch (_) {
return null;
}
}

void validate() => matrixId._validate(sigil);
String get localpart => matrixId._localpart;
String? get domain => matrixId._domain;
}
2 changes: 1 addition & 1 deletion lib/src/client.dart
Original file line number Diff line number Diff line change
Expand Up @@ -428,7 +428,7 @@ class Client extends MatrixApi {
return '$clientName-$_transactionCounter-${DateTime.now().millisecondsSinceEpoch}';
}

Room? getRoomByAlias(String alias) {
Room? getRoomByAlias(RoomAlias alias) {
for (final room in rooms) {
if (room.canonicalAlias == alias) return room;
}
Expand Down
22 changes: 13 additions & 9 deletions lib/src/room.dart
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,7 @@ class Room {
]) {
if (name.isNotEmpty) return name;

final canonicalAlias = this.canonicalAlias.localpart;
final canonicalAlias = this.canonicalAlias?.localpart;
if (canonicalAlias != null && canonicalAlias.isNotEmpty) {
return canonicalAlias;
}
Expand Down Expand Up @@ -322,17 +322,20 @@ class Room {
}

/// The address in the format: #roomname:homeserver.org.
String get canonicalAlias {
final alias = getState(EventTypes.RoomCanonicalAlias)?.content['alias'];
return (alias is String) ? alias : '';
RoomAlias? get canonicalAlias {
final alias = getState(EventTypes.RoomCanonicalAlias)
?.content
.tryGet<String>('alias');
if (alias == null) return null;
return RoomAlias.tryParse(alias);
}

/// Sets the canonical alias. If the [canonicalAlias] is not yet an alias of
/// this room, it will create one.
Future<void> setCanonicalAlias(String canonicalAlias) async {
Future<void> setCanonicalAlias(RoomAlias canonicalAlias) async {
final aliases = await client.getLocalAliases(id);
if (!aliases.contains(canonicalAlias)) {
await client.setRoomAlias(canonicalAlias, id);
if (!aliases.contains(canonicalAlias.matrixId)) {
await client.setRoomAlias(canonicalAlias.matrixId, id);
}
await client.setRoomStateWithKey(id, EventTypes.RoomCanonicalAlias, '', {
'alias': canonicalAlias,
Expand Down Expand Up @@ -2661,9 +2664,10 @@ class Room {

/// Generates a matrix.to link with appropriate routing info to share the room
Future<Uri> matrixToInviteLink() async {
if (canonicalAlias.isNotEmpty) {
final canonicalAlias = this.canonicalAlias;
if (canonicalAlias != null) {
return Uri.parse(
'https://matrix.to/#/${Uri.encodeComponent(canonicalAlias)}',
'https://matrix.to/#/${Uri.encodeComponent(canonicalAlias.matrixId)}',
);
}
final List queryParameters = [];
Expand Down
6 changes: 4 additions & 2 deletions lib/src/utils/push_notification.dart
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
import 'dart:convert';

import 'package:matrix/matrix_api_lite.dart';

/// Push Notification object from https://spec.matrix.org/v1.2/push-gateway-api/
class PushNotification {
final Map<String, Object?>? content;
final PushNotificationCounts? counts;
final List<PushNotificationDevice>? devices;
final String? eventId;
final String? prio;
final String? roomAlias;
final RoomAlias? roomAlias;
final String? roomId;
final String? roomName;
final String? sender;
Expand Down Expand Up @@ -57,7 +59,7 @@ class PushNotification {
: null,
eventId: json['event_id'] as String?,
prio: json['prio'] as String?,
roomAlias: json['room_alias'] as String?,
roomAlias: json['room_alias'] as RoomAlias?,
roomId: json['room_id'] as String?,
roomName: json['room_name'] as String?,
sender: json['sender'] as String?,
Expand Down
2 changes: 1 addition & 1 deletion pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ repository: https://github.com/famedly/matrix-dart-sdk.git
issue_tracker: https://github.com/famedly/matrix-dart-sdk/issues

environment:
sdk: ">=3.0.0 <4.0.0"
sdk: ">=3.3.0 <4.0.0"

dependencies:
async: ^2.8.0
Expand Down
4 changes: 2 additions & 2 deletions test/client_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -299,7 +299,7 @@ void main() {

expect(
matrix.getRoomByAlias(
"#famedlyContactDiscovery:${matrix.userID!.split(":")[1]}",
RoomAlias.from('famedlyContactDiscovery', matrix.userID!.domain!),
),
null,
);
Expand Down Expand Up @@ -1707,7 +1707,7 @@ void main() {
'msgtype': 'm.text',
'body': 'Hello world',
},
roomAlias: '#testalias:blaaa',
roomAlias: RoomAlias('#testalias:blaaa'),
roomName: 'TestRoomName',
sender: '@alicyy:example.com',
senderDisplayName: 'AlicE',
Expand Down
Loading
Loading