Philipp Hörist pushed to branch master at gajim / gajim
Commits:
-
b84ef377
by mjk at 2022-11-30T21:05:07+00:00
-
ec0008b7
by mjk at 2022-11-30T21:17:13+00:00
-
88989297
by mjk at 2022-11-30T21:19:03+00:00
8 changed files:
- .gitlab/issue_templates/Release.md
- gajim/common/const.py
- gajim/common/helpers.py
- + gajim/common/iana.py
- gajim/common/setting_values.py
- gajim/common/styling.py
- + scripts/get_iana_data.py
- test/no_gui/test_styling.py
Changes:
| ... | ... | @@ -2,6 +2,7 @@ |
| 2 | 2 | |
| 3 | 3 | ## Build
|
| 4 | 4 | * [ ] Merge translations from Weblate
|
| 5 | +* [ ] Update IANA data with `./scripts/get_iana_data.py gajim/common/iana.py`
|
|
| 5 | 6 | * [ ] Run `./scripts/bump_version.py x.x.x`
|
| 6 | 7 | * [ ] Push release tag `x.x.x`
|
| 7 | 8 |
| ... | ... | @@ -944,97 +944,6 @@ def is_active(self): |
| 944 | 944 | GAJIM_WIKI_URI = 'https://dev.gajim.org/gajim/gajim/wikis'
|
| 945 | 945 | GAJIM_SUPPORT_JID = 'gajim@conference.gajim.org'
|
| 946 | 946 | |
| 947 | -URI_SCHEMES = {
|
|
| 948 | - 'aaa://',
|
|
| 949 | - 'aaas://',
|
|
| 950 | - 'acap://',
|
|
| 951 | - 'cap://',
|
|
| 952 | - 'cid:',
|
|
| 953 | - 'crid://',
|
|
| 954 | - 'data:',
|
|
| 955 | - 'dav:',
|
|
| 956 | - 'dict://',
|
|
| 957 | - 'dns:',
|
|
| 958 | - 'fax:',
|
|
| 959 | - 'file:/',
|
|
| 960 | - 'ftp://',
|
|
| 961 | - 'geo:',
|
|
| 962 | - 'go:',
|
|
| 963 | - 'gopher://',
|
|
| 964 | - 'h323:',
|
|
| 965 | - 'http://',
|
|
| 966 | - 'https://',
|
|
| 967 | - 'iax:',
|
|
| 968 | - 'icap://',
|
|
| 969 | - 'im:',
|
|
| 970 | - 'imap://',
|
|
| 971 | - 'info:',
|
|
| 972 | - 'ipp://',
|
|
| 973 | - 'iris:',
|
|
| 974 | - 'iris.beep:',
|
|
| 975 | - 'iris.xpc:',
|
|
| 976 | - 'iris.xpcs:',
|
|
| 977 | - 'iris.lwz:',
|
|
| 978 | - 'ldap://',
|
|
| 979 | - 'mailto:',
|
|
| 980 | - 'mid:',
|
|
| 981 | - 'modem:',
|
|
| 982 | - 'msrp://',
|
|
| 983 | - 'msrps://',
|
|
| 984 | - 'mtqp://',
|
|
| 985 | - 'mupdate://',
|
|
| 986 | - 'news:',
|
|
| 987 | - 'nfs://',
|
|
| 988 | - 'nntp://',
|
|
| 989 | - 'opaquelocktoken:',
|
|
| 990 | - 'pop://',
|
|
| 991 | - 'pres:',
|
|
| 992 | - 'prospero://',
|
|
| 993 | - 'rtsp://',
|
|
| 994 | - 'service:',
|
|
| 995 | - 'sip:',
|
|
| 996 | - 'sips:',
|
|
| 997 | - 'sms:',
|
|
| 998 | - 'snmp://',
|
|
| 999 | - 'soap.beep://',
|
|
| 1000 | - 'soap.beeps://',
|
|
| 1001 | - 'tag:',
|
|
| 1002 | - 'tel:',
|
|
| 1003 | - 'telnet://',
|
|
| 1004 | - 'tftp://',
|
|
| 1005 | - 'thismessage:/',
|
|
| 1006 | - 'tip://',
|
|
| 1007 | - 'tv:',
|
|
| 1008 | - 'urn://',
|
|
| 1009 | - 'vemmi://',
|
|
| 1010 | - 'xmlrpc.beep://',
|
|
| 1011 | - 'xmlrpc.beeps://',
|
|
| 1012 | - 'xmpp:',
|
|
| 1013 | - 'z39.50r://',
|
|
| 1014 | - 'z39.50s://',
|
|
| 1015 | - 'about:',
|
|
| 1016 | - 'apt:',
|
|
| 1017 | - 'cvs://',
|
|
| 1018 | - 'daap://',
|
|
| 1019 | - 'ed2k://',
|
|
| 1020 | - 'feed:',
|
|
| 1021 | - 'fish://',
|
|
| 1022 | - 'git://',
|
|
| 1023 | - 'iax2:',
|
|
| 1024 | - 'irc://',
|
|
| 1025 | - 'ircs://',
|
|
| 1026 | - 'ldaps://',
|
|
| 1027 | - 'magnet:',
|
|
| 1028 | - 'mms://',
|
|
| 1029 | - 'rsync://',
|
|
| 1030 | - 'ssh://',
|
|
| 1031 | - 'svn://',
|
|
| 1032 | - 'sftp://',
|
|
| 1033 | - 'smb://',
|
|
| 1034 | - 'webcal://',
|
|
| 1035 | - 'aesgcm://',
|
|
| 1036 | -}
|
|
| 1037 | - |
|
| 1038 | 947 | |
| 1039 | 948 | # This is an excerpt of Media Types from
|
| 1040 | 949 | # https://www.iana.org/assignments/media-types/media-types.xhtml
|
| ... | ... | @@ -87,6 +87,7 @@ |
| 87 | 87 | |
| 88 | 88 | from gajim.common import app
|
| 89 | 89 | from gajim.common import configpaths
|
| 90 | +from gajim.common import iana
|
|
| 90 | 91 | from gajim.common.i18n import Q_
|
| 91 | 92 | from gajim.common.i18n import _
|
| 92 | 93 | from gajim.common.i18n import ngettext
|
| ... | ... | @@ -922,6 +923,12 @@ def func_wrapper(self, *args, **kwargs): |
| 922 | 923 | return func_wrapper
|
| 923 | 924 | |
| 924 | 925 | |
| 926 | +def is_registered_uri_scheme(scheme: str) -> bool:
|
|
| 927 | + if scheme in iana.URI_SCHEMES:
|
|
| 928 | + return True
|
|
| 929 | + return scheme in app.settings.get('additional_uri_schemes').split()
|
|
| 930 | + |
|
| 931 | + |
|
| 925 | 932 | def parse_xmpp_uri_query(pct_iquerycomp: str) -> tuple[str, dict[str, str]]:
|
| 926 | 933 | '''
|
| 927 | 934 | Parses 'mess%61ge;b%6Fdy=Hello%20%F0%9F%8C%90%EF%B8%8F' into
|
| ... | ... | @@ -959,6 +966,9 @@ def parse_uri(uri: str) -> URI: |
| 959 | 966 | |
| 960 | 967 | scheme = urlparts.scheme # urlparse is expected to return it in lower case
|
| 961 | 968 | |
| 969 | + if not is_registered_uri_scheme(scheme):
|
|
| 970 | + return URI(URIType.INVALID, uri, data={'error': 'Unregistered scheme'})
|
|
| 971 | + |
|
| 962 | 972 | if scheme in ('https', 'http'):
|
| 963 | 973 | if not urlparts.netloc:
|
| 964 | 974 | err = f'No host or empty host in an {scheme} URI'
|
| 1 | +# Generated by get_iana_data.py @ 2022-11-30T19:55:30.680829
|
|
| 2 | + |
|
| 3 | +URI_SCHEMES = {
|
|
| 4 | + 'aaa',
|
|
| 5 | + 'aaas',
|
|
| 6 | + 'about',
|
|
| 7 | + 'acap',
|
|
| 8 | + 'acct',
|
|
| 9 | + 'acd',
|
|
| 10 | + 'acr',
|
|
| 11 | + 'adiumxtra',
|
|
| 12 | + 'adt',
|
|
| 13 | + 'afp',
|
|
| 14 | + 'afs',
|
|
| 15 | + 'aim',
|
|
| 16 | + 'amss',
|
|
| 17 | + 'android',
|
|
| 18 | + 'appdata',
|
|
| 19 | + 'apt',
|
|
| 20 | + 'ar',
|
|
| 21 | + 'ark',
|
|
| 22 | + 'attachment',
|
|
| 23 | + 'aw',
|
|
| 24 | + 'barion',
|
|
| 25 | + 'bb',
|
|
| 26 | + 'beshare',
|
|
| 27 | + 'bitcoin',
|
|
| 28 | + 'bitcoincash',
|
|
| 29 | + 'blob',
|
|
| 30 | + 'bolo',
|
|
| 31 | + 'browserext',
|
|
| 32 | + 'cabal',
|
|
| 33 | + 'calculator',
|
|
| 34 | + 'callto',
|
|
| 35 | + 'cap',
|
|
| 36 | + 'cast',
|
|
| 37 | + 'casts',
|
|
| 38 | + 'chrome',
|
|
| 39 | + 'chrome-extension',
|
|
| 40 | + 'cid',
|
|
| 41 | + 'coap',
|
|
| 42 | + 'coap+tcp',
|
|
| 43 | + 'coap+ws',
|
|
| 44 | + 'coaps',
|
|
| 45 | + 'coaps+tcp',
|
|
| 46 | + 'coaps+ws',
|
|
| 47 | + 'com-eventbrite-attendee',
|
|
| 48 | + 'content',
|
|
| 49 | + 'content-type',
|
|
| 50 | + 'crid',
|
|
| 51 | + 'cstr',
|
|
| 52 | + 'cvs',
|
|
| 53 | + 'dab',
|
|
| 54 | + 'dat',
|
|
| 55 | + 'data',
|
|
| 56 | + 'dav',
|
|
| 57 | + 'diaspora',
|
|
| 58 | + 'dict',
|
|
| 59 | + 'did',
|
|
| 60 | + 'dis',
|
|
| 61 | + 'dlna-playcontainer',
|
|
| 62 | + 'dlna-playsingle',
|
|
| 63 | + 'dns',
|
|
| 64 | + 'dntp',
|
|
| 65 | + 'doi',
|
|
| 66 | + 'dpp',
|
|
| 67 | + 'drm',
|
|
| 68 | + 'drop',
|
|
| 69 | + 'dtmi',
|
|
| 70 | + 'dtn',
|
|
| 71 | + 'dvb',
|
|
| 72 | + 'dvx',
|
|
| 73 | + 'dweb',
|
|
| 74 | + 'ed2k',
|
|
| 75 | + 'eid',
|
|
| 76 | + 'elsi',
|
|
| 77 | + 'embedded',
|
|
| 78 | + 'ens',
|
|
| 79 | + 'ethereum',
|
|
| 80 | + 'example',
|
|
| 81 | + 'facetime',
|
|
| 82 | + 'fax',
|
|
| 83 | + 'feed',
|
|
| 84 | + 'feedready',
|
|
| 85 | + 'fido',
|
|
| 86 | + 'file',
|
|
| 87 | + 'filesystem',
|
|
| 88 | + 'finger',
|
|
| 89 | + 'first-run-pen-experience',
|
|
| 90 | + 'fish',
|
|
| 91 | + 'fm',
|
|
| 92 | + 'ftp',
|
|
| 93 | + 'fuchsia-pkg',
|
|
| 94 | + 'geo',
|
|
| 95 | + 'gg',
|
|
| 96 | + 'git',
|
|
| 97 | + 'gitoid',
|
|
| 98 | + 'gizmoproject',
|
|
| 99 | + 'go',
|
|
| 100 | + 'gopher',
|
|
| 101 | + 'graph',
|
|
| 102 | + 'grd',
|
|
| 103 | + 'gtalk',
|
|
| 104 | + 'h323',
|
|
| 105 | + 'ham',
|
|
| 106 | + 'hcap',
|
|
| 107 | + 'hcp',
|
|
| 108 | + 'http',
|
|
| 109 | + 'https',
|
|
| 110 | + 'hxxp',
|
|
| 111 | + 'hxxps',
|
|
| 112 | + 'hydrazone',
|
|
| 113 | + 'hyper',
|
|
| 114 | + 'iax',
|
|
| 115 | + 'icap',
|
|
| 116 | + 'icon',
|
|
| 117 | + 'im',
|
|
| 118 | + 'imap',
|
|
| 119 | + 'info',
|
|
| 120 | + 'iotdisco',
|
|
| 121 | + 'ipfs',
|
|
| 122 | + 'ipn',
|
|
| 123 | + 'ipns',
|
|
| 124 | + 'ipp',
|
|
| 125 | + 'ipps',
|
|
| 126 | + 'irc',
|
|
| 127 | + 'irc6',
|
|
| 128 | + 'ircs',
|
|
| 129 | + 'iris',
|
|
| 130 | + 'iris.beep',
|
|
| 131 | + 'iris.lwz',
|
|
| 132 | + 'iris.xpc',
|
|
| 133 | + 'iris.xpcs',
|
|
| 134 | + 'isostore',
|
|
| 135 | + 'itms',
|
|
| 136 | + 'jabber',
|
|
| 137 | + 'jar',
|
|
| 138 | + 'jms',
|
|
| 139 | + 'keyparc',
|
|
| 140 | + 'lastfm',
|
|
| 141 | + 'lbry',
|
|
| 142 | + 'ldap',
|
|
| 143 | + 'ldaps',
|
|
| 144 | + 'leaptofrogans',
|
|
| 145 | + 'lorawan',
|
|
| 146 | + 'lpa',
|
|
| 147 | + 'lvlt',
|
|
| 148 | + 'magnet',
|
|
| 149 | + 'mailserver',
|
|
| 150 | + 'mailto',
|
|
| 151 | + 'maps',
|
|
| 152 | + 'market',
|
|
| 153 | + 'matrix',
|
|
| 154 | + 'message',
|
|
| 155 | + 'microsoft.windows.camera',
|
|
| 156 | + 'microsoft.windows.camera.multipicker',
|
|
| 157 | + 'microsoft.windows.camera.picker',
|
|
| 158 | + 'mid',
|
|
| 159 | + 'mms',
|
|
| 160 | + 'modem',
|
|
| 161 | + 'mongodb',
|
|
| 162 | + 'moz',
|
|
| 163 | + 'ms-access',
|
|
| 164 | + 'ms-appinstaller',
|
|
| 165 | + 'ms-browser-extension',
|
|
| 166 | + 'ms-calculator',
|
|
| 167 | + 'ms-drive-to',
|
|
| 168 | + 'ms-enrollment',
|
|
| 169 | + 'ms-excel',
|
|
| 170 | + 'ms-eyecontrolspeech',
|
|
| 171 | + 'ms-gamebarservices',
|
|
| 172 | + 'ms-gamingoverlay',
|
|
| 173 | + 'ms-getoffice',
|
|
| 174 | + 'ms-help',
|
|
| 175 | + 'ms-infopath',
|
|
| 176 | + 'ms-inputapp',
|
|
| 177 | + 'ms-lockscreencomponent-config',
|
|
| 178 | + 'ms-media-stream-id',
|
|
| 179 | + 'ms-meetnow',
|
|
| 180 | + 'ms-mixedrealitycapture',
|
|
| 181 | + 'ms-mobileplans',
|
|
| 182 | + 'ms-newsandinterests',
|
|
| 183 | + 'ms-officeapp',
|
|
| 184 | + 'ms-people',
|
|
| 185 | + 'ms-project',
|
|
| 186 | + 'ms-powerpoint',
|
|
| 187 | + 'ms-publisher',
|
|
| 188 | + 'ms-restoretabcompanion',
|
|
| 189 | + 'ms-screenclip',
|
|
| 190 | + 'ms-screensketch',
|
|
| 191 | + 'ms-search',
|
|
| 192 | + 'ms-search-repair',
|
|
| 193 | + 'ms-secondary-screen-controller',
|
|
| 194 | + 'ms-secondary-screen-setup',
|
|
| 195 | + 'ms-settings',
|
|
| 196 | + 'ms-settings-airplanemode',
|
|
| 197 | + 'ms-settings-bluetooth',
|
|
| 198 | + 'ms-settings-camera',
|
|
| 199 | + 'ms-settings-cellular',
|
|
| 200 | + 'ms-settings-cloudstorage',
|
|
| 201 | + 'ms-settings-connectabledevices',
|
|
| 202 | + 'ms-settings-displays-topology',
|
|
| 203 | + 'ms-settings-emailandaccounts',
|
|
| 204 | + 'ms-settings-language',
|
|
| 205 | + 'ms-settings-location',
|
|
| 206 | + 'ms-settings-lock',
|
|
| 207 | + 'ms-settings-nfctransactions',
|
|
| 208 | + 'ms-settings-notifications',
|
|
| 209 | + 'ms-settings-power',
|
|
| 210 | + 'ms-settings-privacy',
|
|
| 211 | + 'ms-settings-proximity',
|
|
| 212 | + 'ms-settings-screenrotation',
|
|
| 213 | + 'ms-settings-wifi',
|
|
| 214 | + 'ms-settings-workplace',
|
|
| 215 | + 'ms-spd',
|
|
| 216 | + 'ms-stickers',
|
|
| 217 | + 'ms-sttoverlay',
|
|
| 218 | + 'ms-transit-to',
|
|
| 219 | + 'ms-useractivityset',
|
|
| 220 | + 'ms-virtualtouchpad',
|
|
| 221 | + 'ms-visio',
|
|
| 222 | + 'ms-walk-to',
|
|
| 223 | + 'ms-whiteboard',
|
|
| 224 | + 'ms-whiteboard-cmd',
|
|
| 225 | + 'ms-word',
|
|
| 226 | + 'msnim',
|
|
| 227 | + 'msrp',
|
|
| 228 | + 'msrps',
|
|
| 229 | + 'mss',
|
|
| 230 | + 'mt',
|
|
| 231 | + 'mtqp',
|
|
| 232 | + 'mumble',
|
|
| 233 | + 'mupdate',
|
|
| 234 | + 'mvn',
|
|
| 235 | + 'news',
|
|
| 236 | + 'nfs',
|
|
| 237 | + 'ni',
|
|
| 238 | + 'nih',
|
|
| 239 | + 'nntp',
|
|
| 240 | + 'notes',
|
|
| 241 | + 'num',
|
|
| 242 | + 'ocf',
|
|
| 243 | + 'oid',
|
|
| 244 | + 'onenote',
|
|
| 245 | + 'onenote-cmd',
|
|
| 246 | + 'opaquelocktoken',
|
|
| 247 | + 'openpgp4fpr',
|
|
| 248 | + 'otpauth',
|
|
| 249 | + 'p1',
|
|
| 250 | + 'pack',
|
|
| 251 | + 'palm',
|
|
| 252 | + 'paparazzi',
|
|
| 253 | + 'payment',
|
|
| 254 | + 'payto',
|
|
| 255 | + 'pkcs11',
|
|
| 256 | + 'platform',
|
|
| 257 | + 'pop',
|
|
| 258 | + 'pres',
|
|
| 259 | + 'prospero',
|
|
| 260 | + 'proxy',
|
|
| 261 | + 'pwid',
|
|
| 262 | + 'psyc',
|
|
| 263 | + 'pttp',
|
|
| 264 | + 'qb',
|
|
| 265 | + 'query',
|
|
| 266 | + 'quic-transport',
|
|
| 267 | + 'redis',
|
|
| 268 | + 'rediss',
|
|
| 269 | + 'reload',
|
|
| 270 | + 'res',
|
|
| 271 | + 'resource',
|
|
| 272 | + 'rmi',
|
|
| 273 | + 'rsync',
|
|
| 274 | + 'rtmfp',
|
|
| 275 | + 'rtmp',
|
|
| 276 | + 'rtsp',
|
|
| 277 | + 'rtsps',
|
|
| 278 | + 'rtspu',
|
|
| 279 | + 'sarif',
|
|
| 280 | + 'secondlife',
|
|
| 281 | + 'secret-token',
|
|
| 282 | + 'service',
|
|
| 283 | + 'session',
|
|
| 284 | + 'sftp',
|
|
| 285 | + 'sgn',
|
|
| 286 | + 'shc',
|
|
| 287 | + 'shttp',
|
|
| 288 | + 'sieve',
|
|
| 289 | + 'simpleledger',
|
|
| 290 | + 'simplex',
|
|
| 291 | + 'sip',
|
|
| 292 | + 'sips',
|
|
| 293 | + 'skype',
|
|
| 294 | + 'smb',
|
|
| 295 | + 'smp',
|
|
| 296 | + 'sms',
|
|
| 297 | + 'smtp',
|
|
| 298 | + 'snews',
|
|
| 299 | + 'snmp',
|
|
| 300 | + 'soap.beep',
|
|
| 301 | + 'soap.beeps',
|
|
| 302 | + 'soldat',
|
|
| 303 | + 'spiffe',
|
|
| 304 | + 'spotify',
|
|
| 305 | + 'ssb',
|
|
| 306 | + 'ssh',
|
|
| 307 | + 'steam',
|
|
| 308 | + 'stun',
|
|
| 309 | + 'stuns',
|
|
| 310 | + 'submit',
|
|
| 311 | + 'svn',
|
|
| 312 | + 'swh',
|
|
| 313 | + 'swid',
|
|
| 314 | + 'swidpath',
|
|
| 315 | + 'tag',
|
|
| 316 | + 'taler',
|
|
| 317 | + 'teamspeak',
|
|
| 318 | + 'tel',
|
|
| 319 | + 'teliaeid',
|
|
| 320 | + 'telnet',
|
|
| 321 | + 'tftp',
|
|
| 322 | + 'things',
|
|
| 323 | + 'thismessage',
|
|
| 324 | + 'tip',
|
|
| 325 | + 'tn3270',
|
|
| 326 | + 'tool',
|
|
| 327 | + 'turn',
|
|
| 328 | + 'turns',
|
|
| 329 | + 'tv',
|
|
| 330 | + 'udp',
|
|
| 331 | + 'unreal',
|
|
| 332 | + 'upt',
|
|
| 333 | + 'urn',
|
|
| 334 | + 'ut2004',
|
|
| 335 | + 'uuid-in-package',
|
|
| 336 | + 'v-event',
|
|
| 337 | + 'vemmi',
|
|
| 338 | + 'ventrilo',
|
|
| 339 | + 'ves',
|
|
| 340 | + 'videotex',
|
|
| 341 | + 'vnc',
|
|
| 342 | + 'view-source',
|
|
| 343 | + 'vscode',
|
|
| 344 | + 'vscode-insiders',
|
|
| 345 | + 'vsls',
|
|
| 346 | + 'w3',
|
|
| 347 | + 'wais',
|
|
| 348 | + 'web3',
|
|
| 349 | + 'wcr',
|
|
| 350 | + 'webcal',
|
|
| 351 | + 'wifi',
|
|
| 352 | + 'wpid',
|
|
| 353 | + 'ws',
|
|
| 354 | + 'wss',
|
|
| 355 | + 'wtai',
|
|
| 356 | + 'wyciwyg',
|
|
| 357 | + 'xcon',
|
|
| 358 | + 'xcon-userid',
|
|
| 359 | + 'xfire',
|
|
| 360 | + 'xmlrpc.beep',
|
|
| 361 | + 'xmlrpc.beeps',
|
|
| 362 | + 'xmpp',
|
|
| 363 | + 'xri',
|
|
| 364 | + 'ymsgr',
|
|
| 365 | + 'z39.50',
|
|
| 366 | + 'z39.50r',
|
|
| 367 | + 'z39.50s',
|
|
| 368 | +} |
| ... | ... | @@ -36,6 +36,7 @@ class _ACCOUNT_DEFAULT: |
| 36 | 36 | |
| 37 | 37 | |
| 38 | 38 | BoolSettings = Literal[
|
| 39 | + 'additional_uri_schemes',
|
|
| 39 | 40 | 'allow_open_file_uris',
|
| 40 | 41 | 'always_english_wikipedia',
|
| 41 | 42 | 'always_english_wiktionary',
|
| ... | ... | @@ -164,6 +165,7 @@ class _ACCOUNT_DEFAULT: |
| 164 | 165 | AllSettingsT = Union[str, int, bool, list[str]]
|
| 165 | 166 | |
| 166 | 167 | APP_SETTINGS = {
|
| 168 | + 'additional_uri_schemes': '',
|
|
| 167 | 169 | 'allow_open_file_uris': False,
|
| 168 | 170 | 'always_english_wikipedia': False,
|
| 169 | 171 | 'always_english_wiktionary': True,
|
| ... | ... | @@ -628,6 +630,9 @@ class WorkspaceSettings(TypedDict): |
| 628 | 630 | |
| 629 | 631 | ADVANCED_SETTINGS = {
|
| 630 | 632 | 'app': {
|
| 633 | + 'additional_uri_schemes': _(
|
|
| 634 | + 'Clickable schemes in addition to the hard-coded list of '
|
|
| 635 | + 'IANA-registered ones. Space-separated, lower-case, no colons.'),
|
|
| 631 | 636 | 'allow_open_file_uris': _('Allows opening file:/ URIs when clicked.'),
|
| 632 | 637 | 'always_english_wikipedia': '',
|
| 633 | 638 | 'always_english_wiktionary': '',
|
| ... | ... | @@ -50,7 +50,8 @@ |
| 50 | 50 | BLOCK_NESTED_RX = re.compile(PRE_NESTED_RX + '|' + QUOTE_RX, re.S | re.M)
|
| 51 | 51 | UNQUOTE_RX = re.compile(r'^> |^>', re.M)
|
| 52 | 52 | |
| 53 | -URI_OR_JID_RX = re.compile(fr'(?P<uri>{regex.IRI})|(?P<jid>{regex.XMPP.jid})')
|
|
| 53 | +URI_OR_JID_RX = re.compile(
|
|
| 54 | + fr'(?P<uri>(?<![\w+.-]){regex.IRI})|(?P<jid>{regex.XMPP.jid})')
|
|
| 54 | 55 | |
| 55 | 56 | EMOJI_RX = emoji_data.get_regex()
|
| 56 | 57 | EMOJI_RX = re.compile(EMOJI_RX)
|
| 1 | +#!/usr/bin/env python3
|
|
| 2 | + |
|
| 3 | +import argparse
|
|
| 4 | +from datetime import datetime
|
|
| 5 | +from urllib.request import urlopen
|
|
| 6 | +import csv
|
|
| 7 | +import io
|
|
| 8 | +import logging
|
|
| 9 | +from pathlib import Path
|
|
| 10 | +import re
|
|
| 11 | +import sys
|
|
| 12 | + |
|
| 13 | + |
|
| 14 | +SCHEMES_URL = 'https://www.iana.org/assignments/uri-schemes/uri-schemes-1.csv'
|
|
| 15 | + |
|
| 16 | +logging.basicConfig(level='INFO', format='%(levelname)s: %(message)s')
|
|
| 17 | + |
|
| 18 | + |
|
| 19 | +def download_file(url: str) -> str:
|
|
| 20 | + logging.info('Download: %s', url)
|
|
| 21 | + with urlopen(url) as f:
|
|
| 22 | + content = f.read()
|
|
| 23 | + return content.decode()
|
|
| 24 | + |
|
| 25 | + |
|
| 26 | +def parse_uri_schemes(content: str) -> list[str]:
|
|
| 27 | + logging.info('Parse uri schemes')
|
|
| 28 | + schemes: list[str] = []
|
|
| 29 | + |
|
| 30 | + reader = csv.reader(io.StringIO(content), delimiter=',')
|
|
| 31 | + next(reader) # Skip header row
|
|
| 32 | + for line in reader:
|
|
| 33 | + scheme = line[0].lower().removesuffix(' (obsolete)')
|
|
| 34 | + if not re.fullmatch('[a-z0-9+.-]+', scheme):
|
|
| 35 | + logging.warning('unexpected scheme field contents: %s', scheme)
|
|
| 36 | + continue
|
|
| 37 | + |
|
| 38 | + schemes.append(scheme)
|
|
| 39 | + |
|
| 40 | + return schemes
|
|
| 41 | + |
|
| 42 | + |
|
| 43 | +def generate_output(schemes: list[str],
|
|
| 44 | + out_path: Path):
|
|
| 45 | + |
|
| 46 | + logging.info('Generate output')
|
|
| 47 | + current_date = datetime.utcnow().isoformat()
|
|
| 48 | + content = f'# Generated by get_iana_data.py @ {current_date}\n'
|
|
| 49 | + |
|
| 50 | + content += '\n'
|
|
| 51 | + |
|
| 52 | + content += 'URI_SCHEMES = {\n'
|
|
| 53 | + for scheme in schemes:
|
|
| 54 | + content += f" '{scheme}',\n"
|
|
| 55 | + content += '}\n'
|
|
| 56 | + |
|
| 57 | + outpath.write_text(content)
|
|
| 58 | + logging.info('Wrote file to %s', outpath)
|
|
| 59 | + |
|
| 60 | + |
|
| 61 | +if __name__ == '__main__':
|
|
| 62 | + parser = argparse.ArgumentParser(description='Generate IANA data')
|
|
| 63 | + parser.add_argument('out', help='Path to output file')
|
|
| 64 | + args = parser.parse_args()
|
|
| 65 | + |
|
| 66 | + outpath = Path(args.out)
|
|
| 67 | + if outpath.is_dir():
|
|
| 68 | + sys.exit('Output path is a directory')
|
|
| 69 | + |
|
| 70 | + scheme_content = download_file(SCHEMES_URL)
|
|
| 71 | + schemes = parse_uri_schemes(scheme_content)
|
|
| 72 | + output = generate_output(schemes,
|
|
| 73 | + outpath)
|
|
| 74 | + logging.info('Finished !') |
| 1 | 1 | import unittest
|
| 2 | +from unittest.mock import MagicMock
|
|
| 2 | 3 | |
| 3 | 4 | from gajim import gui
|
| 4 | 5 | gui.init('gtk')
|
| ... | ... | @@ -17,6 +18,10 @@ |
| 17 | 18 | from gajim.common.text_helpers import jid_to_iri
|
| 18 | 19 | |
| 19 | 20 | |
| 21 | +app.settings = MagicMock()
|
|
| 22 | +app.settings.get.return_value = 'a a- a. scheme' # additional_uri_schemes
|
|
| 23 | + |
|
| 24 | + |
|
| 20 | 25 | STYLING = {
|
| 21 | 26 | 'pre cannot have children': {
|
| 22 | 27 | 'input': '_no pre `with *children*`_',
|
| ... | ... | @@ -384,6 +389,14 @@ |
| 384 | 389 | ' ',
|
| 385 | 390 | '.',
|
| 386 | 391 | ':',
|
| 392 | + '://',
|
|
| 393 | + 'Böblingen:🌥',
|
|
| 394 | + 'Bo\u0308blingen:🌥', # finds blingen:🌥 but rejects an unregistered scheme
|
|
| 395 | + 'path/to/file_na.me:123',
|
|
| 396 | + '_sche.me:body_',
|
|
| 397 | + '_sche+me:body_',
|
|
| 398 | + '.scheme:body',
|
|
| 399 | + '+scheme:body',
|
|
| 387 | 400 | |
| 388 | 401 | # These are from https://mathiasbynens.be/demo/url-regex
|
| 389 | 402 | '//',
|
| ... | ... | @@ -391,10 +404,6 @@ |
| 391 | 404 | '///a',
|
| 392 | 405 | '///',
|
| 393 | 406 | 'foo.com',
|
| 394 | - 'http://foo.bar?q=Spaces should be encoded',
|
|
| 395 | - 'http:// shouldfail.com',
|
|
| 396 | - ':// should fail',
|
|
| 397 | - 'http://foo.bar/foo(bar)baz quux',
|
|
| 398 | 407 | ]
|
| 399 | 408 | |
| 400 | 409 | |
| ... | ... | @@ -427,6 +436,8 @@ |
| 427 | 436 | 'file:a',
|
| 428 | 437 | 'file:a/',
|
| 429 | 438 | 'file:a/b',
|
| 439 | + |
|
| 440 | + 'mailtomailto:foo@bar.com.uk',
|
|
| 430 | 441 | ]
|
| 431 | 442 | |
| 432 | 443 | |
| ... | ... | @@ -484,7 +495,6 @@ |
| 484 | 495 | |
| 485 | 496 | # These are from https://rfc-editor.org/rfc/rfc7622#section-3.5
|
| 486 | 497 | '"juliet"@example.com',
|
| 487 | - 'foo bar@example.com', # search is expected to find 'bar@example.com'
|
|
| 488 | 498 | '@example.com',
|
| 489 | 499 | 'henryⅣ@example.com', # localpart has a compatibility-decomposable cp
|
| 490 | 500 | '♚@example.com', # localpart has a symbol cp
|
| ... | ... | @@ -495,13 +505,14 @@ |
| 495 | 505 | URIS_WITH_TEXT = [
|
| 496 | 506 | ('write to my email mailto:foo@bar.com.uk (but not to mailto:bar@foo.com)',
|
| 497 | 507 | ['mailto:foo@bar.com.uk', 'mailto:bar@foo.com']),
|
| 498 | - ('write to my email mailtomailto:foo@bar.com.uk (but not to mailto:bar@foo.com)',
|
|
| 499 | - ['mailtomailto:foo@bar.com.uk', 'mailto:bar@foo.com']),
|
|
| 500 | 508 | ('see this http://userid@example.com/ link', ['http://userid@example.com/']),
|
| 501 | 509 | ('see this http://userid@example.com/, and ..', ['http://userid@example.com/']),
|
| 502 | 510 | ('<http://userid@example.com/>', ['http://userid@example.com/']),
|
| 503 | 511 | ('"http://userid@example.com/"', ['http://userid@example.com/']),
|
| 504 | 512 | ('regexes are useless (see https://en.wikipedia.org/wiki/Recursion_(computer_science)), but comfy', ['https://en.wikipedia.org/wiki/Recursion_(computer_science)']),
|
| 513 | + ('(scheme:body)', ['scheme:body']),
|
|
| 514 | + ('/scheme:body', ['scheme:body']),
|
|
| 515 | + ('!scheme:body', ['scheme:body']),
|
|
| 505 | 516 | ]
|
| 506 | 517 | |
| 507 | 518 | |
| ... | ... | @@ -519,41 +530,32 @@ def test_uris(self): |
| 519 | 530 | for uri in URIS:
|
| 520 | 531 | text = self.wrap(uri)
|
| 521 | 532 | hlinks = process_uris(text)
|
| 522 | - self.assertEqual(len(hlinks), 1, text)
|
|
| 523 | - self.assertEqual(hlinks[0].uri, uri, text)
|
|
| 533 | + self.assertEqual([l.uri for l in hlinks], [uri], text)
|
|
| 524 | 534 | |
| 525 | 535 | def test_invalid_uris(self):
|
| 526 | 536 | for foo in NONURIS + UNACCEPTABLE_URIS:
|
| 527 | 537 | text = self.wrap(foo)
|
| 528 | 538 | hlinks = process_uris(text)
|
| 529 | - if len(hlinks) == 0:
|
|
| 530 | - continue
|
|
| 531 | - self.assertEqual(len(hlinks), 1, text)
|
|
| 532 | - self.assertNotEqual(hlinks[0].text, foo, text)
|
|
| 539 | + self.assertEqual([l.text for l in hlinks], [], text)
|
|
| 533 | 540 | |
| 534 | 541 | def test_jids(self):
|
| 535 | 542 | for jid in JIDS:
|
| 536 | 543 | text = self.wrap(jid)
|
| 544 | + uri = jid_to_iri(jid)
|
|
| 537 | 545 | hlinks = process_uris(text)
|
| 538 | - self.assertEqual(len(hlinks), 1, text)
|
|
| 539 | - self.assertEqual(hlinks[0].text, jid, text)
|
|
| 540 | - self.assertEqual(hlinks[0].uri, jid_to_iri(jid), text)
|
|
| 546 | + self.assertEqual([(l.text, l.uri) for l in hlinks],
|
|
| 547 | + [(jid, uri)], text)
|
|
| 541 | 548 | |
| 542 | 549 | def test_nonjids(self):
|
| 543 | 550 | for foo in NONJIDS:
|
| 544 | 551 | text = self.wrap(foo)
|
| 545 | 552 | hlinks = process_uris(text)
|
| 546 | - if len(hlinks) == 0:
|
|
| 547 | - continue
|
|
| 548 | - self.assertEqual(len(hlinks), 1, text)
|
|
| 549 | - self.assertNotEqual(hlinks[0].text, foo, text)
|
|
| 553 | + self.assertEqual([l.text for l in hlinks], [], text)
|
|
| 550 | 554 | |
| 551 | 555 | def test_uris_with_text(self):
|
| 552 | - for text, result in URIS_WITH_TEXT:
|
|
| 556 | + for text, results in URIS_WITH_TEXT:
|
|
| 553 | 557 | hlinks = process_uris(text)
|
| 554 | - self.assertEqual(len(hlinks), len(result), text)
|
|
| 555 | - for i, res in enumerate(result):
|
|
| 556 | - self.assertEqual(hlinks[i].text, res, text)
|
|
| 558 | + self.assertEqual([l.text for l in hlinks], results, text)
|
|
| 557 | 559 | |
| 558 | 560 | |
| 559 | 561 | if __name__ == "__main__":
|