LCOV - code coverage report
Current view: top level - lib/src/utils - matrix_id_string_extension.dart (source / functions) Coverage Total Hit
Test: merged.info Lines: 100.0 % 47 47
Test Date: 2025-01-14 13:39:53 Functions: - 0 0

            Line data    Source code
       1              : /*
       2              :  *   Famedly Matrix SDK
       3              :  *   Copyright (C) 2020, 2021 Famedly GmbH
       4              :  *
       5              :  *   This program is free software: you can redistribute it and/or modify
       6              :  *   it under the terms of the GNU Affero General Public License as
       7              :  *   published by the Free Software Foundation, either version 3 of the
       8              :  *   License, or (at your option) any later version.
       9              :  *
      10              :  *   This program is distributed in the hope that it will be useful,
      11              :  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
      12              :  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
      13              :  *   GNU Affero General Public License for more details.
      14              :  *
      15              :  *   You should have received a copy of the GNU Affero General Public License
      16              :  *   along with this program.  If not, see <https://www.gnu.org/licenses/>.
      17              :  */
      18              : 
      19              : const Set<String> validSigils = {'@', '!', '#', '\$', '+'};
      20              : 
      21              : const int maxLength = 255;
      22              : 
      23              : extension MatrixIdExtension on String {
      24           35 :   List<String> _getParts() {
      25           35 :     final s = substring(1);
      26           35 :     final ix = s.indexOf(':');
      27           70 :     if (ix == -1) {
      28            4 :       return [substring(1)];
      29              :     }
      30          140 :     return [s.substring(0, ix), s.substring(ix + 1)];
      31              :   }
      32              : 
      33           35 :   bool get isValidMatrixId {
      34           35 :     if (isEmpty) return false;
      35           70 :     if (length > maxLength) return false;
      36           70 :     if (!validSigils.contains(substring(0, 1))) {
      37              :       return false;
      38              :     }
      39              :     // event IDs do not have to have a domain
      40           70 :     if (substring(0, 1) == '\$') {
      41              :       return true;
      42              :     }
      43              :     // all other matrix IDs have to have a domain
      44           35 :     final parts = _getParts();
      45              :     // the localpart can be an empty string, e.g. for aliases
      46          140 :     if (parts.length != 2 || parts[1].isEmpty) {
      47              :       return false;
      48              :     }
      49              :     return true;
      50              :   }
      51              : 
      52            6 :   String? get sigil => isValidMatrixId ? substring(0, 1) : null;
      53              : 
      54          140 :   String? get localpart => isValidMatrixId ? _getParts().first : null;
      55              : 
      56          132 :   String? get domain => isValidMatrixId ? _getParts().last : null;
      57              : 
      58            8 :   bool equals(String? other) => toLowerCase() == other?.toLowerCase();
      59              : 
      60              :   /// Parse a matrix identifier string into a Uri. Primary and secondary identifiers
      61              :   /// are stored in pathSegments. The query string is stored as such.
      62            2 :   Uri? _parseIdentifierIntoUri() {
      63              :     const matrixUriPrefix = 'matrix:';
      64              :     const matrixToPrefix = 'https://matrix.to/#/';
      65            4 :     if (toLowerCase().startsWith(matrixUriPrefix)) {
      66            2 :       final uri = Uri.tryParse(this);
      67              :       if (uri == null) return null;
      68            2 :       final pathSegments = uri.pathSegments;
      69            2 :       final identifiers = <String>[];
      70            8 :       for (var i = 0; i < pathSegments.length - 1; i += 2) {
      71            2 :         final thisSigil = {
      72              :           'u': '@',
      73              :           'roomid': '!',
      74              :           'r': '#',
      75              :           'e': '\$',
      76            6 :         }[pathSegments[i].toLowerCase()];
      77              :         if (thisSigil == null) {
      78              :           break;
      79              :         }
      80            8 :         identifiers.add(thisSigil + pathSegments[i + 1]);
      81              :       }
      82            2 :       return uri.replace(pathSegments: identifiers);
      83            4 :     } else if (toLowerCase().startsWith(matrixToPrefix)) {
      84            2 :       return Uri.tryParse(
      85           30 :         '//${substring(matrixToPrefix.length - 1).replaceAllMapped(RegExp(r'(?<=/)[#!@+][^:]*:|(\?.*$)'), (m) => m[0]!.replaceAllMapped(RegExp(m.group(1) != null ? '' : '[/?]'), (m) => Uri.encodeComponent(m.group(0)!))).replaceAll('#', '%23')}',
      86              :       );
      87              :     } else {
      88            2 :       return Uri(
      89            2 :         pathSegments: RegExp(r'/((?:[#!@+][^:]*:)?[^/?]*)(?:\?.*$)?')
      90            4 :             .allMatches('/$this')
      91            6 :             .map((m) => m[1]!),
      92            2 :         query: RegExp(r'(?:/(?:[#!@+][^:]*:)?[^/?]*)*\?(.*$)')
      93            6 :             .firstMatch('/$this')?[1],
      94              :       );
      95              :     }
      96              :   }
      97              : 
      98              :   /// Separate a matrix identifier string into a primary indentifier, a secondary identifier,
      99              :   /// a query string and already parsed `via` parameters. A matrix identifier string
     100              :   /// can be an mxid, a matrix.to-url or a matrix-uri.
     101            2 :   MatrixIdentifierStringExtensionResults? parseIdentifierIntoParts() {
     102            2 :     final uri = _parseIdentifierIntoUri();
     103              :     if (uri == null) return null;
     104            8 :     final primary = uri.pathSegments.isNotEmpty ? uri.pathSegments[0] : null;
     105            2 :     if (primary == null || !primary.isValidMatrixId) return null;
     106           10 :     final secondary = uri.pathSegments.length > 1 ? uri.pathSegments[1] : null;
     107            2 :     if (secondary != null && !secondary.isValidMatrixId) return null;
     108              : 
     109            2 :     return MatrixIdentifierStringExtensionResults(
     110              :       primaryIdentifier: primary,
     111              :       secondaryIdentifier: secondary,
     112            6 :       queryString: uri.query.isNotEmpty ? uri.query : null,
     113            8 :       via: (uri.queryParametersAll['via'] ?? []).toSet(),
     114            4 :       action: uri.queryParameters['action'],
     115              :     );
     116              :   }
     117              : }
     118              : 
     119              : class MatrixIdentifierStringExtensionResults {
     120              :   final String primaryIdentifier;
     121              :   final String? secondaryIdentifier;
     122              :   final String? queryString;
     123              :   final Set<String> via;
     124              :   final String? action;
     125              : 
     126            2 :   MatrixIdentifierStringExtensionResults({
     127              :     required this.primaryIdentifier,
     128              :     this.secondaryIdentifier,
     129              :     this.queryString,
     130              :     this.via = const {},
     131              :     this.action,
     132              :   });
     133              : }
        

Generated by: LCOV version 2.0-1