Compare commits
308 Commits
moxxmpp_so
...
master
Author | SHA1 | Date | |
---|---|---|---|
c61ddeb338 | |||
e2515e25e4 | |||
09a849c6eb | |||
9eb94e5f48 | |||
db77790bf4 | |||
7ceee48d31 | |||
941c3e4fd8 | |||
365ff2f238 | |||
b3c8a6cd2f | |||
d4166d087e | |||
ddf781daff | |||
5973076b89 | |||
72cb76d1f6 | |||
be7581e841 | |||
8a2435e4ad | |||
97f082b6f5 | |||
f287d501ab | |||
93e9d6ca22 | |||
007cdce53d | |||
6d3a5e98de | |||
e97d6e6517 | |||
882d20dc7a | |||
1f712151e4 | |||
e7922668b1 | |||
87866bf3f5 | |||
41b789fa28 | |||
0a68f09fb4 | |||
edf1d0b257 | |||
59b90307c2 | |||
49d3c6411b | |||
3a94dd9634 | |||
fb4b4c71e2 | |||
d9fbb9e102 | |||
aba90f2e90 | |||
9211963390 | |||
c7d58c3d3f | |||
6dbbf08be4 | |||
7ca648c478 | |||
814f99436b | |||
5bd2466c54 | |||
14b62cef96 | |||
c3088f9046 | |||
64b93b536e | |||
c1c48d0a83 | |||
4a681b9483 | |||
c504afc944 | |||
76a9f7be7a | |||
afa3927720 | |||
5f36289f50 | |||
fbe3b90200 | |||
d7c13abde6 | |||
d4416c8a47 | |||
9666557655 | |||
1625f912b0 | |||
864cc0e747 | |||
c9e817054d | |||
d57bf2ef80 | |||
8bfdd5e54a | |||
e58082bf38 | |||
dbb945b424 | |||
2431eafa6c | |||
264ab130ee | |||
38dba0e6b7 | |||
94d6fe4925 | |||
c8b903e5df | |||
b14363319a | |||
a18507cc3a | |||
93418f0127 | |||
1e7279e23b | |||
![]() |
b2724aba0c | ||
![]() |
d3742ea156 | ||
b92e825bc1 | |||
![]() |
8b00e85167 | ||
![]() |
04dfc6d2ac | ||
![]() |
9e70e802ef | ||
![]() |
3ebd9b86ec | ||
a928c5c877 | |||
77a1acb0e7 | |||
![]() |
a873edb9ec | ||
![]() |
e6bd6d05cd | ||
05e3d804a4 | |||
b5efc2dfae | |||
![]() |
b7d53b8f47 | ||
![]() |
217c3ac236 | ||
d35b955259 | |||
30dca67fb6 | |||
2db44e2f51 | |||
![]() |
51bca6c25d | ||
4f9a0605c7 | |||
3621f2709a | |||
9da6d319a3 | |||
e3ca83670a | |||
fbbe413148 | |||
![]() |
8728166a4d | ||
![]() |
1f1321b269 | ||
9fd2daabb2 | |||
8252472fae | |||
3cb5a568ce | |||
c2f62e2967 | |||
![]() |
66195f66fa | ||
![]() |
70fdfaf16d | ||
![]() |
cd73f89e63 | ||
![]() |
05c41d3185 | ||
![]() |
64a8de6caa | ||
![]() |
68809469f6 | ||
![]() |
762cf1c77a | ||
29a5417b31 | |||
![]() |
255d0f88e0 | ||
fa2ce7c2d1 | |||
![]() |
fa11a3a384 | ||
![]() |
ac5bb9e461 | ||
aa71d3ed5d | |||
f2d8c6a009 | |||
88545e3308 | |||
925a46c0da | |||
327f695a40 | |||
8266765ff8 | |||
e234c812ff | |||
4ff2992a03 | |||
09fd5845aa | |||
963f3f2cd9 | |||
da1d28a6d6 | |||
cbd90b1163 | |||
f0538b0447 | |||
968604b0ba | |||
60279a84e0 | |||
cf3287ccf4 | |||
1ab0ed856f | |||
10a5046431 | |||
4d76b9f57a | |||
0ec3777f44 | |||
6f5de9c4dc | |||
79d7e3ba64 | |||
8270185027 | |||
9e0f38154e | |||
1475cb542f | |||
b949ec6ff5 | |||
c3be199cca | |||
83ebe58c47 | |||
4db0ef6b34 | |||
b95e75329d | |||
3163101f82 | |||
bd4e1d28ea | |||
b1da6e5a53 | |||
c6552968d5 | |||
1d87c0ce95 | |||
da591a552d | |||
47b679d168 | |||
320f4a8d4c | |||
1fdefacd52 | |||
09b8613c80 | |||
f3d906e69b | |||
483cb0d7f1 | |||
f73daf4d1c | |||
a8693da262 | |||
0fb66f6aca | |||
c9173c49bd | |||
2de1126fa9 | |||
4d312b2100 | |||
c1ad646905 | |||
c88ab940c4 | |||
916be1c927 | |||
5c47c35a46 | |||
7f3538875b | |||
f6efa20ff4 | |||
8443411f07 | |||
dc24b3c48a | |||
f6abf3d5b5 | |||
63c84d9479 | |||
3e43ac22d7 | |||
47d821c02e | |||
355d580a9a | |||
03328bdf7a | |||
275d6e0346 | |||
0d9afd546c | |||
3da334b5cf | |||
2947e2c539 | |||
ac8433f51f | |||
808371b271 | |||
7fdd83ea69 | |||
68e2a65dcf | |||
d977a74446 | |||
29f0419154 | |||
b354ca8d0a | |||
ec6b5ab753 | |||
ce1815d1f3 | |||
fbb495dc2f | |||
4a6aa79e56 | |||
0033d0eb6e | |||
24cb05f91b | |||
91f763ac26 | |||
51edb61443 | |||
4e01d32e90 | |||
f2fe06104c | |||
89fe8f0a9c | |||
9358175925 | |||
564a237986 | |||
cf425917cf | |||
63b7abd6f9 | |||
f460e5ebe9 | |||
af8bc606d6 | |||
30482c86f0 | |||
f86dbe6af8 | |||
478b5b8770 | |||
7ab3f4f0d9 | |||
2e60e9841e | |||
52ad9a7ddb | |||
ac5e0c13b7 | |||
b49658784b | |||
d4a972e073 | |||
1009a2f967 | |||
f355f01fc8 | |||
85995d51e4 | |||
2557a2fe5b | |||
4321573dfb | |||
70d4d6c56f | |||
e1e492832e | |||
1950394f7d | |||
308f7d93f5 | |||
de85bf848d | |||
7a6bf468bc | |||
9cb6346c4d | |||
f49eb66bb7 | |||
324ef9ca29 | |||
5b4dcc67b2 | |||
9010218b10 | |||
61144a10b3 | |||
7a1f737c65 | |||
546c032d43 | |||
b1869be3d9 | |||
574fdfecaa | |||
25c778965c | |||
976c0040b5 | |||
b53c62b40c | |||
![]() |
2cdc56c882 | ||
![]() |
f5059d8008 | ||
![]() |
792ec4d731 | ||
93d08188ea | |||
e9ad5a6c66 | |||
8b0f118e2d | |||
![]() |
60c89e28d3 | ||
38155051f5 | |||
![]() |
7b215d5c6e | ||
1000e0756b | |||
902b497526 | |||
039f954e70 | |||
5dc2b127fa | |||
252cc44841 | |||
96d9ce4761 | |||
7f294d6632 | |||
e17de9065b | |||
098687de45 | |||
6da3342f22 | |||
47337540f5 | |||
7e588f01b0 | |||
c7c6c9dae4 | |||
c77cfc4dcd | |||
1bd61076ea | |||
bff4a6f707 | |||
1cc266c675 | |||
72099dfde5 | |||
c9c45baabc | |||
a01022c217 | |||
c3459e6820 | |||
e031e6d760 | |||
6c63b53cf4 | |||
1aa50699ad | |||
b2c54ae8c0 | |||
b16c9f4b30 | |||
a8d80eaddf | |||
9baf1ed73c | |||
ce3ea656ad | |||
ed49212f5a | |||
ad1242c47d | |||
890fcfb506 | |||
d7723615fe | |||
6517065a1a | |||
9223a7d403 | |||
7ce6703c5b | |||
37261cddbb | |||
d8c2ef6f3b | |||
98e5324409 | |||
a69c2a23f2 | |||
d8de093e4d | |||
678564dbb3 | |||
09d2601e85 | |||
41560682a1 | |||
473f8e4bb6 | |||
67446285c1 | |||
e12f4688d3 | |||
2581bbe203 | |||
995f2e0248 | |||
e2c8f79429 | |||
763c93857d | |||
55d2ef9c25 | |||
f37cbd1616 | |||
2a3449d0f2 | |||
596693c206 | |||
22aa07c4ba | |||
62001c1e29 | |||
ca85c94fe5 | |||
637e1e25a6 | |||
09696c1c4d | |||
298a8342b8 | |||
d64220426b | |||
88efdc361c | |||
cc1b371198 | |||
d9e4a3c1d4 |
1
.github/FUNDING.yml
vendored
Normal file
@ -0,0 +1 @@
|
||||
ko_fi: papatutuwawa
|
3
.gitignore
vendored
@ -13,3 +13,6 @@ pubspec.lock
|
||||
|
||||
# Omit pubspec override files generated by melos
|
||||
**/pubspec_overrides.yaml
|
||||
|
||||
# Flake results
|
||||
result
|
||||
|
14
.gitlint
Normal file
@ -0,0 +1,14 @@
|
||||
[general]
|
||||
ignore=B5,B6,B7,B8
|
||||
|
||||
[title-max-length]
|
||||
line-length=72
|
||||
|
||||
[title-trailing-punctuation]
|
||||
[title-hard-tab]
|
||||
[title-match-regex]
|
||||
regex=^((feat|fix|chore|refactor|docs|release|test)\((meta|tests|style|docs|xep|core|example|all|flake|ci)+(,(meta|tests|style|docs|xep|core|example|all|flake|ci))*\)|release): [A-Z0-9].*$
|
||||
|
||||
|
||||
[body-trailing-whitespace]
|
||||
[body-first-line-empty]
|
49
.woodpecker.yml
Normal file
@ -0,0 +1,49 @@
|
||||
when:
|
||||
branch: master
|
||||
|
||||
pipeline:
|
||||
# Check moxxmpp
|
||||
moxxmpp-lint:
|
||||
image: dart:3.0.7
|
||||
commands:
|
||||
- cd packages/moxxmpp
|
||||
# Proxy requests to pub.dev using pubcached
|
||||
- PUB_HOSTED_URL=http://172.17.0.1:8000 dart pub get
|
||||
- dart analyze --fatal-infos --fatal-warnings
|
||||
|
||||
moxxmpp-test:
|
||||
image: dart:3.0.7
|
||||
commands:
|
||||
- cd packages/moxxmpp
|
||||
# Proxy requests to pub.dev using pubcached
|
||||
- PUB_HOSTED_URL=http://172.17.0.1:8000 dart pub get
|
||||
- dart test
|
||||
|
||||
# Check moxxmpp_socket_tcp
|
||||
moxxmpp_socket_tcp-lint:
|
||||
image: dart:3.0.7
|
||||
commands:
|
||||
- cd packages/moxxmpp_socket_tcp
|
||||
# Proxy requests to pub.dev using pubcached
|
||||
- PUB_HOSTED_URL=http://172.17.0.1:8000 dart pub get
|
||||
- dart analyze --fatal-infos --fatal-warnings
|
||||
|
||||
# moxxmpp-test:
|
||||
# image: dart:3.0.7
|
||||
# commands:
|
||||
# - cd packages/moxxmpp
|
||||
# # Proxy requests to pub.dev using pubcached
|
||||
# - PUB_HOSTED_URL=http://172.17.0.1:8000 dart pub get
|
||||
# - dart test
|
||||
|
||||
notify:
|
||||
image: git.polynom.me/papatutuwawa/woodpecker-xmpp
|
||||
settings:
|
||||
xmpp_tls: 1
|
||||
xmpp_is_muc: 1
|
||||
xmpp_recipient: moxxy-build@muc.moxxy.org
|
||||
xmpp_alias: 2Bot
|
||||
secrets: [ xmpp_jid, xmpp_password, xmpp_server ]
|
||||
when:
|
||||
status:
|
||||
- failure
|
19
CONTRIBUTING.md
Normal file
@ -0,0 +1,19 @@
|
||||
# Contribution Guide
|
||||
|
||||
Thanks for your interest in the moxxmpp XMPP library! This document contains guidelines and guides for working on the moxxmpp codebase.
|
||||
|
||||
## Contributing
|
||||
|
||||
If you want to fix a small issue, you can just fork, create a new branch, and start working right away. However, if you want to work
|
||||
on a bigger feature, please first create an issue (if an issue does not already exist) or join the [development chat](xmpp:moxxy@muc.moxxy.org?join) (xmpp:moxxy@muc.moxxy.org?join)
|
||||
to discuss the feature first.
|
||||
|
||||
Before creating a pull request, please make sure you checked every item on the following checklist:
|
||||
|
||||
- [ ] I formatted the code with the dart formatter (`dart format`) before running the linter
|
||||
- [ ] I ran the linter (`dart analyze`) and introduced no new linter warnings
|
||||
- [ ] I ran the tests (`dart test`) and introduced no new failing tests
|
||||
- [ ] I used [gitlint](https://github.com/jorisroovers/gitlint) to ensure propper formatting of my commig messages
|
||||
|
||||
If you think that your code is ready for a pull request, but you are not sure if it is ready, prefix the PR's title with "WIP: ", so that discussion
|
||||
can happen there. If you think your PR is ready for review, remove the "WIP: " prefix.
|
27
README.md
@ -3,16 +3,22 @@
|
||||
moxxmpp is a XMPP library written purely in Dart for usage in Moxxy.
|
||||
|
||||
## Packages
|
||||
### moxxmpp
|
||||
### [moxxmpp](./packages/moxxmpp)
|
||||
|
||||
This package contains the actual XMPP code that is platform-independent.
|
||||
|
||||
### moxxmpp_socket
|
||||
Documentation is available [here](https://docs.moxxy.org/moxxmpp/index.html).
|
||||
|
||||
`moxxmpp_socket` contains the implementation of the `BaseSocketWrapper` class that
|
||||
### [moxxmpp_socket_tcp](./packages/moxxmpp_socket_tcp)
|
||||
|
||||
`moxxmpp_socket_tcp` contains the implementation of the `BaseSocketWrapper` class that
|
||||
implements the RFC6120 connection algorithm and XEP-0368 direct TLS connections,
|
||||
if a DNS implementation is given, and supports StartTLS.
|
||||
|
||||
### moxxmpp_color
|
||||
|
||||
Implementation of [XEP-0392](https://xmpp.org/extensions/xep-0392.html).
|
||||
|
||||
## Development
|
||||
|
||||
To begin, use [melos](https://github.com/invertase/melos) to bootstrap the project: `melos bootstrap`. Then, the example
|
||||
@ -22,6 +28,21 @@ To run the example, make sure that Flutter is correctly set up and working. If y
|
||||
the development shell provided by the NixOS Flake, ensure that `ANDROID_HOME` and
|
||||
`ANDROID_AVD_HOME` are pointing to the correct directories.
|
||||
|
||||
## Examples
|
||||
|
||||
This repository contains 2 types of examples:
|
||||
|
||||
- `example_flutter`: An example of using moxxmpp using Flutter
|
||||
- `examples_dart`: A collection of pure Dart examples for showing different aspects of moxxmpp
|
||||
|
||||
For more information, see the respective README files.
|
||||
|
||||
## License
|
||||
|
||||
See `./LICENSE`.
|
||||
|
||||
## Support
|
||||
|
||||
If you like what I do and you want to support me, feel free to donate to me on Ko-Fi.
|
||||
|
||||
[<img src="https://codeberg.org/moxxy/moxxyv2/raw/branch/master/assets/repo/kofi.png" height="36" style="height: 36px; border: 0px;"></img>](https://ko-fi.com/papatutuwawa)
|
||||
|
@ -11,5 +11,3 @@ analyzer:
|
||||
exclude:
|
||||
- "**/*.g.dart"
|
||||
- "**/*.freezed.dart"
|
||||
- "test/"
|
||||
- "integration_test/"
|
||||
|
@ -1,6 +0,0 @@
|
||||
# melos_managed_dependency_overrides: moxxmpp,moxxmpp_socket_tcp
|
||||
dependency_overrides:
|
||||
moxxmpp:
|
||||
path: ../packages/moxxmpp
|
||||
moxxmpp_socket_tcp:
|
||||
path: ../packages/moxxmpp_socket_tcp
|
Before Width: | Height: | Size: 544 B After Width: | Height: | Size: 544 B |
Before Width: | Height: | Size: 442 B After Width: | Height: | Size: 442 B |
Before Width: | Height: | Size: 721 B After Width: | Height: | Size: 721 B |
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 1.0 KiB |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
@ -5,25 +5,28 @@ import 'package:moxxmpp/moxxmpp.dart';
|
||||
import 'package:moxxmpp_socket_tcp/moxxmpp_socket_tcp.dart';
|
||||
|
||||
class ExampleTcpSocketWrapper extends TCPSocketWrapper {
|
||||
ExampleTcpSocketWrapper() : super(false);
|
||||
ExampleTcpSocketWrapper() : super();
|
||||
|
||||
@override
|
||||
Future<List<MoxSrvRecord>> srvQuery(String domain, bool dnssec) async {
|
||||
final records = await MoxdnsPlugin.srvQuery(domain, false);
|
||||
return records
|
||||
.map((record) => MoxSrvRecord(
|
||||
record.priority,
|
||||
record.weight,
|
||||
record.target,
|
||||
record.port,
|
||||
),)
|
||||
.toList();
|
||||
.map(
|
||||
(record) => MoxSrvRecord(
|
||||
record.priority,
|
||||
record.weight,
|
||||
record.target,
|
||||
record.port,
|
||||
),
|
||||
)
|
||||
.toList();
|
||||
}
|
||||
}
|
||||
|
||||
void main() {
|
||||
Logger.root.level = Level.ALL;
|
||||
Logger.root.onRecord.listen((record) {
|
||||
// ignore: avoid_print
|
||||
print('${record.level.name}: ${record.time}: ${record.message}');
|
||||
});
|
||||
|
||||
@ -54,22 +57,32 @@ class MyHomePage extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _MyHomePageState extends State<MyHomePage> {
|
||||
final logger = Logger('MyHomePage');
|
||||
final XmppConnection connection = XmppConnection(
|
||||
ExponentialBackoffReconnectionPolicy(),
|
||||
ExampleTcpSocketWrapper(),
|
||||
RandomBackoffReconnectionPolicy(1, 60),
|
||||
AlwaysConnectedConnectivityManager(),
|
||||
ClientToServerNegotiator(),
|
||||
// The below causes the app to crash.
|
||||
//ExampleTcpSocketWrapper(),
|
||||
// In a production app, the below should be false.
|
||||
TCPSocketWrapper(),
|
||||
);
|
||||
TextEditingController jidController = TextEditingController();
|
||||
TextEditingController passwordController = TextEditingController();
|
||||
bool connected = false;
|
||||
bool loading = false;
|
||||
|
||||
_MyHomePageState() : super() {
|
||||
connection
|
||||
..registerManagers([
|
||||
StreamManagementManager(),
|
||||
DiscoManager(),
|
||||
RosterManager(),
|
||||
PingManager(),
|
||||
DiscoManager([]),
|
||||
RosterManager(TestingRosterStateManager("", [])),
|
||||
PingManager(
|
||||
const Duration(minutes: 3),
|
||||
),
|
||||
MessageManager(),
|
||||
PresenceManager('http://moxxmpp.example'),
|
||||
PresenceManager(),
|
||||
])
|
||||
..registerFeatureNegotiators([
|
||||
ResourceBindingNegotiator(),
|
||||
@ -83,17 +96,39 @@ class _MyHomePageState extends State<MyHomePage> {
|
||||
SaslScramNegotiator(8, '', '', ScramHashType.sha1),
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
Future<void> _buttonPressed() async {
|
||||
connection.setConnectionSettings(
|
||||
ConnectionSettings(
|
||||
jid: JID.fromString(jidController.text),
|
||||
password: passwordController.text,
|
||||
useDirectTLS: true,
|
||||
allowPlainAuth: false,
|
||||
),
|
||||
if (connected) {
|
||||
await connection.disconnect();
|
||||
setState(() {
|
||||
connected = false;
|
||||
});
|
||||
return;
|
||||
}
|
||||
setState(() {
|
||||
loading = true;
|
||||
});
|
||||
connection.connectionSettings = ConnectionSettings(
|
||||
jid: JID.fromString(jidController.text),
|
||||
password: passwordController.text,
|
||||
);
|
||||
await connection.connect();
|
||||
final result = await connection.connect(waitUntilLogin: true);
|
||||
setState(() {
|
||||
connected = result.isType<bool>() && result.get<bool>();
|
||||
loading = false;
|
||||
});
|
||||
if (result.isType<XmppError>()) {
|
||||
logger.severe(result.get<XmppError>());
|
||||
if (context.mounted) {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (_) => AlertDialog(
|
||||
title: const Text('Error'),
|
||||
content: Text(result.get<XmppError>().toString()),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
@ -101,20 +136,24 @@ class _MyHomePageState extends State<MyHomePage> {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(widget.title),
|
||||
backgroundColor: connected ? Colors.green : Colors.deepPurple[800],
|
||||
foregroundColor: connected ? Colors.black : Colors.white,
|
||||
),
|
||||
body: Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
TextField(
|
||||
enabled: !loading,
|
||||
controller: jidController,
|
||||
decoration: InputDecoration(
|
||||
decoration: const InputDecoration(
|
||||
labelText: 'JID',
|
||||
),
|
||||
),
|
||||
TextField(
|
||||
enabled: !loading,
|
||||
controller: passwordController,
|
||||
decoration: InputDecoration(
|
||||
decoration: const InputDecoration(
|
||||
labelText: 'Password',
|
||||
),
|
||||
obscureText: true,
|
||||
@ -122,10 +161,13 @@ class _MyHomePageState extends State<MyHomePage> {
|
||||
],
|
||||
),
|
||||
),
|
||||
floatingActionButton: FloatingActionButton(
|
||||
floatingActionButton: FloatingActionButton.extended(
|
||||
onPressed: _buttonPressed,
|
||||
label: Text(connected ? 'Disconnect' : 'Connect'),
|
||||
backgroundColor: Colors.blue,
|
||||
foregroundColor: Colors.white,
|
||||
tooltip: 'Connect',
|
||||
child: const Icon(Icons.add),
|
||||
icon: const Icon(Icons.power),
|
||||
),
|
||||
);
|
||||
}
|
@ -16,10 +16,10 @@ dependencies:
|
||||
version: 0.1.4+1
|
||||
moxxmpp:
|
||||
hosted: https://git.polynom.me/api/packages/Moxxy/pub
|
||||
version: 0.1.6+1
|
||||
version: 0.3.1
|
||||
moxxmpp_socket_tcp:
|
||||
hosted: https://git.polynom.me/api/packages/Moxxy/pub
|
||||
version: 0.1.2+9
|
||||
version: 0.3.1
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
6
examples_dart/.gitignore
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
# Files and directories created by pub.
|
||||
.dart_tool/
|
||||
.packages
|
||||
|
||||
# Conventional directory for build output.
|
||||
build/
|
7
examples_dart/README.md
Normal file
@ -0,0 +1,7 @@
|
||||
# Dart Examples
|
||||
|
||||
Run using `dart run bin/<example>.dart`.
|
||||
|
||||
## Examples
|
||||
|
||||
- `component.dart`: Use moxxmpp to implement a component using XEP-0114.
|
30
examples_dart/analysis_options.yaml
Normal file
@ -0,0 +1,30 @@
|
||||
# This file configures the static analysis results for your project (errors,
|
||||
# warnings, and lints).
|
||||
#
|
||||
# This enables the 'recommended' set of lints from `package:lints`.
|
||||
# This set helps identify many issues that may lead to problems when running
|
||||
# or consuming Dart code, and enforces writing Dart using a single, idiomatic
|
||||
# style and format.
|
||||
#
|
||||
# If you want a smaller set of lints you can change this to specify
|
||||
# 'package:lints/core.yaml'. These are just the most critical lints
|
||||
# (the recommended set includes the core lints).
|
||||
# The core lints are also what is used by pub.dev for scoring packages.
|
||||
|
||||
include: package:lints/recommended.yaml
|
||||
|
||||
# Uncomment the following section to specify additional rules.
|
||||
|
||||
# linter:
|
||||
# rules:
|
||||
# - camel_case_types
|
||||
|
||||
# analyzer:
|
||||
# exclude:
|
||||
# - path/to/excluded/files/**
|
||||
|
||||
# For more information about the core and recommended set of lints, see
|
||||
# https://dart.dev/go/core-lints
|
||||
|
||||
# For additional information about configuring this file, see
|
||||
# https://dart.dev/guides/language/analysis-options
|
94
examples_dart/bin/component.dart
Normal file
@ -0,0 +1,94 @@
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:moxxmpp/moxxmpp.dart';
|
||||
import 'package:moxxmpp_socket_tcp/moxxmpp_socket_tcp.dart';
|
||||
|
||||
class TestingTCPSocketWrapper extends TCPSocketWrapper {
|
||||
TestingTCPSocketWrapper() : super(true);
|
||||
|
||||
@override
|
||||
bool onBadCertificate(dynamic certificate, String domain) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
class EchoMessageManager extends XmppManagerBase {
|
||||
EchoMessageManager() : super('org.moxxy.example.message');
|
||||
|
||||
@override
|
||||
Future<bool> isSupported() async => true;
|
||||
|
||||
@override
|
||||
List<StanzaHandler> getIncomingStanzaHandlers() => [
|
||||
StanzaHandler(
|
||||
stanzaTag: 'message',
|
||||
callback: _onMessage,
|
||||
priority: -100,
|
||||
xmlns: null,
|
||||
)
|
||||
];
|
||||
|
||||
Future<StanzaHandlerData> _onMessage(
|
||||
Stanza stanza,
|
||||
StanzaHandlerData state,
|
||||
) async {
|
||||
final body = stanza.firstTag('body');
|
||||
if (body == null) return state..done = true;
|
||||
|
||||
final bodyText = body.innerText();
|
||||
|
||||
await getAttributes().sendStanza(
|
||||
StanzaDetails(
|
||||
Stanza.message(
|
||||
to: stanza.from,
|
||||
children: [
|
||||
XMLNode(
|
||||
tag: 'body',
|
||||
text: 'Hello, ${stanza.from}! You said "$bodyText"',
|
||||
),
|
||||
],
|
||||
),
|
||||
awaitable: false,
|
||||
),
|
||||
);
|
||||
|
||||
return state..done = true;
|
||||
}
|
||||
}
|
||||
|
||||
void main(List<String> arguments) async {
|
||||
Logger.root.level = Level.ALL;
|
||||
Logger.root.onRecord.listen((record) {
|
||||
// ignore: avoid_print
|
||||
print(
|
||||
'[${record.level.name}] (${record.loggerName}) ${record.time}: ${record.message}',
|
||||
);
|
||||
});
|
||||
|
||||
final conn = XmppConnection(
|
||||
TestingReconnectionPolicy(),
|
||||
AlwaysConnectedConnectivityManager(),
|
||||
ComponentToServerNegotiator(),
|
||||
TestingTCPSocketWrapper(),
|
||||
)..connectionSettings = ConnectionSettings(
|
||||
jid: JID.fromString('component.localhost'),
|
||||
password: 'abc123',
|
||||
host: '127.0.0.1',
|
||||
port: 8888,
|
||||
);
|
||||
await conn.registerManagers([
|
||||
EchoMessageManager(),
|
||||
]);
|
||||
|
||||
final result = await conn.connect(
|
||||
waitUntilLogin: true,
|
||||
shouldReconnect: false,
|
||||
enableReconnectOnSuccess: false,
|
||||
);
|
||||
if (result.isType<XmppError>()) {
|
||||
print('Failed to connect as component');
|
||||
return;
|
||||
}
|
||||
|
||||
// Just block for some time to test the connection
|
||||
await Future<void>.delayed(const Duration(seconds: 9999));
|
||||
}
|
111
examples_dart/bin/muc_client.dart
Normal file
@ -0,0 +1,111 @@
|
||||
import 'package:cli_repl/cli_repl.dart';
|
||||
import 'package:example_dart/arguments.dart';
|
||||
import 'package:example_dart/socket.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:moxxmpp/moxxmpp.dart';
|
||||
|
||||
void main(List<String> args) async {
|
||||
// Set up logging
|
||||
Logger.root.level = Level.ALL;
|
||||
Logger.root.onRecord.listen((record) {
|
||||
// ignore: avoid_print
|
||||
print(
|
||||
'[${record.level.name}] (${record.loggerName}) ${record.time}: ${record.message}',
|
||||
);
|
||||
});
|
||||
|
||||
final parser = ArgumentParser()
|
||||
..parser.addOption('muc', help: 'The MUC to send messages to')
|
||||
..parser.addOption('nick', help: 'The nickname with which to join the MUC');
|
||||
final options = parser.handleArguments(args);
|
||||
if (options == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Connect
|
||||
final muc = JID.fromString(options['muc']! as String).toBare();
|
||||
final nick = options['nick']! as String;
|
||||
final connection = XmppConnection(
|
||||
TestingReconnectionPolicy(),
|
||||
AlwaysConnectedConnectivityManager(),
|
||||
ClientToServerNegotiator(),
|
||||
ExampleTCPSocketWrapper(parser.srvRecord),
|
||||
)..connectionSettings = parser.connectionSettings;
|
||||
|
||||
// Register the managers and negotiators
|
||||
await connection.registerManagers([
|
||||
PresenceManager(),
|
||||
DiscoManager([]),
|
||||
PubSubManager(),
|
||||
MessageManager(),
|
||||
StableIdManager(),
|
||||
MUCManager(),
|
||||
]);
|
||||
await connection.registerFeatureNegotiators([
|
||||
SaslPlainNegotiator(),
|
||||
ResourceBindingNegotiator(),
|
||||
StartTlsNegotiator(),
|
||||
SaslScramNegotiator(10, '', '', ScramHashType.sha1),
|
||||
]);
|
||||
|
||||
// Connect
|
||||
Logger.root.info('Connecting...');
|
||||
final result =
|
||||
await connection.connect(shouldReconnect: false, waitUntilLogin: true);
|
||||
if (!result.isType<bool>()) {
|
||||
Logger.root.severe('Authentication failed!');
|
||||
return;
|
||||
}
|
||||
Logger.root.info('Connected.');
|
||||
|
||||
// Print received messages.
|
||||
connection
|
||||
.asBroadcastStream()
|
||||
.where((event) => event is MessageEvent)
|
||||
.listen((event) {
|
||||
event as MessageEvent;
|
||||
|
||||
// Ignore messages with no <body />
|
||||
final body = event.get<MessageBodyData>()?.body;
|
||||
if (body == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
print('=====> [${event.from}] $body');
|
||||
});
|
||||
|
||||
// Join room
|
||||
final mm = connection.getManagerById<MUCManager>(mucManager)!;
|
||||
await mm.joinRoom(
|
||||
muc,
|
||||
nick,
|
||||
maxHistoryStanzas: 0,
|
||||
);
|
||||
final state = (await mm.getRoomState(muc))!;
|
||||
|
||||
print('=====> ${state.members.length} users in room');
|
||||
print('=====> ${state.members.values.map((m) => m.nick).join(", ")}');
|
||||
|
||||
final repl = Repl(prompt: '> ');
|
||||
await for (final line in repl.runAsync()) {
|
||||
await connection
|
||||
.getManagerById<MessageManager>(messageManager)!
|
||||
.sendMessage(
|
||||
muc,
|
||||
TypedMap<StanzaHandlerExtension>.fromList([
|
||||
MessageBodyData(line),
|
||||
StableIdData(
|
||||
// NOTE: Don't do this. Use a UUID.
|
||||
DateTime.now().millisecondsSinceEpoch.toString(),
|
||||
null,
|
||||
),
|
||||
]),
|
||||
type: 'groupchat');
|
||||
}
|
||||
|
||||
// Leave room
|
||||
await connection.getManagerById<MUCManager>(mucManager)!.leaveRoom(muc);
|
||||
|
||||
// Disconnect
|
||||
await connection.disconnect();
|
||||
}
|
116
examples_dart/bin/omemo_client.dart
Normal file
@ -0,0 +1,116 @@
|
||||
import 'package:chalkdart/chalk.dart';
|
||||
import 'package:cli_repl/cli_repl.dart';
|
||||
import 'package:example_dart/arguments.dart';
|
||||
import 'package:example_dart/socket.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:moxxmpp/moxxmpp.dart';
|
||||
import 'package:omemo_dart/omemo_dart.dart' as omemo;
|
||||
|
||||
void main(List<String> args) async {
|
||||
// Set up logging
|
||||
Logger.root.level = Level.ALL;
|
||||
Logger.root.onRecord.listen((record) {
|
||||
// ignore: avoid_print
|
||||
print(
|
||||
'[${record.level.name}] (${record.loggerName}) ${record.time}: ${record.message}',
|
||||
);
|
||||
});
|
||||
|
||||
final parser = ArgumentParser()
|
||||
..parser.addOption('to', help: 'The JID to send messages to');
|
||||
final options = parser.handleArguments(args);
|
||||
if (options == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Connect
|
||||
final jid = parser.jid;
|
||||
final to = JID.fromString(options['to']! as String).toBare();
|
||||
final connection = XmppConnection(
|
||||
TestingReconnectionPolicy(),
|
||||
AlwaysConnectedConnectivityManager(),
|
||||
ClientToServerNegotiator(),
|
||||
ExampleTCPSocketWrapper(parser.srvRecord, true),
|
||||
)..connectionSettings = parser.connectionSettings;
|
||||
|
||||
// Generate OMEMO data
|
||||
omemo.OmemoManager? oom;
|
||||
final moxxmppOmemo = OmemoManager(
|
||||
() async => oom!,
|
||||
(toJid, _) async => toJid == to,
|
||||
);
|
||||
oom = omemo.OmemoManager(
|
||||
await omemo.OmemoDevice.generateNewDevice(jid.toString(), opkAmount: 5),
|
||||
omemo.BlindTrustBeforeVerificationTrustManager(),
|
||||
moxxmppOmemo.sendEmptyMessageImpl,
|
||||
moxxmppOmemo.fetchDeviceList,
|
||||
moxxmppOmemo.fetchDeviceBundle,
|
||||
moxxmppOmemo.subscribeToDeviceListImpl,
|
||||
moxxmppOmemo.publishDeviceImpl,
|
||||
);
|
||||
final deviceId = await oom.getDeviceId();
|
||||
Logger.root.info('Our device id: $deviceId');
|
||||
|
||||
// Register the managers and negotiators
|
||||
await connection.registerManagers([
|
||||
PresenceManager(),
|
||||
DiscoManager([]),
|
||||
PubSubManager(),
|
||||
MessageManager(),
|
||||
moxxmppOmemo,
|
||||
]);
|
||||
await connection.registerFeatureNegotiators([
|
||||
SaslPlainNegotiator(),
|
||||
ResourceBindingNegotiator(),
|
||||
StartTlsNegotiator(),
|
||||
SaslScramNegotiator(10, '', '', ScramHashType.sha1),
|
||||
]);
|
||||
|
||||
// Set up event handlers
|
||||
connection.asBroadcastStream().listen((event) {
|
||||
if (event is MessageEvent) {
|
||||
Logger.root.info(event.id);
|
||||
Logger.root.info(event.extensions.keys.toList());
|
||||
|
||||
final body = event.encryptionError != null
|
||||
? chalk.red('Failed to decrypt message: ${event.encryptionError}')
|
||||
: chalk.green(event.get<MessageBodyData>()?.body ?? '');
|
||||
print('[${event.from.toString()}] $body');
|
||||
}
|
||||
});
|
||||
|
||||
// Connect
|
||||
Logger.root.info('Connecting...');
|
||||
final result =
|
||||
await connection.connect(shouldReconnect: false, waitUntilLogin: true);
|
||||
if (!result.isType<bool>()) {
|
||||
Logger.root.severe('Authentication failed!');
|
||||
return;
|
||||
}
|
||||
Logger.root.info('Connected.');
|
||||
|
||||
// Publish our bundle
|
||||
Logger.root.info('Publishing bundle');
|
||||
final device = await oom.getDevice();
|
||||
final omemoResult = await moxxmppOmemo.publishBundle(await device.toBundle());
|
||||
if (!omemoResult.isType<bool>()) {
|
||||
Logger.root.severe(
|
||||
'Failed to publish OMEMO bundle: ${omemoResult.get<OmemoError>()}');
|
||||
return;
|
||||
}
|
||||
|
||||
final repl = Repl(prompt: '> ');
|
||||
await for (final line in repl.runAsync()) {
|
||||
await connection
|
||||
.getManagerById<MessageManager>(messageManager)!
|
||||
.sendMessage(
|
||||
to,
|
||||
TypedMap<StanzaHandlerExtension>.fromList([
|
||||
MessageBodyData(line),
|
||||
]),
|
||||
);
|
||||
}
|
||||
|
||||
// Disconnect
|
||||
await connection.disconnect();
|
||||
}
|
112
examples_dart/bin/simple_client.dart
Normal file
@ -0,0 +1,112 @@
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:moxxmpp/moxxmpp.dart';
|
||||
import 'package:moxxmpp_socket_tcp/moxxmpp_socket_tcp.dart';
|
||||
|
||||
/// The JID we want to authenticate as.
|
||||
final xmppUser = JID.fromString('jane@example.com');
|
||||
|
||||
/// The password to authenticate with.
|
||||
const xmppPass = 'secret';
|
||||
|
||||
/// The [xmppHost]:[xmppPort] server address to connect to.
|
||||
/// In a real application, one might prefer to use [TCPSocketWrapper]
|
||||
/// with a custom DNS implementation to let moxxmpp resolve the XMPP
|
||||
/// server's address automatically. However, if we just provide a host
|
||||
/// and a port, then [TCPSocketWrapper] will just skip the resolution and
|
||||
/// immediately use the provided connection details.
|
||||
const xmppHost = 'localhost';
|
||||
const xmppPort = 5222;
|
||||
|
||||
void main(List<String> args) async {
|
||||
Logger.root.level = Level.ALL;
|
||||
Logger.root.onRecord.listen((record) {
|
||||
print('${record.level.name}|${record.time}: ${record.message}');
|
||||
});
|
||||
|
||||
// This class manages every aspect of handling the XMPP stream.
|
||||
final connection = XmppConnection(
|
||||
// A reconnection policy tells the connection how to handle an error
|
||||
// while or after connecting to the server. The [TestingReconnectionPolicy]
|
||||
// immediately triggers a reconnection. In a real implementation, one might
|
||||
// prefer to use a smarter strategy, like using an exponential backoff.
|
||||
TestingReconnectionPolicy(),
|
||||
|
||||
// A connectivity manager tells the connection when it can connect. This is to
|
||||
// ensure that we're not constantly trying to reconnect because we have no
|
||||
// Internet connection. [AlwaysConnectedConnectivityManager] always says that
|
||||
// we're connected. In a real application, one might prefer to use a smarter
|
||||
// strategy, like using connectivity_plus to query the system's network connectivity
|
||||
// state.
|
||||
AlwaysConnectedConnectivityManager(),
|
||||
|
||||
// This kind of negotiator tells the connection how to handle the stream
|
||||
// negotiations. The [ClientToServerNegotiator] allows to connect to the server
|
||||
// as a regular client. Another negotiator would be the [ComponentToServerNegotiator] that
|
||||
// allows for connections to the server where we're acting as a component.
|
||||
ClientToServerNegotiator(),
|
||||
|
||||
// A wrapper around any kind of connection. In this case, we use the [TCPSocketWrapper], which
|
||||
// uses a dart:io Socket/SecureSocket to connect to the server. If you want, you can also
|
||||
// provide your own socket to use, for example, WebSockets or any other connection
|
||||
// mechanism.
|
||||
TCPSocketWrapper(false),
|
||||
)..connectionSettings = ConnectionSettings(
|
||||
jid: xmppUser,
|
||||
password: xmppPass,
|
||||
host: xmppHost,
|
||||
port: xmppPort,
|
||||
);
|
||||
|
||||
// Register a set of "managers" that provide you with implementations of various
|
||||
// XEPs. Some have interdependencies, which need to be met. However, this example keeps
|
||||
// it simple and just registers a [MessageManager], which has no required dependencies.
|
||||
await connection.registerManagers([
|
||||
// The [MessageManager] handles receiving and sending <message /> stanzas.
|
||||
MessageManager(),
|
||||
]);
|
||||
|
||||
// Feature negotiators are objects that tell the connection negotiator what stream features
|
||||
// we can negotiate and enable. moxxmpp negotiators always try to enable their features.
|
||||
await connection.registerFeatureNegotiators([
|
||||
// This negotiator authenticates to the server using SASL PLAIN with the provided
|
||||
// credentials.
|
||||
SaslPlainNegotiator(),
|
||||
// This negotiator attempts to bind a resource. By default, it's always a random one.
|
||||
ResourceBindingNegotiator(),
|
||||
// This negotiator attempts to do StartTLS before authenticating.
|
||||
StartTlsNegotiator(),
|
||||
]);
|
||||
|
||||
// Set up a stream handler for the connection's event stream. Managers and negotiators
|
||||
// may trigger certain events. The [MessageManager], for example, triggers a [MessageEvent]
|
||||
// whenever a message is received. If other managers are registered that parse a message's
|
||||
// contents, then they can add their data to the event.
|
||||
connection.asBroadcastStream().listen((event) {
|
||||
if (event is! MessageEvent) {
|
||||
return;
|
||||
}
|
||||
|
||||
// The text body (contents of the <body /> element) are returned as a
|
||||
// [MessageBodyData] object. However, a message does not have to contain a
|
||||
// body, so it is nullable.
|
||||
final body = event.extensions.get<MessageBodyData>()?.body;
|
||||
print('[<-- ${event.from}] $body');
|
||||
});
|
||||
|
||||
// Connect to the server.
|
||||
final result = await connection.connect(
|
||||
// This flag indicates that we want to reconnect in case something happens.
|
||||
shouldReconnect: true,
|
||||
// This flag indicates that we want the returned Future to only resolve
|
||||
// once the stream negotiations are done and no negotiator has any feature left
|
||||
// to negotiate.
|
||||
waitUntilLogin: true,
|
||||
);
|
||||
|
||||
// Check if the connection was successful. [connection.connect] can return a boolean
|
||||
// to indicate success or a [XmppError] in case the connection attempt failed.
|
||||
if (!result.isType<bool>()) {
|
||||
print('Failed to connect to server');
|
||||
return;
|
||||
}
|
||||
}
|
84
examples_dart/lib/arguments.dart
Normal file
@ -0,0 +1,84 @@
|
||||
import 'package:args/args.dart';
|
||||
import 'package:chalkdart/chalk.dart';
|
||||
import 'package:moxxmpp/moxxmpp.dart';
|
||||
import 'package:moxxmpp_socket_tcp/moxxmpp_socket_tcp.dart';
|
||||
|
||||
extension StringToInt on String {
|
||||
int toInt() => int.parse(this);
|
||||
}
|
||||
|
||||
/// A wrapper around [ArgParser] for providing convenience functions and standard parameters
|
||||
/// to the examples.
|
||||
class ArgumentParser {
|
||||
ArgumentParser() {
|
||||
parser
|
||||
..addOption('jid', help: 'The JID to connect as')
|
||||
..addOption('password', help: 'The password to use for authenticating')
|
||||
..addOption('host',
|
||||
help:
|
||||
'The host address to connect to (By default uses the domain part of the JID)')
|
||||
..addOption('port', help: 'The port to connect to')
|
||||
..addOption('xmpps-srv',
|
||||
help:
|
||||
'Inject a SRV record for _xmpps-client._tcp. Format: <priority>,<weight>,<target>,<port>')
|
||||
..addFlag('help',
|
||||
abbr: 'h',
|
||||
negatable: false,
|
||||
defaultsTo: false,
|
||||
help: 'Show this help text');
|
||||
}
|
||||
|
||||
/// The [ArgParser] that handles parsing the arguments.
|
||||
final ArgParser parser = ArgParser();
|
||||
|
||||
/// The parsed options. Only valid after calling [handleArguments].
|
||||
late ArgResults options;
|
||||
|
||||
ArgResults? handleArguments(List<String> args) {
|
||||
options = parser.parse(args);
|
||||
if (options['help']!) {
|
||||
print(parser.usage);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (options['jid'] == null) {
|
||||
print(chalk.red('No JID specified'));
|
||||
print(parser.usage);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (options['password'] == null) {
|
||||
print(chalk.red('No password specified'));
|
||||
print(parser.usage);
|
||||
return null;
|
||||
}
|
||||
|
||||
return options;
|
||||
}
|
||||
|
||||
/// The JID to connect as.
|
||||
JID get jid => JID.fromString(options['jid']!).toBare();
|
||||
|
||||
/// Construct connection settings from the parsed options.
|
||||
ConnectionSettings get connectionSettings => ConnectionSettings(
|
||||
jid: jid,
|
||||
password: options['password']!,
|
||||
host: options['host'],
|
||||
port: (options['port'] as String?)?.toInt(),
|
||||
);
|
||||
|
||||
/// Construct an xmpps-client SRV record for injection, if specified.
|
||||
MoxSrvRecord? get srvRecord {
|
||||
if (options['xmpps-srv'] == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final parts = options['xmpps-srv']!.split(',');
|
||||
return MoxSrvRecord(
|
||||
int.parse(parts[0]),
|
||||
int.parse(parts[1]),
|
||||
parts[2],
|
||||
int.parse(parts[3]),
|
||||
);
|
||||
}
|
||||
}
|
22
examples_dart/lib/socket.dart
Normal file
@ -0,0 +1,22 @@
|
||||
import 'package:moxxmpp_socket_tcp/moxxmpp_socket_tcp.dart';
|
||||
|
||||
/// A simple socket for examples that allows injection of SRV records (since
|
||||
/// we cannot use moxdns here).
|
||||
class ExampleTCPSocketWrapper extends TCPSocketWrapper {
|
||||
ExampleTCPSocketWrapper(this.srvRecord, bool logData) : super(logData);
|
||||
|
||||
/// A potential SRV record to inject for testing.
|
||||
final MoxSrvRecord? srvRecord;
|
||||
|
||||
@override
|
||||
bool onBadCertificate(dynamic certificate, String domain) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<MoxSrvRecord>> srvQuery(String domain, bool dnssec) async {
|
||||
return [
|
||||
if (srvRecord != null) srvRecord!,
|
||||
];
|
||||
}
|
||||
}
|
35
examples_dart/pubspec.yaml
Normal file
@ -0,0 +1,35 @@
|
||||
name: example_dart
|
||||
description: A collection of samples for moxxmpp.
|
||||
version: 1.0.0
|
||||
|
||||
environment:
|
||||
sdk: '>=3.0.0 <4.0.0'
|
||||
|
||||
dependencies:
|
||||
args: 2.4.1
|
||||
chalkdart: 2.0.9
|
||||
cli_repl: 0.2.3
|
||||
logging: ^1.0.2
|
||||
moxxmpp:
|
||||
hosted: https://git.polynom.me/api/packages/Moxxy/pub
|
||||
version: 0.4.0
|
||||
moxxmpp_socket_tcp:
|
||||
hosted: https://git.polynom.me/api/packages/Moxxy/pub
|
||||
version: 0.4.0
|
||||
omemo_dart:
|
||||
hosted: https://git.polynom.me/api/packages/PapaTutuWawa/pub
|
||||
version: ^0.5.1
|
||||
|
||||
dependency_overrides:
|
||||
moxxmpp:
|
||||
path: ../packages/moxxmpp
|
||||
moxxmpp_socket_tcp:
|
||||
path: ../packages/moxxmpp_socket_tcp
|
||||
omemo_dart:
|
||||
git:
|
||||
url: https://github.com/PapaTutuWawa/omemo_dart.git
|
||||
rev: 49c7e114e6cf80dcde55fbbd218bba3182045862
|
||||
|
||||
dev_dependencies:
|
||||
lints: ^2.0.0
|
||||
test: ^1.16.0
|
127
flake.lock
@ -1,12 +1,74 @@
|
||||
{
|
||||
"nodes": {
|
||||
"flake-utils": {
|
||||
"android-nixpkgs": {
|
||||
"inputs": {
|
||||
"devshell": "devshell",
|
||||
"flake-utils": "flake-utils",
|
||||
"nixpkgs": "nixpkgs"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1667395993,
|
||||
"narHash": "sha256-nuEHfE/LcWyuSWnS8t12N1wc105Qtau+/OdUAjtQ0rA=",
|
||||
"lastModified": 1727554699,
|
||||
"narHash": "sha256-puBCNL5PW7Pej+6Srmi2YjEgNeE015NFe33hbkkLqeQ=",
|
||||
"owner": "tadfisher",
|
||||
"repo": "android-nixpkgs",
|
||||
"rev": "bc34ef1c71fe9eafcfb1d637b431fca83d746625",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "tadfisher",
|
||||
"repo": "android-nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"devshell": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
"android-nixpkgs",
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1722113426,
|
||||
"narHash": "sha256-Yo/3loq572A8Su6aY5GP56knpuKYRvM2a1meP9oJZCw=",
|
||||
"owner": "numtide",
|
||||
"repo": "devshell",
|
||||
"rev": "67cce7359e4cd3c45296fb4aaf6a19e2a9c757ae",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "numtide",
|
||||
"repo": "devshell",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"flake-utils": {
|
||||
"inputs": {
|
||||
"systems": "systems"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1726560853,
|
||||
"narHash": "sha256-X6rJYSESBVr3hBoH0WbKE5KvhPU5bloyZ2L4K60/fPQ=",
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"rev": "5aed5285a952e0b949eb3ba02c12fa4fcfef535f",
|
||||
"rev": "c1dfcf08411b08f6b8615f7d8971a2bfa81d5e8a",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"flake-utils_2": {
|
||||
"inputs": {
|
||||
"systems": "systems_2"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1692799911,
|
||||
"narHash": "sha256-3eihraek4qL744EvQXsK1Ha6C3CR7nnT8X2qWap4RNk=",
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"rev": "f9e7cf818399d17d347f847525c5a5a8032e4e44",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@ -17,11 +79,27 @@
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1667610399,
|
||||
"narHash": "sha256-XZd0f4ZWAY0QOoUSdiNWj/eFiKb4B9CJPtl9uO9SYY4=",
|
||||
"lastModified": 1727348695,
|
||||
"narHash": "sha256-J+PeFKSDV+pHL7ukkfpVzCOO7mBSrrpJ3svwBFABbhI=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "1dd8696f96db47156e1424a49578fe7dd4ce99a4",
|
||||
"rev": "1925c603f17fc89f4c8f6bf6f631a802ad85d784",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "nixos-unstable",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs_2": {
|
||||
"locked": {
|
||||
"lastModified": 1727586919,
|
||||
"narHash": "sha256-e/YXG0tO5GWHDS8QQauj8aj4HhXEm602q9swrrlTlKQ=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "2dcd9c55e8914017226f5948ac22c53872a13ee2",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@ -33,8 +111,39 @@
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"flake-utils": "flake-utils",
|
||||
"nixpkgs": "nixpkgs"
|
||||
"android-nixpkgs": "android-nixpkgs",
|
||||
"flake-utils": "flake-utils_2",
|
||||
"nixpkgs": "nixpkgs_2"
|
||||
}
|
||||
},
|
||||
"systems": {
|
||||
"locked": {
|
||||
"lastModified": 1681028828,
|
||||
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"systems_2": {
|
||||
"locked": {
|
||||
"lastModified": 1681028828,
|
||||
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"type": "github"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
75
flake.nix
@ -2,17 +2,19 @@
|
||||
description = "moxxmpp";
|
||||
inputs = {
|
||||
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
|
||||
android-nixpkgs.url = "github:tadfisher/android-nixpkgs";
|
||||
flake-utils.url = "github:numtide/flake-utils";
|
||||
};
|
||||
|
||||
outputs = { self, nixpkgs, flake-utils }: flake-utils.lib.eachDefaultSystem (system: let
|
||||
pkgs = import nixpkgs {
|
||||
outputs = { self, nixpkgs, android-nixpkgs, flake-utils }: flake-utils.lib.eachDefaultSystem (system: let
|
||||
pkgs = import nixpkgs {
|
||||
inherit system;
|
||||
config = {
|
||||
android_sdk.accept_license = true;
|
||||
allowUnfree = true;
|
||||
};
|
||||
};
|
||||
# Everything to make Flutter happy
|
||||
android = pkgs.androidenv.composeAndroidPackages {
|
||||
# TODO: Find a way to pin these
|
||||
#toolsVersion = "26.1.1";
|
||||
@ -29,15 +31,50 @@
|
||||
useGoogleAPIs = false;
|
||||
useGoogleTVAddOns = false;
|
||||
};
|
||||
pinnedJDK = pkgs.jdk;
|
||||
lib = pkgs.lib;
|
||||
pinnedJDK = pkgs.jdk17;
|
||||
|
||||
pythonEnv = pkgs.python3.withPackages (ps: with ps; [
|
||||
pyyaml
|
||||
requests
|
||||
]);
|
||||
|
||||
moxxmppPubCache = import ./nix/pubcache.moxxmpp.nix {
|
||||
inherit (pkgs) fetchzip runCommand;
|
||||
};
|
||||
in {
|
||||
devShell = pkgs.mkShell {
|
||||
packages = {
|
||||
moxxmppDartDocs = pkgs.callPackage ./nix/moxxmpp-docs.nix {
|
||||
inherit (moxxmppPubCache) pubCache;
|
||||
};
|
||||
};
|
||||
|
||||
devShell = let
|
||||
prosody-newer-community-modules = pkgs.prosody.overrideAttrs (old: {
|
||||
communityModules = pkgs.fetchhg {
|
||||
url = "https://hg.prosody.im/prosody-modules";
|
||||
rev = "e3a3a6c86a9f";
|
||||
sha256 = "sha256-C2x6PCv0sYuj4/SroDOJLsNPzfeNCodYKbMqmNodFrk=";
|
||||
};
|
||||
|
||||
src = pkgs.fetchhg {
|
||||
url = "https://hg.prosody.im/trunk";
|
||||
rev = "8a2f75e38eb2";
|
||||
sha256 = "sha256-zMNp9+wQ/hvUVyxFl76DqCVzQUPP8GkNdstiTDkG8Hw=";
|
||||
};
|
||||
});
|
||||
prosody-sasl2 = prosody-newer-community-modules.override {
|
||||
withCommunityModules = [
|
||||
"sasl2" "sasl2_fast" "sasl2_sm" "sasl2_bind2"
|
||||
];
|
||||
};
|
||||
in pkgs.mkShell {
|
||||
buildInputs = with pkgs; [
|
||||
flutter pinnedJDK android.platform-tools dart # Flutter/Android
|
||||
flutter pinnedJDK android.platform-tools dart # Dart
|
||||
gitlint # Code hygiene
|
||||
ripgrep # General utilities
|
||||
|
||||
# Flutter dependencies for linux desktop
|
||||
# Flutter dependencies for Linux desktop
|
||||
atk
|
||||
cairo
|
||||
clang
|
||||
@ -53,12 +90,38 @@
|
||||
pkg-config
|
||||
xorg.libX11
|
||||
xorg.xorgproto
|
||||
|
||||
# For the scripts in ./scripts/
|
||||
pythonEnv
|
||||
|
||||
# For integration testing against a local prosody server
|
||||
prosody-sasl2
|
||||
mkcert
|
||||
];
|
||||
|
||||
CPATH = "${pkgs.xorg.libX11.dev}/include:${pkgs.xorg.xorgproto}/include";
|
||||
LD_LIBRARY_PATH = with pkgs; lib.makeLibraryPath [ atk cairo epoxy gdk-pixbuf glib gtk3 harfbuzz pango ];
|
||||
|
||||
ANDROID_SDK_ROOT = "${android.androidsdk}/share/android-sdk";
|
||||
ANDROID_HOME = "${android.androidsdk}/share/android-sdk";
|
||||
JAVA_HOME = pinnedJDK;
|
||||
|
||||
# Fix an issue with Flutter using an older version of aapt2, which does not know
|
||||
# an used parameter.
|
||||
GRADLE_OPTS = "-Dorg.gradle.project.android.aapt2FromMavenOverride=${android.androidsdk}/share/android-sdk/build-tools/34.0.0/aapt2";
|
||||
};
|
||||
|
||||
apps = {
|
||||
regenerateNixPackage = let
|
||||
script = pkgs.writeShellScript "regenerate-nix-package.sh" ''
|
||||
set -e
|
||||
${pythonEnv}/bin/python ./scripts/pubspec2lock.py ./packages/moxxmpp/pubspec.lock ./nix/moxxmpp.lock
|
||||
${pythonEnv}/bin/python ./scripts/lock2nix.py ./nix/moxxmpp.lock ./nix/pubcache.moxxmpp.nix moxxmpp
|
||||
'';
|
||||
in {
|
||||
type = "app";
|
||||
program = "${script}";
|
||||
};
|
||||
};
|
||||
});
|
||||
}
|
||||
|
6
integration_tests/.gitignore
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
# Files and directories created by pub.
|
||||
.dart_tool/
|
||||
.packages
|
||||
|
||||
# Conventional directory for build output.
|
||||
build/
|
5
integration_tests/README.md
Normal file
@ -0,0 +1,5 @@
|
||||
# Integration Tests
|
||||
|
||||
The included `./prosody.cfg.lua` config file must be used for integration testing.
|
||||
Additionally, ensure that a user `testuser@localhost` with the password `abc123`
|
||||
exists. Note that this currently requires prosody-trunk.
|
1
integration_tests/analysis_options.yaml
Normal file
@ -0,0 +1 @@
|
||||
include: ../analysis_options.yaml
|
24
integration_tests/certs/localhost.crt
Normal file
@ -0,0 +1,24 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIEAzCCAmugAwIBAgIQd61NPnP8++X7h8a+85C6DjANBgkqhkiG9w0BAQsFADBZ
|
||||
MR4wHAYDVQQKExVta2NlcnQgZGV2ZWxvcG1lbnQgQ0ExFzAVBgNVBAsMDmFsZXhh
|
||||
bmRlckBtaWt1MR4wHAYDVQQDDBVta2NlcnQgYWxleGFuZGVyQG1pa3UwHhcNMjMw
|
||||
NDAyMTM1ODIxWhcNMjUwNzAyMTM1ODIxWjBCMScwJQYDVQQKEx5ta2NlcnQgZGV2
|
||||
ZWxvcG1lbnQgY2VydGlmaWNhdGUxFzAVBgNVBAsMDmFsZXhhbmRlckBtaWt1MIIB
|
||||
IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1DElEXPY+VDQP7cSikK0ne0K
|
||||
gDgorGYPG9R7lOeuPLHyFYYry78+hB037OT0BOyA2uTu1yrog0dI/4YGicPDIqXh
|
||||
IgHfjV+4kMi5SgO7ECWOBmZFqTC3bBwvbNtoW40aFjYSFaOkm/nnfp+nalEJJZ/N
|
||||
kSkD4gdT3pH1ClsovlI4BlsxeIoJtyGzxMidJVXDAqMNraLatzJBwnT3OEs93xTf
|
||||
7Kd1KUpQp9OZFrGi15zv/n6tCmrcC3xMOVHuYkhW0UCTFmev7ZqbghQsQ9N9s0E6
|
||||
kk9rUf9xtMNH4Af6+2YRkT1DAGQ6FkXl1nQdB5H5XRgOBl+3k9s8wUrxQvQddQID
|
||||
AQABo14wXDAOBgNVHQ8BAf8EBAMCBaAwEwYDVR0lBAwwCgYIKwYBBQUHAwEwHwYD
|
||||
VR0jBBgwFoAU54aUZ+dytAOBTsYIdGtSnjiig/gwFAYDVR0RBA0wC4IJbG9jYWxo
|
||||
b3N0MA0GCSqGSIb3DQEBCwUAA4IBgQBU8p7Ua0Cs+lXlWmtCh2j+YF9R+dvc+3Iw
|
||||
dYEzCmYd375uxPctyHXW0yYjyuH9WuYn0F7OicEFEeC2+exHND+/z0J2Zv5yu34r
|
||||
SfgHVfvE/Vxisn9InYrUCVtfRwLDF3HgLyIlm8FVzIyiIANhpe6vJdqjEWTsiL2X
|
||||
I6hoDf1xlRgEqUx+Wxl2IFWrg+1SPPGTQzDPImiRlz8d+9ZJ9v48vaV5+aITMvDP
|
||||
Gfm/bnNXXd5Gf7nGwL8zFHiwLoYQ5AUYl0IfXYwFAXJ72+LjiRT33IOidVJF0gsQ
|
||||
6k9cTsc4lIrt4FOzdchalbF1Eu2prieWoZxz0apG8OuUeAhaB+t8kT6swAkwvkLW
|
||||
OnlSATm9Cls9Pc4XDHTbZlbMmwF2Jmukgz/l1vlTutt4ZgZwQkSEa9Qfoi9Zym0R
|
||||
iKls1CgD49zguR/cFDKK3agvfv6Afw6HdgaS/WqcI/Ros7b+RCkbAlAG5gqr6BLQ
|
||||
8RGyVjZSC4Mz/ddcnMEpRAnjuFJjhGA=
|
||||
-----END CERTIFICATE-----
|
28
integration_tests/certs/localhost.key
Normal file
@ -0,0 +1,28 @@
|
||||
-----BEGIN PRIVATE KEY-----
|
||||
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDUMSURc9j5UNA/
|
||||
txKKQrSd7QqAOCisZg8b1HuU5648sfIVhivLvz6EHTfs5PQE7IDa5O7XKuiDR0j/
|
||||
hgaJw8MipeEiAd+NX7iQyLlKA7sQJY4GZkWpMLdsHC9s22hbjRoWNhIVo6Sb+ed+
|
||||
n6dqUQkln82RKQPiB1PekfUKWyi+UjgGWzF4igm3IbPEyJ0lVcMCow2totq3MkHC
|
||||
dPc4Sz3fFN/sp3UpSlCn05kWsaLXnO/+fq0KatwLfEw5Ue5iSFbRQJMWZ6/tmpuC
|
||||
FCxD032zQTqST2tR/3G0w0fgB/r7ZhGRPUMAZDoWReXWdB0HkfldGA4GX7eT2zzB
|
||||
SvFC9B11AgMBAAECggEAYaj4yY6LFzxVjG2i79WBsYnOonK2bZpPa9ygwEjdTXwM
|
||||
0lE9SPoNONsFyVca5EVBjP1+27MY7orZkxlJWxCpeAHmmzNHg5bBqIlpliIfb3AJ
|
||||
bPKXLyaH1Q8n2K8m2bQYhI6ARktZ0Jv1KrcqY2lGj3V8NEovSlFbDX4ZzJlmKCly
|
||||
d4Ia6eQ7f9AjgsOwpQGeCTF7WLaVDnch6D4JfCGrW08lFeaqogiBQczsOE3hcNSd
|
||||
tEul21Z0CkC7Iiw28KdkApPINquo1VYdAcOvUCOXkwJfPC1gsJwK4O2jxfi9v5NF
|
||||
uU1niK0/00b396pQKvXpkfViynexwzK0MZCoo3zuQQKBgQDzaZexcniQNDyWqN3C
|
||||
oMe4V3rnxs+aO/lu8Ed3mng+Jf4vuarZlxNot7WRBMGT/T+b7/UIrqRJy50CYAPY
|
||||
3RRR84tLg3UMwUWhDYsPucNc2icODBG4c+QWJ300W19r+J+iT8PwS9AbH2n094Rn
|
||||
LCRYFrX5aMsgIH5uwuncKzweMQKBgQDfKj2i1ptC53aOcr1tMCFYcnMGtaAZ8u6+
|
||||
cKSgnzKlTw/g0EYlGcETUnCyZe0oVYWp3y859FBXU0JMDmxu84aYEZNF6BwRVlpF
|
||||
feQgtUFZHyf9MepQGhjIJ5El8n7jhh1bsBY18QbDFe6/GtqPx/mQEF7vE+wPFl9h
|
||||
putwdv3OhQKBgGKPyi2/BVSW4kW7IPiTM+vP+GNrnFp+mHS0dKvYb4HyzmcyzhyH
|
||||
UQOhB7Mt8thivmP9GQIn/TwoZ24zxLsGYhkA/dFY7Id6pyAcpMd8V7/8Ub4dYvuG
|
||||
acASw1709MF6jeEiXVuqxxyEbtoTc5h3Rkwo/gx8w2tB3RAqepl9JD2xAoGAfVL3
|
||||
ci8a2iOqTKza/Cp/T3BWcHonAuuOb5xKl3lPs84GmLXd7o/cAcHWUBk1aeU9Pvx7
|
||||
RQyS4bd8D8I52sUf3N5h2mxS9tmLsGLWbhfcLvR0PJh/gaRmLmEp/imEYLm8WvU0
|
||||
Q+6rYXs7rE6kVwJygBjxd0m003Q49FoM9gec2RECgYEA5SLAe2UmJSLIb0DKk27o
|
||||
nSfARDSdi9N40vIjDFHmDRdKTOYicED/f7KqXnxVpvFxDdCvJ7xeC4V7vkaqiiwd
|
||||
/oMLQq0GjmBxG/PNd1AFIWDydyH+JcY6U4XWIzIw92OKVYC/KMvd2f9orTfmDyAU
|
||||
RsGMfgV90kCzouAZKy3yPmo=
|
||||
-----END PRIVATE KEY-----
|
4
integration_tests/create_users.sh
Normal file
@ -0,0 +1,4 @@
|
||||
set -ex
|
||||
|
||||
prosodyctl --config ./prosody.cfg.lua register testuser1 localhost abc123
|
||||
prosodyctl --config ./prosody.cfg.lua register testuser2 localhost abc123
|
62
integration_tests/prosody.cfg.lua
Normal file
@ -0,0 +1,62 @@
|
||||
admins = { }
|
||||
plugin_paths = {}
|
||||
|
||||
modules_enabled = {
|
||||
-- Generally required
|
||||
"disco"; -- Service discovery
|
||||
"roster"; -- Allow users to have a roster. Recommended ;)
|
||||
"saslauth"; -- Authentication for clients and servers. Recommended if you want to log in.
|
||||
"tls"; -- Add support for secure TLS on c2s/s2s connections
|
||||
|
||||
-- Not essential, but recommended
|
||||
"blocklist"; -- Allow users to block communications with other users
|
||||
"bookmarks"; -- Synchronise the list of open rooms between clients
|
||||
"carbons"; -- Keep multiple online clients in sync
|
||||
"dialback"; -- Support for verifying remote servers using DNS
|
||||
"limits"; -- Enable bandwidth limiting for XMPP connections
|
||||
"pep"; -- Allow users to store public and private data in their account
|
||||
"private"; -- Legacy account storage mechanism (XEP-0049)
|
||||
"smacks"; -- Stream management and resumption (XEP-0198)
|
||||
"vcard4"; -- User profiles (stored in PEP)
|
||||
"vcard_legacy"; -- Conversion between legacy vCard and PEP Avatar, vcard
|
||||
|
||||
-- Nice to have
|
||||
"csi_simple"; -- Simple but effective traffic optimizations for mobile devices
|
||||
"invites"; -- Create and manage invites
|
||||
"invites_adhoc"; -- Allow admins/users to create invitations via their client
|
||||
"invites_register"; -- Allows invited users to create accounts
|
||||
"ping"; -- Replies to XMPP pings with pongs
|
||||
"register"; -- Allow users to register on this server using a client and change passwords
|
||||
"time"; -- Let others know the time here on this server
|
||||
"uptime"; -- Report how long server has been running
|
||||
"version"; -- Replies to server version requests
|
||||
|
||||
-- SASL2
|
||||
"sasl2";
|
||||
"sasl2_sm";
|
||||
"sasl2_fast";
|
||||
"sasl2_bind2";
|
||||
}
|
||||
|
||||
s2s_secure_auth = false
|
||||
|
||||
-- Authentication
|
||||
authentication = "internal_plain"
|
||||
|
||||
-- Storage
|
||||
storage = "internal"
|
||||
data_path = "/tmp/prosody-data/"
|
||||
log = {
|
||||
debug = "*console";
|
||||
}
|
||||
|
||||
pidfile = "/tmp/prosody.pid"
|
||||
|
||||
component_ports = { 8888 }
|
||||
component_interfaces = { '127.0.0.1' }
|
||||
VirtualHost "localhost"
|
||||
|
||||
Component "component.localhost"
|
||||
component_secret = "abc123"
|
||||
|
||||
Component "muc.localhost" "muc"
|
18
integration_tests/pubspec.yaml
Normal file
@ -0,0 +1,18 @@
|
||||
name: integration_tests
|
||||
description: A sample command-line application.
|
||||
version: 1.0.0
|
||||
|
||||
environment:
|
||||
sdk: ">=3.0.0 <4.0.0"
|
||||
|
||||
dependencies:
|
||||
logging: ^1.3.0
|
||||
moxxmpp:
|
||||
path: ../packages/moxxmpp
|
||||
moxxmpp_socket_tcp:
|
||||
path: ../packages/moxxmpp_socket_tcp
|
||||
|
||||
dev_dependencies:
|
||||
build_runner: ^2.4.13
|
||||
test: ^1.25.8
|
||||
very_good_analysis: ^6.0.0
|
44
integration_tests/test/component_test.dart
Normal file
@ -0,0 +1,44 @@
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:moxxmpp/moxxmpp.dart';
|
||||
import 'package:moxxmpp_socket_tcp/moxxmpp_socket_tcp.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
class TestingTCPSocketWrapper extends TCPSocketWrapper {
|
||||
TestingTCPSocketWrapper() : super(true);
|
||||
|
||||
@override
|
||||
bool onBadCertificate(dynamic certificate, String domain) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
void main() {
|
||||
Logger.root.level = Level.ALL;
|
||||
Logger.root.onRecord.listen((record) {
|
||||
// ignore: avoid_print
|
||||
print(
|
||||
'[${record.level.name}] (${record.loggerName}) ${record.time}: ${record.message}',
|
||||
);
|
||||
});
|
||||
|
||||
test('Test connecting to prosody as a component', () async {
|
||||
final conn = XmppConnection(
|
||||
TestingReconnectionPolicy(),
|
||||
AlwaysConnectedConnectivityManager(),
|
||||
ComponentToServerNegotiator(),
|
||||
TestingTCPSocketWrapper(),
|
||||
)..connectionSettings = ConnectionSettings(
|
||||
jid: JID.fromString('component.localhost'),
|
||||
password: 'abc123',
|
||||
host: '127.0.0.1',
|
||||
port: 8888,
|
||||
);
|
||||
|
||||
final result = await conn.connect(
|
||||
waitUntilLogin: true,
|
||||
shouldReconnect: false,
|
||||
enableReconnectOnSuccess: false,
|
||||
);
|
||||
expect(result.isType<bool>(), true);
|
||||
});
|
||||
}
|
77
integration_tests/test/sasl2_test.dart
Normal file
@ -0,0 +1,77 @@
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:moxxmpp/moxxmpp.dart';
|
||||
import 'package:moxxmpp_socket_tcp/moxxmpp_socket_tcp.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
class TestingTCPSocketWrapper extends TCPSocketWrapper {
|
||||
TestingTCPSocketWrapper() : super(true);
|
||||
|
||||
@override
|
||||
bool onBadCertificate(dynamic certificate, String domain) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
void main() {
|
||||
Logger.root.level = Level.ALL;
|
||||
Logger.root.onRecord.listen((record) {
|
||||
// ignore: avoid_print
|
||||
print(
|
||||
'[${record.level.name}] (${record.loggerName}) ${record.time}: ${record.message}',
|
||||
);
|
||||
});
|
||||
|
||||
test('Test authenticating against Prosody with SASL2, Bind2, and FAST',
|
||||
() async {
|
||||
final conn = XmppConnection(
|
||||
TestingReconnectionPolicy(),
|
||||
AlwaysConnectedConnectivityManager(),
|
||||
ClientToServerNegotiator(),
|
||||
TestingTCPSocketWrapper(),
|
||||
)..connectionSettings = ConnectionSettings(
|
||||
jid: JID.fromString('testuser1@localhost'),
|
||||
password: 'abc123',
|
||||
host: '127.0.0.1',
|
||||
port: 5222,
|
||||
);
|
||||
final csi = CSIManager();
|
||||
await csi.setInactive(sendNonza: false);
|
||||
await conn.registerManagers([
|
||||
RosterManager(TestingRosterStateManager('', [])),
|
||||
DiscoManager([]),
|
||||
]);
|
||||
await conn.registerFeatureNegotiators([
|
||||
SaslPlainNegotiator(),
|
||||
SaslScramNegotiator(9, '', '', ScramHashType.sha1),
|
||||
SaslScramNegotiator(10, '', '', ScramHashType.sha256),
|
||||
ResourceBindingNegotiator(),
|
||||
FASTSaslNegotiator(),
|
||||
Bind2Negotiator(),
|
||||
StartTlsNegotiator(),
|
||||
Sasl2Negotiator()
|
||||
..userAgent = const UserAgent(
|
||||
id: 'd4565fa7-4d72-4749-b3d3-740edbf87770',
|
||||
software: 'moxxmpp',
|
||||
device: "PapaTutuWawa's awesome device",
|
||||
),
|
||||
]);
|
||||
|
||||
final result = await conn.connect(
|
||||
waitUntilLogin: true,
|
||||
shouldReconnect: false,
|
||||
enableReconnectOnSuccess: false,
|
||||
);
|
||||
expect(result.isType<bool>(), true);
|
||||
expect(
|
||||
conn.getNegotiatorById<Sasl2Negotiator>(sasl2Negotiator)!.state,
|
||||
NegotiatorState.done,
|
||||
);
|
||||
expect(
|
||||
conn
|
||||
.getNegotiatorById<FASTSaslNegotiator>(saslFASTNegotiator)!
|
||||
.fastToken !=
|
||||
null,
|
||||
true,
|
||||
);
|
||||
});
|
||||
}
|
35
nix/moxxmpp-docs.nix
Normal file
@ -0,0 +1,35 @@
|
||||
{
|
||||
stdenv
|
||||
, pubCache
|
||||
, dart
|
||||
, lib
|
||||
}:
|
||||
|
||||
stdenv.mkDerivation {
|
||||
pname = "moxxmpp-docs";
|
||||
version = "0.3.1";
|
||||
|
||||
PUB_CACHE = "${pubCache}";
|
||||
|
||||
src = "${./..}/packages/moxxmpp";
|
||||
|
||||
buildPhase = ''
|
||||
runHook preBuild
|
||||
|
||||
(
|
||||
set -x
|
||||
echo $PUB_CACHE
|
||||
${dart}/bin/dart pub get --no-precompile --offline
|
||||
)
|
||||
|
||||
runHook postBuild
|
||||
'';
|
||||
|
||||
installPhase = ''
|
||||
runHook preInstall
|
||||
|
||||
${dart}/bin/dart doc -o $out
|
||||
|
||||
runHook postInstall
|
||||
'';
|
||||
}
|
813
nix/moxxmpp.lock
Normal file
@ -0,0 +1,813 @@
|
||||
# CREATED BY pubspec2lock.py
|
||||
# DO NOT EDIT BY HAND
|
||||
packages:
|
||||
_fe_analyzer_shared:
|
||||
archive_url: https://pub.dartlang.org/packages/_fe_analyzer_shared/versions/61.0.0.tar.gz
|
||||
dependency: transitive
|
||||
description:
|
||||
name: _fe_analyzer_shared
|
||||
sha256: ae92f5d747aee634b87f89d9946000c2de774be1d6ac3e58268224348cd0101a
|
||||
url: https://pub.dev
|
||||
sha256: 15fh9ka41dw4qsynv07msq4i243fibprcmafdygw5x88f7m55fq3
|
||||
source: hosted
|
||||
version: 61.0.0
|
||||
analyzer:
|
||||
archive_url: https://pub.dartlang.org/packages/analyzer/versions/5.13.0.tar.gz
|
||||
dependency: transitive
|
||||
description:
|
||||
name: analyzer
|
||||
sha256: ea3d8652bda62982addfd92fdc2d0214e5f82e43325104990d4f4c4a2a313562
|
||||
url: https://pub.dev
|
||||
sha256: 0w604zngxwfx0xqxvhbxrhdh04wgm6ad6a1lbwnyvmk57amv44np
|
||||
source: hosted
|
||||
version: 5.13.0
|
||||
args:
|
||||
archive_url: https://pub.dartlang.org/packages/args/versions/2.4.2.tar.gz
|
||||
dependency: transitive
|
||||
description:
|
||||
name: args
|
||||
sha256: eef6c46b622e0494a36c5a12d10d77fb4e855501a91c1b9ef9339326e58f0596
|
||||
url: https://pub.dev
|
||||
sha256: 01ps253280c6dbx0vncw4wga4l2qp1zx779qjj2x06xzb3744zbz
|
||||
source: hosted
|
||||
version: 2.4.2
|
||||
async:
|
||||
archive_url: https://pub.dartlang.org/packages/async/versions/2.11.0.tar.gz
|
||||
dependency: transitive
|
||||
description:
|
||||
name: async
|
||||
sha256: 947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c
|
||||
url: https://pub.dev
|
||||
sha256: 0hfgvjajp5c2mw68186hgrk9v5zjhhi149hlhl0fap274p2v1g3q
|
||||
source: hosted
|
||||
version: 2.11.0
|
||||
boolean_selector:
|
||||
archive_url: https://pub.dartlang.org/packages/boolean_selector/versions/2.1.1.tar.gz
|
||||
dependency: transitive
|
||||
description:
|
||||
name: boolean_selector
|
||||
sha256: 6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66
|
||||
url: https://pub.dev
|
||||
sha256: 0hxq8072hb89q9s91xlz9fvrjxfy7hw6jkdwkph5dp77df841kmj
|
||||
source: hosted
|
||||
version: 2.1.1
|
||||
build:
|
||||
archive_url: https://pub.dartlang.org/packages/build/versions/2.3.1.tar.gz
|
||||
dependency: transitive
|
||||
description:
|
||||
name: build
|
||||
sha256: 3fbda25365741f8251b39f3917fb3c8e286a96fd068a5a242e11c2012d495777
|
||||
url: https://pub.dev
|
||||
sha256: 1x6nkii6kqy6y7ck0151yfhc9lp2nvbhznnhdi2mxr8afk6jxigd
|
||||
source: hosted
|
||||
version: 2.3.1
|
||||
build_config:
|
||||
archive_url: https://pub.dartlang.org/packages/build_config/versions/1.1.1.tar.gz
|
||||
dependency: transitive
|
||||
description:
|
||||
name: build_config
|
||||
sha256: bf80fcfb46a29945b423bd9aad884590fb1dc69b330a4d4700cac476af1708d1
|
||||
url: https://pub.dev
|
||||
sha256: 092rrbhbdy9fk50jqb1fwj1sfk415fi43irvsd0hk5w90gn8vazj
|
||||
source: hosted
|
||||
version: 1.1.1
|
||||
build_daemon:
|
||||
archive_url: https://pub.dartlang.org/packages/build_daemon/versions/3.1.1.tar.gz
|
||||
dependency: transitive
|
||||
description:
|
||||
name: build_daemon
|
||||
sha256: 757153e5d9cd88253cb13f28c2fb55a537dc31fefd98137549895b5beb7c6169
|
||||
url: https://pub.dev
|
||||
sha256: 1wn7bq846vgdj62bkh9h25l95xdsndv0jdyw52nyr0591l3bpg3h
|
||||
source: hosted
|
||||
version: 3.1.1
|
||||
build_resolvers:
|
||||
archive_url: https://pub.dartlang.org/packages/build_resolvers/versions/2.3.1.tar.gz
|
||||
dependency: transitive
|
||||
description:
|
||||
name: build_resolvers
|
||||
sha256: a7417cc44d9edb3f2c8760000270c99dba8c72ff66d0146772b8326565780745
|
||||
url: https://pub.dev
|
||||
sha256: 00h9abhrfmnl0xxziyf6p68sxnbv2ww1c4dhgpnz00mzbmamnq5c
|
||||
source: hosted
|
||||
version: 2.3.1
|
||||
build_runner:
|
||||
archive_url: https://pub.dartlang.org/packages/build_runner/versions/2.3.3.tar.gz
|
||||
dependency: direct dev
|
||||
description:
|
||||
name: build_runner
|
||||
sha256: b0a8a7b8a76c493e85f1b84bffa0588859a06197863dba8c9036b15581fd9727
|
||||
url: https://pub.dev
|
||||
sha256: 0b5ha1l6k0gn2swqgqvfy2vl58klf81sxrjnmk0p7rj1wzbqjm7l
|
||||
source: hosted
|
||||
version: 2.3.3
|
||||
build_runner_core:
|
||||
archive_url: https://pub.dartlang.org/packages/build_runner_core/versions/7.2.7%2B1.tar.gz
|
||||
dependency: transitive
|
||||
description:
|
||||
name: build_runner_core
|
||||
sha256: 0671ad4162ed510b70d0eb4ad6354c249f8429cab4ae7a4cec86bbc2886eb76e
|
||||
url: https://pub.dev
|
||||
sha256: 07r1kfy6ylm4i4xrb24ns8l26h4h1lgcskmnf8wvq2rd5d5hq790
|
||||
source: hosted
|
||||
version: 7.2.7+1
|
||||
built_collection:
|
||||
archive_url: https://pub.dartlang.org/packages/built_collection/versions/5.1.1.tar.gz
|
||||
dependency: transitive
|
||||
description:
|
||||
name: built_collection
|
||||
sha256: 376e3dd27b51ea877c28d525560790aee2e6fbb5f20e2f85d5081027d94e2100
|
||||
url: https://pub.dev
|
||||
sha256: 0bqjahxr42q84w91nhv3n4cr580l3s3ffx3vgzyyypgqnrck0hv3
|
||||
source: hosted
|
||||
version: 5.1.1
|
||||
built_value:
|
||||
archive_url: https://pub.dartlang.org/packages/built_value/versions/8.6.2.tar.gz
|
||||
dependency: transitive
|
||||
description:
|
||||
name: built_value
|
||||
sha256: ff627b645b28fb8bdb69e645f910c2458fd6b65f6585c3a53e0626024897dedf
|
||||
url: https://pub.dev
|
||||
sha256: 1y84imf9xqqy3gnd5zz9bcln6mycy7qx35r70b0izm31ismlbzkv
|
||||
source: hosted
|
||||
version: 8.6.2
|
||||
checked_yaml:
|
||||
archive_url: https://pub.dartlang.org/packages/checked_yaml/versions/2.0.3.tar.gz
|
||||
dependency: transitive
|
||||
description:
|
||||
name: checked_yaml
|
||||
sha256: feb6bed21949061731a7a75fc5d2aa727cf160b91af9a3e464c5e3a32e28b5ff
|
||||
url: https://pub.dev
|
||||
sha256: 1sn01yrmj0pkijn08g3v45c3zmyvdygk9svigkkzybgicdwlkpqs
|
||||
source: hosted
|
||||
version: 2.0.3
|
||||
code_builder:
|
||||
archive_url: https://pub.dartlang.org/packages/code_builder/versions/4.6.0.tar.gz
|
||||
dependency: transitive
|
||||
description:
|
||||
name: code_builder
|
||||
sha256: 315a598c7fbe77f22de1c9da7cfd6fd21816312f16ffa124453b4fc679e540f1
|
||||
url: https://pub.dev
|
||||
sha256: 1shgl7mxiyv0hhw326yqj2b9jxi1h74qxmsnxf1d1xc6yz766p9a
|
||||
source: hosted
|
||||
version: 4.6.0
|
||||
collection:
|
||||
archive_url: https://pub.dartlang.org/packages/collection/versions/1.18.0.tar.gz
|
||||
dependency: direct main
|
||||
description:
|
||||
name: collection
|
||||
sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a
|
||||
url: https://pub.dev
|
||||
sha256: 1mr8j0078c4z9hhckiq8m735rggsazwfprm0w9gisil51vh7j2mk
|
||||
source: hosted
|
||||
version: 1.18.0
|
||||
convert:
|
||||
archive_url: https://pub.dartlang.org/packages/convert/versions/3.1.1.tar.gz
|
||||
dependency: transitive
|
||||
description:
|
||||
name: convert
|
||||
sha256: 0f08b14755d163f6e2134cb58222dd25ea2a2ee8a195e53983d57c075324d592
|
||||
url: https://pub.dev
|
||||
sha256: 0adsigjk3l1c31i6k91p28dqyjlgwiqrs4lky5djrm2scf8k6cri
|
||||
source: hosted
|
||||
version: 3.1.1
|
||||
coverage:
|
||||
archive_url: https://pub.dartlang.org/packages/coverage/versions/1.6.3.tar.gz
|
||||
dependency: transitive
|
||||
description:
|
||||
name: coverage
|
||||
sha256: 2fb815080e44a09b85e0f2ca8a820b15053982b2e714b59267719e8a9ff17097
|
||||
url: https://pub.dev
|
||||
sha256: 1yy9bgkax5b6kk7qa07p452v82fyj4rl1j03fn366ywyvhfrh6lp
|
||||
source: hosted
|
||||
version: 1.6.3
|
||||
crypto:
|
||||
archive_url: https://pub.dartlang.org/packages/crypto/versions/3.0.3.tar.gz
|
||||
dependency: transitive
|
||||
description:
|
||||
name: crypto
|
||||
sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab
|
||||
url: https://pub.dev
|
||||
sha256: 100ai8qa4p3dyvvd60c4xa9p0gm06yh0d68xgcfm3giraad8xmqj
|
||||
source: hosted
|
||||
version: 3.0.3
|
||||
cryptography:
|
||||
archive_url: https://pub.dartlang.org/packages/cryptography/versions/2.5.0.tar.gz
|
||||
dependency: direct main
|
||||
description:
|
||||
name: cryptography
|
||||
sha256: df156c5109286340817d21fa7b62f9140f17915077127dd70f8bd7a2a0997a35
|
||||
url: https://pub.dev
|
||||
sha256: 1yxn9slqq93ri81fbr2nbsinz0mpk9wk39ny076ja8q31d4i8v3f
|
||||
source: hosted
|
||||
version: 2.5.0
|
||||
dart_style:
|
||||
archive_url: https://pub.dartlang.org/packages/dart_style/versions/2.3.2.tar.gz
|
||||
dependency: transitive
|
||||
description:
|
||||
name: dart_style
|
||||
sha256: 1efa911ca7086affd35f463ca2fc1799584fb6aa89883cf0af8e3664d6a02d55
|
||||
url: https://pub.dev
|
||||
sha256: 0cjhrb1hs8iw9smmfd0fgnxq3nm0w8sz17l6q6svyz6kif19wk9k
|
||||
source: hosted
|
||||
version: 2.3.2
|
||||
file:
|
||||
archive_url: https://pub.dartlang.org/packages/file/versions/6.1.4.tar.gz
|
||||
dependency: transitive
|
||||
description:
|
||||
name: file
|
||||
sha256: 1b92bec4fc2a72f59a8e15af5f52cd441e4a7860b49499d69dfa817af20e925d
|
||||
url: https://pub.dev
|
||||
sha256: 0ajcfblf8d4dicp1sgzkbrhd0b0v0d8wl70jsnf5drjck3p3ppk7
|
||||
source: hosted
|
||||
version: 6.1.4
|
||||
fixnum:
|
||||
archive_url: https://pub.dartlang.org/packages/fixnum/versions/1.1.0.tar.gz
|
||||
dependency: transitive
|
||||
description:
|
||||
name: fixnum
|
||||
sha256: 25517a4deb0c03aa0f32fd12db525856438902d9c16536311e76cdc57b31d7d1
|
||||
url: https://pub.dev
|
||||
sha256: 0nqrzj41ys8dpxf1x70r0kfj1avj0f2j2b7498k8kvc0i9c5asz7
|
||||
source: hosted
|
||||
version: 1.1.0
|
||||
frontend_server_client:
|
||||
archive_url: https://pub.dartlang.org/packages/frontend_server_client/versions/3.2.0.tar.gz
|
||||
dependency: transitive
|
||||
description:
|
||||
name: frontend_server_client
|
||||
sha256: 408e3ca148b31c20282ad6f37ebfa6f4bdc8fede5b74bc2f08d9d92b55db3612
|
||||
url: https://pub.dev
|
||||
sha256: 096v7ycix5hgnk750s1qgykyghl2mymhdkg39jrlk3kbj6xygq5b
|
||||
source: hosted
|
||||
version: 3.2.0
|
||||
glob:
|
||||
archive_url: https://pub.dartlang.org/packages/glob/versions/2.1.2.tar.gz
|
||||
dependency: transitive
|
||||
description:
|
||||
name: glob
|
||||
sha256: 0e7014b3b7d4dac1ca4d6114f82bf1782ee86745b9b42a92c9289c23d8a0ab63
|
||||
url: https://pub.dev
|
||||
sha256: 0ffab3azx8zkma36mk6wnig8bn8g5g0vjrq2gl21y77rxgw9iqxj
|
||||
source: hosted
|
||||
version: 2.1.2
|
||||
graphs:
|
||||
archive_url: https://pub.dartlang.org/packages/graphs/versions/2.3.1.tar.gz
|
||||
dependency: transitive
|
||||
description:
|
||||
name: graphs
|
||||
sha256: aedc5a15e78fc65a6e23bcd927f24c64dd995062bcd1ca6eda65a3cff92a4d19
|
||||
url: https://pub.dev
|
||||
sha256: 0fda0j8y6sq1rc9zpzglrzysl5h49y2ji1wq2lq0wx2c609dxm7f
|
||||
source: hosted
|
||||
version: 2.3.1
|
||||
hex:
|
||||
archive_url: https://pub.dartlang.org/packages/hex/versions/0.2.0.tar.gz
|
||||
dependency: direct main
|
||||
description:
|
||||
name: hex
|
||||
sha256: 4e7cd54e4b59ba026432a6be2dd9d96e4c5205725194997193bf871703b82c4a
|
||||
url: https://pub.dev
|
||||
sha256: 19w3f90mdiy06a6kf8hlwc4jn4cxixkj106kc3g3bis27ar7smkh
|
||||
source: hosted
|
||||
version: 0.2.0
|
||||
http_multi_server:
|
||||
archive_url: https://pub.dartlang.org/packages/http_multi_server/versions/3.2.1.tar.gz
|
||||
dependency: transitive
|
||||
description:
|
||||
name: http_multi_server
|
||||
sha256: 97486f20f9c2f7be8f514851703d0119c3596d14ea63227af6f7a481ef2b2f8b
|
||||
url: https://pub.dev
|
||||
sha256: 1zdcm04z85jahb2hs7qs85rh974kw49hffhy9cn1gfda3077dvql
|
||||
source: hosted
|
||||
version: 3.2.1
|
||||
http_parser:
|
||||
archive_url: https://pub.dartlang.org/packages/http_parser/versions/4.0.2.tar.gz
|
||||
dependency: transitive
|
||||
description:
|
||||
name: http_parser
|
||||
sha256: 2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b
|
||||
url: https://pub.dev
|
||||
sha256: 027c4sjkhkkx3sk1aqs6s4djb87syi9h521qpm1bf21bq3gga5jd
|
||||
source: hosted
|
||||
version: 4.0.2
|
||||
io:
|
||||
archive_url: https://pub.dartlang.org/packages/io/versions/1.0.4.tar.gz
|
||||
dependency: transitive
|
||||
description:
|
||||
name: io
|
||||
sha256: 2ec25704aba361659e10e3e5f5d672068d332fc8ac516421d483a11e5cbd061e
|
||||
url: https://pub.dev
|
||||
sha256: 101kd0rw26vglmr1m5p130kbrp3k7dk4p5nr77wsbwgg53w8c0d4
|
||||
source: hosted
|
||||
version: 1.0.4
|
||||
js:
|
||||
archive_url: https://pub.dartlang.org/packages/js/versions/0.6.7.tar.gz
|
||||
dependency: transitive
|
||||
description:
|
||||
name: js
|
||||
sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3
|
||||
url: https://pub.dev
|
||||
sha256: 124a9yqrjdw3p4nnirab9hm9ziwraldlw4q5cb3sr0dcrli74qpw
|
||||
source: hosted
|
||||
version: 0.6.7
|
||||
json_annotation:
|
||||
archive_url: https://pub.dartlang.org/packages/json_annotation/versions/4.8.1.tar.gz
|
||||
dependency: transitive
|
||||
description:
|
||||
name: json_annotation
|
||||
sha256: b10a7b2ff83d83c777edba3c6a0f97045ddadd56c944e1a23a3fdf43a1bf4467
|
||||
url: https://pub.dev
|
||||
sha256: 1jjw7p8qyqajgdq4jqvxipq5w0qrq9dpi1qmia70pk995akryh6m
|
||||
source: hosted
|
||||
version: 4.8.1
|
||||
json_serializable:
|
||||
archive_url: https://pub.dartlang.org/packages/json_serializable/versions/6.6.2.tar.gz
|
||||
dependency: direct main
|
||||
description:
|
||||
name: json_serializable
|
||||
sha256: 43793352f90efa5d8b251893a63d767b2f7c833120e3cc02adad55eefec04dc7
|
||||
url: https://pub.dev
|
||||
sha256: 1pmidql9x6s2pbhdx9x20pwqwvwpfkvrz0h0cm1f8pqis76c90hb
|
||||
source: hosted
|
||||
version: 6.6.2
|
||||
logging:
|
||||
archive_url: https://pub.dartlang.org/packages/logging/versions/1.2.0.tar.gz
|
||||
dependency: direct main
|
||||
description:
|
||||
name: logging
|
||||
sha256: 623a88c9594aa774443aa3eb2d41807a48486b5613e67599fb4c41c0ad47c340
|
||||
url: https://pub.dev
|
||||
sha256: 124hfjs66r30p92ndfmy5fymgy66yk9in97h8sq6fi7r78pqyc7g
|
||||
source: hosted
|
||||
version: 1.2.0
|
||||
matcher:
|
||||
archive_url: https://pub.dartlang.org/packages/matcher/versions/0.12.16.tar.gz
|
||||
dependency: transitive
|
||||
description:
|
||||
name: matcher
|
||||
sha256: 1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e
|
||||
url: https://pub.dev
|
||||
sha256: 0inznqkrxqnq09lcbwvda3xd07qfm1k3aa6dv1wy39gvci8hybss
|
||||
source: hosted
|
||||
version: 0.12.16
|
||||
meta:
|
||||
archive_url: https://pub.dartlang.org/packages/meta/versions/1.9.1.tar.gz
|
||||
dependency: direct main
|
||||
description:
|
||||
name: meta
|
||||
sha256: 3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3
|
||||
url: https://pub.dev
|
||||
sha256: 1l3zaz6q2s9mnm7s674xshsfqspy79p5kdbbnc99rf2l76avv4h3
|
||||
source: hosted
|
||||
version: 1.9.1
|
||||
mime:
|
||||
archive_url: https://pub.dartlang.org/packages/mime/versions/1.0.4.tar.gz
|
||||
dependency: transitive
|
||||
description:
|
||||
name: mime
|
||||
sha256: e4ff8e8564c03f255408decd16e7899da1733852a9110a58fe6d1b817684a63e
|
||||
url: https://pub.dev
|
||||
sha256: 1dha9z64bsz8xhi0p62vmlyikr8xwbdlrw90hxghmm3rdgd9h25w
|
||||
source: hosted
|
||||
version: 1.0.4
|
||||
moxlib:
|
||||
archive_url: https://git.polynom.me/api/packages/moxxy/pub/api/packages/moxlib/files/0.2.0.tar.gz
|
||||
dependency: direct main
|
||||
description:
|
||||
name: moxlib
|
||||
sha256: 2a76a632d23ea73906964cee4463352995e40199036162217ea323a6c3846e73
|
||||
url: https://git.polynom.me/api/packages/Moxxy/pub/
|
||||
sha256: 1qaacmcqhq33grn2nq8sn23ki62dcmw0fqy589xm1zv6w0pzfmsk
|
||||
source: hosted
|
||||
version: 0.2.0
|
||||
node_preamble:
|
||||
archive_url: https://pub.dartlang.org/packages/node_preamble/versions/2.0.2.tar.gz
|
||||
dependency: transitive
|
||||
description:
|
||||
name: node_preamble
|
||||
sha256: 6e7eac89047ab8a8d26cf16127b5ed26de65209847630400f9aefd7cd5c730db
|
||||
url: https://pub.dev
|
||||
sha256: 12ajg76r9aqmqkavvlxbnb3sszg1szcq3f30badkd0xc25mnhyh8
|
||||
source: hosted
|
||||
version: 2.0.2
|
||||
omemo_dart:
|
||||
archive_url: https://git.polynom.me/api/packages/PapaTutuWawa/pub/api/packages/omemo_dart/files/0.5.1.tar.gz
|
||||
dependency: direct main
|
||||
description:
|
||||
name: omemo_dart
|
||||
sha256: 61f4695f105bc72663bd804350fc9cdb14c81e95fc190137a8344280e70fbc00
|
||||
url: https://git.polynom.me/api/packages/PapaTutuWawa/pub/
|
||||
sha256: 0fhf89ic5mdyld25l6rfb37a1fk1f0f2b4d72xi4r7pvr0ddjhz8
|
||||
source: hosted
|
||||
version: 0.5.1
|
||||
package_config:
|
||||
archive_url: https://pub.dartlang.org/packages/package_config/versions/2.1.0.tar.gz
|
||||
dependency: transitive
|
||||
description:
|
||||
name: package_config
|
||||
sha256: 1c5b77ccc91e4823a5af61ee74e6b972db1ef98c2ff5a18d3161c982a55448bd
|
||||
url: https://pub.dev
|
||||
sha256: 1d4l0i4cby344zj45f5shrg2pkw1i1jn03kx0qqh0l7gh1ha7bpc
|
||||
source: hosted
|
||||
version: 2.1.0
|
||||
path:
|
||||
archive_url: https://pub.dartlang.org/packages/path/versions/1.8.3.tar.gz
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path
|
||||
sha256: 8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917
|
||||
url: https://pub.dev
|
||||
sha256: 1mjdhq2fsz6i9krhp2mnaks2bcw34sa4p7mg0v6njk8dgx2754iv
|
||||
source: hosted
|
||||
version: 1.8.3
|
||||
pedantic:
|
||||
archive_url: https://pub.dartlang.org/packages/pedantic/versions/1.11.1.tar.gz
|
||||
dependency: transitive
|
||||
description:
|
||||
name: pedantic
|
||||
sha256: 67fc27ed9639506c856c840ccce7594d0bdcd91bc8d53d6e52359449a1d50602
|
||||
url: https://pub.dev
|
||||
sha256: 10ch0h3hi6cfwiz2ihfkh6m36m75c0m7fd0wwqaqggffsj2dn8ad
|
||||
source: hosted
|
||||
version: 1.11.1
|
||||
petitparser:
|
||||
archive_url: https://pub.dartlang.org/packages/petitparser/versions/5.4.0.tar.gz
|
||||
dependency: transitive
|
||||
description:
|
||||
name: petitparser
|
||||
sha256: cb3798bef7fc021ac45b308f4b51208a152792445cce0448c9a4ba5879dd8750
|
||||
url: https://pub.dev
|
||||
sha256: 19zqrpb1z77aw1k2s8rsxdfxczzv9934g2rdfj2jyiv3pqgdq8gh
|
||||
source: hosted
|
||||
version: 5.4.0
|
||||
pinenacl:
|
||||
archive_url: https://pub.dartlang.org/packages/pinenacl/versions/0.5.1.tar.gz
|
||||
dependency: transitive
|
||||
description:
|
||||
name: pinenacl
|
||||
sha256: 3a5503637587d635647c93ea9a8fecf48a420cc7deebe6f1fc85c2a5637ab327
|
||||
url: https://pub.dev
|
||||
sha256: 0didjgva658z90hbcmhd0y8w1b8v86dp6gabfhylnw1aixl47cxg
|
||||
source: hosted
|
||||
version: 0.5.1
|
||||
pool:
|
||||
archive_url: https://pub.dartlang.org/packages/pool/versions/1.5.1.tar.gz
|
||||
dependency: transitive
|
||||
description:
|
||||
name: pool
|
||||
sha256: 20fe868b6314b322ea036ba325e6fc0711a22948856475e2c2b6306e8ab39c2a
|
||||
url: https://pub.dev
|
||||
sha256: 0wmzs46hjszv3ayhr1p5l7xza7q9rkg2q9z4swmhdqmhlz3c50x4
|
||||
source: hosted
|
||||
version: 1.5.1
|
||||
protobuf:
|
||||
archive_url: https://pub.dartlang.org/packages/protobuf/versions/2.1.0.tar.gz
|
||||
dependency: transitive
|
||||
description:
|
||||
name: protobuf
|
||||
sha256: 01dd9bd0fa02548bf2ceee13545d4a0ec6046459d847b6b061d8a27237108a08
|
||||
url: https://pub.dev
|
||||
sha256: 1jriyisf8bnvq5ygjk93mn2yzdlnii7xrhy6aabz54xr3y4dcy9x
|
||||
source: hosted
|
||||
version: 2.1.0
|
||||
protoc_plugin:
|
||||
archive_url: https://pub.dartlang.org/packages/protoc_plugin/versions/20.0.1.tar.gz
|
||||
dependency: transitive
|
||||
description:
|
||||
name: protoc_plugin
|
||||
sha256: e2be5014ba145dc0f8de20ac425afa2a513aff64fe350d338e481d40de0573df
|
||||
url: https://pub.dev
|
||||
sha256: 0hjjd1xkv4s4g1d5n2aza0kdwlbfl2aivq99230m3yml7irn00jk
|
||||
source: hosted
|
||||
version: 20.0.1
|
||||
pub_semver:
|
||||
archive_url: https://pub.dartlang.org/packages/pub_semver/versions/2.1.4.tar.gz
|
||||
dependency: transitive
|
||||
description:
|
||||
name: pub_semver
|
||||
sha256: 40d3ab1bbd474c4c2328c91e3a7df8c6dd629b79ece4c4bd04bee496a224fb0c
|
||||
url: https://pub.dev
|
||||
sha256: 0wpcfz1crxipbjm18m71pl4vl2ra8vw1n93ff8snr54mmlyfb9z1
|
||||
source: hosted
|
||||
version: 2.1.4
|
||||
pubspec_parse:
|
||||
archive_url: https://pub.dartlang.org/packages/pubspec_parse/versions/1.2.3.tar.gz
|
||||
dependency: transitive
|
||||
description:
|
||||
name: pubspec_parse
|
||||
sha256: c63b2876e58e194e4b0828fcb080ad0e06d051cb607a6be51a9e084f47cb9367
|
||||
url: https://pub.dev
|
||||
sha256: 0dj8sf1w61g7vh1ly3sl690z0nwllzjzbapxswmgsglr0ndcyrs1
|
||||
source: hosted
|
||||
version: 1.2.3
|
||||
random_string:
|
||||
archive_url: https://pub.dartlang.org/packages/random_string/versions/2.3.1.tar.gz
|
||||
dependency: direct main
|
||||
description:
|
||||
name: random_string
|
||||
sha256: 03b52435aae8cbdd1056cf91bfc5bf845e9706724dd35ae2e99fa14a1ef79d02
|
||||
url: https://pub.dev
|
||||
sha256: 11cjiv75sgldvk3x7w6j77lgi08r6737wm94m3ylabylsr6zdyff
|
||||
source: hosted
|
||||
version: 2.3.1
|
||||
saslprep:
|
||||
archive_url: https://pub.dartlang.org/packages/saslprep/versions/1.0.2.tar.gz
|
||||
dependency: direct main
|
||||
description:
|
||||
name: saslprep
|
||||
sha256: 79c9e163a82f55da542feaf0f7a59031e74493299c92008b2b404cd88d639bb4
|
||||
url: https://pub.dev
|
||||
sha256: 04lss0xvm6p801p8306jdxg7k0b28kr6n65dz2f57dkca237kcw7
|
||||
source: hosted
|
||||
version: 1.0.2
|
||||
shelf:
|
||||
archive_url: https://pub.dartlang.org/packages/shelf/versions/1.4.1.tar.gz
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shelf
|
||||
sha256: ad29c505aee705f41a4d8963641f91ac4cee3c8fad5947e033390a7bd8180fa4
|
||||
url: https://pub.dev
|
||||
sha256: 10yk98nadrgj5d3r3241kdaywjjs1j10mg8gacv80kg1mhcfdrxp
|
||||
source: hosted
|
||||
version: 1.4.1
|
||||
shelf_packages_handler:
|
||||
archive_url: https://pub.dartlang.org/packages/shelf_packages_handler/versions/3.0.2.tar.gz
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shelf_packages_handler
|
||||
sha256: 89f967eca29607c933ba9571d838be31d67f53f6e4ee15147d5dc2934fee1b1e
|
||||
url: https://pub.dev
|
||||
sha256: 1h8s42nff9ar0xn7yb42m64lpvmqzq8wranqrkkixdnp7w3pmv1x
|
||||
source: hosted
|
||||
version: 3.0.2
|
||||
shelf_static:
|
||||
archive_url: https://pub.dartlang.org/packages/shelf_static/versions/1.1.2.tar.gz
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shelf_static
|
||||
sha256: a41d3f53c4adf0f57480578c1d61d90342cd617de7fc8077b1304643c2d85c1e
|
||||
url: https://pub.dev
|
||||
sha256: 1bcqynn2z2syrigmrclxgg8hjhd1x9742938i62cicbaga6vclaz
|
||||
source: hosted
|
||||
version: 1.1.2
|
||||
shelf_web_socket:
|
||||
archive_url: https://pub.dartlang.org/packages/shelf_web_socket/versions/1.0.4.tar.gz
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shelf_web_socket
|
||||
sha256: 9ca081be41c60190ebcb4766b2486a7d50261db7bd0f5d9615f2d653637a84c1
|
||||
url: https://pub.dev
|
||||
sha256: 110b5hrqwpnmq16shxxzjmcih5yfs5kh80dn8avfv0xj5iv7n94c
|
||||
source: hosted
|
||||
version: 1.0.4
|
||||
source_gen:
|
||||
archive_url: https://pub.dartlang.org/packages/source_gen/versions/1.3.2.tar.gz
|
||||
dependency: transitive
|
||||
description:
|
||||
name: source_gen
|
||||
sha256: 373f96cf5a8744bc9816c1ff41cf5391bbdbe3d7a96fe98c622b6738a8a7bd33
|
||||
url: https://pub.dev
|
||||
sha256: 1jql5zccv4vnbbvwcpyyvz8l27pg1rviqbp4vrks5313nf4b0kjg
|
||||
source: hosted
|
||||
version: 1.3.2
|
||||
source_helper:
|
||||
archive_url: https://pub.dartlang.org/packages/source_helper/versions/1.3.4.tar.gz
|
||||
dependency: transitive
|
||||
description:
|
||||
name: source_helper
|
||||
sha256: 6adebc0006c37dd63fe05bca0a929b99f06402fc95aa35bf36d67f5c06de01fd
|
||||
url: https://pub.dev
|
||||
sha256: 0mdd02vhcdcv9n58gzbx2q0bphwj0alz312ca1a8xpkf8jx3y8v4
|
||||
source: hosted
|
||||
version: 1.3.4
|
||||
source_map_stack_trace:
|
||||
archive_url: https://pub.dartlang.org/packages/source_map_stack_trace/versions/2.1.1.tar.gz
|
||||
dependency: transitive
|
||||
description:
|
||||
name: source_map_stack_trace
|
||||
sha256: 84cf769ad83aa6bb61e0aa5a18e53aea683395f196a6f39c4c881fb90ed4f7ae
|
||||
url: https://pub.dev
|
||||
sha256: 0b5d4c5n5qd3j8n10gp1khhr508wfl3819bhk6xnl34qxz8n032k
|
||||
source: hosted
|
||||
version: 2.1.1
|
||||
source_maps:
|
||||
archive_url: https://pub.dartlang.org/packages/source_maps/versions/0.10.12.tar.gz
|
||||
dependency: transitive
|
||||
description:
|
||||
name: source_maps
|
||||
sha256: 708b3f6b97248e5781f493b765c3337db11c5d2c81c3094f10904bfa8004c703
|
||||
url: https://pub.dev
|
||||
sha256: 004lcfka01agxjdw7zjhrffdkisvgx5s61b5gsl8qqk2jd1rswa7
|
||||
source: hosted
|
||||
version: 0.10.12
|
||||
source_span:
|
||||
archive_url: https://pub.dartlang.org/packages/source_span/versions/1.10.0.tar.gz
|
||||
dependency: transitive
|
||||
description:
|
||||
name: source_span
|
||||
sha256: 53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c
|
||||
url: https://pub.dev
|
||||
sha256: 1nybnf7l5chslp4fczhqnrgrhymy844lw7qrj6y08i626dshrd46
|
||||
source: hosted
|
||||
version: 1.10.0
|
||||
stack_trace:
|
||||
archive_url: https://pub.dartlang.org/packages/stack_trace/versions/1.11.1.tar.gz
|
||||
dependency: transitive
|
||||
description:
|
||||
name: stack_trace
|
||||
sha256: 73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b
|
||||
url: https://pub.dev
|
||||
sha256: 0xpk2cvmgdh46iwip9jsb54fqx13jnina8pk03akxkmsxvag5izb
|
||||
source: hosted
|
||||
version: 1.11.1
|
||||
stream_channel:
|
||||
archive_url: https://pub.dartlang.org/packages/stream_channel/versions/2.1.2.tar.gz
|
||||
dependency: transitive
|
||||
description:
|
||||
name: stream_channel
|
||||
sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7
|
||||
url: https://pub.dev
|
||||
sha256: 0nrlw6zcscgnn6818krkbgs9qiv3f7q8pa7ljw1bqkrsb7xabm8s
|
||||
source: hosted
|
||||
version: 2.1.2
|
||||
stream_transform:
|
||||
archive_url: https://pub.dartlang.org/packages/stream_transform/versions/2.1.0.tar.gz
|
||||
dependency: transitive
|
||||
description:
|
||||
name: stream_transform
|
||||
sha256: 14a00e794c7c11aa145a170587321aedce29769c08d7f58b1d141da75e3b1c6f
|
||||
url: https://pub.dev
|
||||
sha256: 0jq6767v9ds17i2nd6mdd9i0f7nvsgg3dz74d0v54x66axjgr0gp
|
||||
source: hosted
|
||||
version: 2.1.0
|
||||
string_scanner:
|
||||
archive_url: https://pub.dartlang.org/packages/string_scanner/versions/1.2.0.tar.gz
|
||||
dependency: transitive
|
||||
description:
|
||||
name: string_scanner
|
||||
sha256: 556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde
|
||||
url: https://pub.dev
|
||||
sha256: 0p1r0v2923avwfg03rk0pmc6f21m0zxpcx6i57xygd25k6hdfi00
|
||||
source: hosted
|
||||
version: 1.2.0
|
||||
synchronized:
|
||||
archive_url: https://pub.dartlang.org/packages/synchronized/versions/3.1.0.tar.gz
|
||||
dependency: direct main
|
||||
description:
|
||||
name: synchronized
|
||||
sha256: 5fcbd27688af6082f5abd611af56ee575342c30e87541d0245f7ff99faa02c60
|
||||
url: https://pub.dev
|
||||
sha256: 1fx1z1p5qkn4qnq24riw5s86vmq645ppg8f74iyv2fc9rvr301ar
|
||||
source: hosted
|
||||
version: 3.1.0
|
||||
term_glyph:
|
||||
archive_url: https://pub.dartlang.org/packages/term_glyph/versions/1.2.1.tar.gz
|
||||
dependency: transitive
|
||||
description:
|
||||
name: term_glyph
|
||||
sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84
|
||||
url: https://pub.dev
|
||||
sha256: 1x8nspxaccls0sxjamp703yp55yxdvhj6wg21lzwd296i9rwlxh9
|
||||
source: hosted
|
||||
version: 1.2.1
|
||||
test:
|
||||
archive_url: https://pub.dartlang.org/packages/test/versions/1.24.3.tar.gz
|
||||
dependency: direct dev
|
||||
description:
|
||||
name: test
|
||||
sha256: 13b41f318e2a5751c3169137103b60c584297353d4b1761b66029bae6411fe46
|
||||
url: https://pub.dev
|
||||
sha256: 002phlj2pg6nll5hv449izxbqk29zwmwc77d0jx2iimz18dgy2r5
|
||||
source: hosted
|
||||
version: 1.24.3
|
||||
test_api:
|
||||
archive_url: https://pub.dartlang.org/packages/test_api/versions/0.6.0.tar.gz
|
||||
dependency: transitive
|
||||
description:
|
||||
name: test_api
|
||||
sha256: 75760ffd7786fffdfb9597c35c5b27eaeec82be8edfb6d71d32651128ed7aab8
|
||||
url: https://pub.dev
|
||||
sha256: 0as1xcywjrd2zax3cm56qmnac12shf8c1ynnzzjwnggm23f61dxb
|
||||
source: hosted
|
||||
version: 0.6.0
|
||||
test_core:
|
||||
archive_url: https://pub.dartlang.org/packages/test_core/versions/0.5.3.tar.gz
|
||||
dependency: transitive
|
||||
description:
|
||||
name: test_core
|
||||
sha256: 99806e9e6d95c7b059b7a0fc08f07fc53fabe54a829497f0d9676299f1e8637e
|
||||
url: https://pub.dev
|
||||
sha256: 1cx2rmz1xzk5z5yh8fpbsrsz4mgjanrw4xvnp0qzdnm2d7vhaq0y
|
||||
source: hosted
|
||||
version: 0.5.3
|
||||
timing:
|
||||
archive_url: https://pub.dartlang.org/packages/timing/versions/1.0.1.tar.gz
|
||||
dependency: transitive
|
||||
description:
|
||||
name: timing
|
||||
sha256: 70a3b636575d4163c477e6de42f247a23b315ae20e86442bebe32d3cabf61c32
|
||||
url: https://pub.dev
|
||||
sha256: 15jvxsw7v0gwbdlykma60l1qlhlzb3brh6m0sg2bgbfir4l5s9gw
|
||||
source: hosted
|
||||
version: 1.0.1
|
||||
typed_data:
|
||||
archive_url: https://pub.dartlang.org/packages/typed_data/versions/1.3.2.tar.gz
|
||||
dependency: transitive
|
||||
description:
|
||||
name: typed_data
|
||||
sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c
|
||||
url: https://pub.dev
|
||||
sha256: 0q6ggc52vfpr8kqaq69h757wy942hvgshhnsr2pjdinb4zk2sxl1
|
||||
source: hosted
|
||||
version: 1.3.2
|
||||
unorm_dart:
|
||||
archive_url: https://pub.dartlang.org/packages/unorm_dart/versions/0.2.0.tar.gz
|
||||
dependency: transitive
|
||||
description:
|
||||
name: unorm_dart
|
||||
sha256: 5b35bff83fce4d76467641438f9e867dc9bcfdb8c1694854f230579d68cd8f4b
|
||||
url: https://pub.dev
|
||||
sha256: 05kyk2764yz14pzgx00i7h5b1lzh8kjqnxspfzyf8z920bcgbz0v
|
||||
source: hosted
|
||||
version: 0.2.0
|
||||
uuid:
|
||||
archive_url: https://pub.dartlang.org/packages/uuid/versions/3.0.7.tar.gz
|
||||
dependency: direct main
|
||||
description:
|
||||
name: uuid
|
||||
sha256: 648e103079f7c64a36dc7d39369cabb358d377078a051d6ae2ad3aa539519313
|
||||
url: https://pub.dev
|
||||
sha256: 1nh1hxfr6bhyadqqcxrpwrphmm75f1iq4rzfjdwa2486xwlh7vx3
|
||||
source: hosted
|
||||
version: 3.0.7
|
||||
very_good_analysis:
|
||||
archive_url: https://pub.dartlang.org/packages/very_good_analysis/versions/3.1.0.tar.gz
|
||||
dependency: direct dev
|
||||
description:
|
||||
name: very_good_analysis
|
||||
sha256: 4815adc7ded57657038d2bb2a7f332c50e3c8152f7d3c6acf8f6b7c0cc81e5e2
|
||||
url: https://pub.dev
|
||||
sha256: 1p2dh8aahbqyyqfzbsxswafgxnmxgisjq2xfp008skyh7imk6sz4
|
||||
source: hosted
|
||||
version: 3.1.0
|
||||
vm_service:
|
||||
archive_url: https://pub.dartlang.org/packages/vm_service/versions/11.10.0.tar.gz
|
||||
dependency: transitive
|
||||
description:
|
||||
name: vm_service
|
||||
sha256: c538be99af830f478718b51630ec1b6bee5e74e52c8a802d328d9e71d35d2583
|
||||
url: https://pub.dev
|
||||
sha256: 15ail7rbaq9ksg73cc0mw2k5imbiidl95yfd4v49k81gp5xmj92w
|
||||
source: hosted
|
||||
version: 11.10.0
|
||||
watcher:
|
||||
archive_url: https://pub.dartlang.org/packages/watcher/versions/1.0.2.tar.gz
|
||||
dependency: transitive
|
||||
description:
|
||||
name: watcher
|
||||
sha256: 6a7f46926b01ce81bfc339da6a7f20afbe7733eff9846f6d6a5466aa4c6667c0
|
||||
url: https://pub.dev
|
||||
sha256: 1sk7gvwa7s0h4l652qrgbh7l8wyqc6nr6lki8m4rj55720p0fnyg
|
||||
source: hosted
|
||||
version: 1.0.2
|
||||
web_socket_channel:
|
||||
archive_url: https://pub.dartlang.org/packages/web_socket_channel/versions/2.4.0.tar.gz
|
||||
dependency: transitive
|
||||
description:
|
||||
name: web_socket_channel
|
||||
sha256: d88238e5eac9a42bb43ca4e721edba3c08c6354d4a53063afaa568516217621b
|
||||
url: https://pub.dev
|
||||
sha256: 0f9441c4zifb5qadpjg319dcilimpkdhfacnkl543802bf8qjn4w
|
||||
source: hosted
|
||||
version: 2.4.0
|
||||
webkit_inspection_protocol:
|
||||
archive_url: https://pub.dartlang.org/packages/webkit_inspection_protocol/versions/1.2.0.tar.gz
|
||||
dependency: transitive
|
||||
description:
|
||||
name: webkit_inspection_protocol
|
||||
sha256: 67d3a8b6c79e1987d19d848b0892e582dbb0c66c57cc1fef58a177dd2aa2823d
|
||||
url: https://pub.dev
|
||||
sha256: 0z400dzw7gf68a3wm95xi2mf461iigkyq6x69xgi7qs3fvpmn3hx
|
||||
source: hosted
|
||||
version: 1.2.0
|
||||
xml:
|
||||
archive_url: https://pub.dartlang.org/packages/xml/versions/6.3.0.tar.gz
|
||||
dependency: direct main
|
||||
description:
|
||||
name: xml
|
||||
sha256: 5bc72e1e45e941d825fd7468b9b4cc3b9327942649aeb6fc5cdbf135f0a86e84
|
||||
url: https://pub.dev
|
||||
sha256: 120azx71gazvrrn07vd83vrffzrhsqnmf9rdjxl73rra9py8ixiy
|
||||
source: hosted
|
||||
version: 6.3.0
|
||||
yaml:
|
||||
archive_url: https://pub.dartlang.org/packages/yaml/versions/3.1.2.tar.gz
|
||||
dependency: transitive
|
||||
description:
|
||||
name: yaml
|
||||
sha256: 75769501ea3489fca56601ff33454fe45507ea3bfb014161abc3b43ae25989d5
|
||||
url: https://pub.dev
|
||||
sha256: 0awh9dynbhrlq8zgszaiyxbyyn9b6wyps1zww4z2lx62nbma0pda
|
||||
source: hosted
|
||||
version: 3.1.2
|
816
nix/pubcache.moxxmpp.nix
Normal file
@ -0,0 +1,816 @@
|
||||
# GENERATED BY LOCK2NIX.py
|
||||
# DO NOT EDIT BY HAND
|
||||
{fetchzip, runCommand} : rec {
|
||||
_fe_analyzer_shared = fetchzip {
|
||||
sha256 = "15fh9ka41dw4qsynv07msq4i243fibprcmafdygw5x88f7m55fq3";
|
||||
url = "https://pub.dartlang.org/packages/_fe_analyzer_shared/versions/61.0.0.tar.gz";
|
||||
stripRoot = false;
|
||||
extension = "tar.gz";
|
||||
};
|
||||
|
||||
analyzer = fetchzip {
|
||||
sha256 = "0w604zngxwfx0xqxvhbxrhdh04wgm6ad6a1lbwnyvmk57amv44np";
|
||||
url = "https://pub.dartlang.org/packages/analyzer/versions/5.13.0.tar.gz";
|
||||
stripRoot = false;
|
||||
extension = "tar.gz";
|
||||
};
|
||||
|
||||
args = fetchzip {
|
||||
sha256 = "01ps253280c6dbx0vncw4wga4l2qp1zx779qjj2x06xzb3744zbz";
|
||||
url = "https://pub.dartlang.org/packages/args/versions/2.4.2.tar.gz";
|
||||
stripRoot = false;
|
||||
extension = "tar.gz";
|
||||
};
|
||||
|
||||
async = fetchzip {
|
||||
sha256 = "0hfgvjajp5c2mw68186hgrk9v5zjhhi149hlhl0fap274p2v1g3q";
|
||||
url = "https://pub.dartlang.org/packages/async/versions/2.11.0.tar.gz";
|
||||
stripRoot = false;
|
||||
extension = "tar.gz";
|
||||
};
|
||||
|
||||
boolean_selector = fetchzip {
|
||||
sha256 = "0hxq8072hb89q9s91xlz9fvrjxfy7hw6jkdwkph5dp77df841kmj";
|
||||
url = "https://pub.dartlang.org/packages/boolean_selector/versions/2.1.1.tar.gz";
|
||||
stripRoot = false;
|
||||
extension = "tar.gz";
|
||||
};
|
||||
|
||||
build = fetchzip {
|
||||
sha256 = "1x6nkii6kqy6y7ck0151yfhc9lp2nvbhznnhdi2mxr8afk6jxigd";
|
||||
url = "https://pub.dartlang.org/packages/build/versions/2.3.1.tar.gz";
|
||||
stripRoot = false;
|
||||
extension = "tar.gz";
|
||||
};
|
||||
|
||||
build_config = fetchzip {
|
||||
sha256 = "092rrbhbdy9fk50jqb1fwj1sfk415fi43irvsd0hk5w90gn8vazj";
|
||||
url = "https://pub.dartlang.org/packages/build_config/versions/1.1.1.tar.gz";
|
||||
stripRoot = false;
|
||||
extension = "tar.gz";
|
||||
};
|
||||
|
||||
build_daemon = fetchzip {
|
||||
sha256 = "1wn7bq846vgdj62bkh9h25l95xdsndv0jdyw52nyr0591l3bpg3h";
|
||||
url = "https://pub.dartlang.org/packages/build_daemon/versions/3.1.1.tar.gz";
|
||||
stripRoot = false;
|
||||
extension = "tar.gz";
|
||||
};
|
||||
|
||||
build_resolvers = fetchzip {
|
||||
sha256 = "00h9abhrfmnl0xxziyf6p68sxnbv2ww1c4dhgpnz00mzbmamnq5c";
|
||||
url = "https://pub.dartlang.org/packages/build_resolvers/versions/2.3.1.tar.gz";
|
||||
stripRoot = false;
|
||||
extension = "tar.gz";
|
||||
};
|
||||
|
||||
build_runner = fetchzip {
|
||||
sha256 = "0b5ha1l6k0gn2swqgqvfy2vl58klf81sxrjnmk0p7rj1wzbqjm7l";
|
||||
url = "https://pub.dartlang.org/packages/build_runner/versions/2.3.3.tar.gz";
|
||||
stripRoot = false;
|
||||
extension = "tar.gz";
|
||||
};
|
||||
|
||||
build_runner_core = fetchzip {
|
||||
sha256 = "07r1kfy6ylm4i4xrb24ns8l26h4h1lgcskmnf8wvq2rd5d5hq790";
|
||||
url = "https://pub.dartlang.org/packages/build_runner_core/versions/7.2.7%2B1.tar.gz";
|
||||
stripRoot = false;
|
||||
extension = "tar.gz";
|
||||
};
|
||||
|
||||
built_collection = fetchzip {
|
||||
sha256 = "0bqjahxr42q84w91nhv3n4cr580l3s3ffx3vgzyyypgqnrck0hv3";
|
||||
url = "https://pub.dartlang.org/packages/built_collection/versions/5.1.1.tar.gz";
|
||||
stripRoot = false;
|
||||
extension = "tar.gz";
|
||||
};
|
||||
|
||||
built_value = fetchzip {
|
||||
sha256 = "1y84imf9xqqy3gnd5zz9bcln6mycy7qx35r70b0izm31ismlbzkv";
|
||||
url = "https://pub.dartlang.org/packages/built_value/versions/8.6.2.tar.gz";
|
||||
stripRoot = false;
|
||||
extension = "tar.gz";
|
||||
};
|
||||
|
||||
checked_yaml = fetchzip {
|
||||
sha256 = "1sn01yrmj0pkijn08g3v45c3zmyvdygk9svigkkzybgicdwlkpqs";
|
||||
url = "https://pub.dartlang.org/packages/checked_yaml/versions/2.0.3.tar.gz";
|
||||
stripRoot = false;
|
||||
extension = "tar.gz";
|
||||
};
|
||||
|
||||
code_builder = fetchzip {
|
||||
sha256 = "1shgl7mxiyv0hhw326yqj2b9jxi1h74qxmsnxf1d1xc6yz766p9a";
|
||||
url = "https://pub.dartlang.org/packages/code_builder/versions/4.6.0.tar.gz";
|
||||
stripRoot = false;
|
||||
extension = "tar.gz";
|
||||
};
|
||||
|
||||
collection = fetchzip {
|
||||
sha256 = "1mr8j0078c4z9hhckiq8m735rggsazwfprm0w9gisil51vh7j2mk";
|
||||
url = "https://pub.dartlang.org/packages/collection/versions/1.18.0.tar.gz";
|
||||
stripRoot = false;
|
||||
extension = "tar.gz";
|
||||
};
|
||||
|
||||
convert = fetchzip {
|
||||
sha256 = "0adsigjk3l1c31i6k91p28dqyjlgwiqrs4lky5djrm2scf8k6cri";
|
||||
url = "https://pub.dartlang.org/packages/convert/versions/3.1.1.tar.gz";
|
||||
stripRoot = false;
|
||||
extension = "tar.gz";
|
||||
};
|
||||
|
||||
coverage = fetchzip {
|
||||
sha256 = "1yy9bgkax5b6kk7qa07p452v82fyj4rl1j03fn366ywyvhfrh6lp";
|
||||
url = "https://pub.dartlang.org/packages/coverage/versions/1.6.3.tar.gz";
|
||||
stripRoot = false;
|
||||
extension = "tar.gz";
|
||||
};
|
||||
|
||||
crypto = fetchzip {
|
||||
sha256 = "100ai8qa4p3dyvvd60c4xa9p0gm06yh0d68xgcfm3giraad8xmqj";
|
||||
url = "https://pub.dartlang.org/packages/crypto/versions/3.0.3.tar.gz";
|
||||
stripRoot = false;
|
||||
extension = "tar.gz";
|
||||
};
|
||||
|
||||
cryptography = fetchzip {
|
||||
sha256 = "1yxn9slqq93ri81fbr2nbsinz0mpk9wk39ny076ja8q31d4i8v3f";
|
||||
url = "https://pub.dartlang.org/packages/cryptography/versions/2.5.0.tar.gz";
|
||||
stripRoot = false;
|
||||
extension = "tar.gz";
|
||||
};
|
||||
|
||||
dart_style = fetchzip {
|
||||
sha256 = "0cjhrb1hs8iw9smmfd0fgnxq3nm0w8sz17l6q6svyz6kif19wk9k";
|
||||
url = "https://pub.dartlang.org/packages/dart_style/versions/2.3.2.tar.gz";
|
||||
stripRoot = false;
|
||||
extension = "tar.gz";
|
||||
};
|
||||
|
||||
file = fetchzip {
|
||||
sha256 = "0ajcfblf8d4dicp1sgzkbrhd0b0v0d8wl70jsnf5drjck3p3ppk7";
|
||||
url = "https://pub.dartlang.org/packages/file/versions/6.1.4.tar.gz";
|
||||
stripRoot = false;
|
||||
extension = "tar.gz";
|
||||
};
|
||||
|
||||
fixnum = fetchzip {
|
||||
sha256 = "0nqrzj41ys8dpxf1x70r0kfj1avj0f2j2b7498k8kvc0i9c5asz7";
|
||||
url = "https://pub.dartlang.org/packages/fixnum/versions/1.1.0.tar.gz";
|
||||
stripRoot = false;
|
||||
extension = "tar.gz";
|
||||
};
|
||||
|
||||
frontend_server_client = fetchzip {
|
||||
sha256 = "096v7ycix5hgnk750s1qgykyghl2mymhdkg39jrlk3kbj6xygq5b";
|
||||
url = "https://pub.dartlang.org/packages/frontend_server_client/versions/3.2.0.tar.gz";
|
||||
stripRoot = false;
|
||||
extension = "tar.gz";
|
||||
};
|
||||
|
||||
glob = fetchzip {
|
||||
sha256 = "0ffab3azx8zkma36mk6wnig8bn8g5g0vjrq2gl21y77rxgw9iqxj";
|
||||
url = "https://pub.dartlang.org/packages/glob/versions/2.1.2.tar.gz";
|
||||
stripRoot = false;
|
||||
extension = "tar.gz";
|
||||
};
|
||||
|
||||
graphs = fetchzip {
|
||||
sha256 = "0fda0j8y6sq1rc9zpzglrzysl5h49y2ji1wq2lq0wx2c609dxm7f";
|
||||
url = "https://pub.dartlang.org/packages/graphs/versions/2.3.1.tar.gz";
|
||||
stripRoot = false;
|
||||
extension = "tar.gz";
|
||||
};
|
||||
|
||||
hex = fetchzip {
|
||||
sha256 = "19w3f90mdiy06a6kf8hlwc4jn4cxixkj106kc3g3bis27ar7smkh";
|
||||
url = "https://pub.dartlang.org/packages/hex/versions/0.2.0.tar.gz";
|
||||
stripRoot = false;
|
||||
extension = "tar.gz";
|
||||
};
|
||||
|
||||
http_multi_server = fetchzip {
|
||||
sha256 = "1zdcm04z85jahb2hs7qs85rh974kw49hffhy9cn1gfda3077dvql";
|
||||
url = "https://pub.dartlang.org/packages/http_multi_server/versions/3.2.1.tar.gz";
|
||||
stripRoot = false;
|
||||
extension = "tar.gz";
|
||||
};
|
||||
|
||||
http_parser = fetchzip {
|
||||
sha256 = "027c4sjkhkkx3sk1aqs6s4djb87syi9h521qpm1bf21bq3gga5jd";
|
||||
url = "https://pub.dartlang.org/packages/http_parser/versions/4.0.2.tar.gz";
|
||||
stripRoot = false;
|
||||
extension = "tar.gz";
|
||||
};
|
||||
|
||||
io = fetchzip {
|
||||
sha256 = "101kd0rw26vglmr1m5p130kbrp3k7dk4p5nr77wsbwgg53w8c0d4";
|
||||
url = "https://pub.dartlang.org/packages/io/versions/1.0.4.tar.gz";
|
||||
stripRoot = false;
|
||||
extension = "tar.gz";
|
||||
};
|
||||
|
||||
js = fetchzip {
|
||||
sha256 = "124a9yqrjdw3p4nnirab9hm9ziwraldlw4q5cb3sr0dcrli74qpw";
|
||||
url = "https://pub.dartlang.org/packages/js/versions/0.6.7.tar.gz";
|
||||
stripRoot = false;
|
||||
extension = "tar.gz";
|
||||
};
|
||||
|
||||
json_annotation = fetchzip {
|
||||
sha256 = "1jjw7p8qyqajgdq4jqvxipq5w0qrq9dpi1qmia70pk995akryh6m";
|
||||
url = "https://pub.dartlang.org/packages/json_annotation/versions/4.8.1.tar.gz";
|
||||
stripRoot = false;
|
||||
extension = "tar.gz";
|
||||
};
|
||||
|
||||
json_serializable = fetchzip {
|
||||
sha256 = "1pmidql9x6s2pbhdx9x20pwqwvwpfkvrz0h0cm1f8pqis76c90hb";
|
||||
url = "https://pub.dartlang.org/packages/json_serializable/versions/6.6.2.tar.gz";
|
||||
stripRoot = false;
|
||||
extension = "tar.gz";
|
||||
};
|
||||
|
||||
logging = fetchzip {
|
||||
sha256 = "124hfjs66r30p92ndfmy5fymgy66yk9in97h8sq6fi7r78pqyc7g";
|
||||
url = "https://pub.dartlang.org/packages/logging/versions/1.2.0.tar.gz";
|
||||
stripRoot = false;
|
||||
extension = "tar.gz";
|
||||
};
|
||||
|
||||
matcher = fetchzip {
|
||||
sha256 = "0inznqkrxqnq09lcbwvda3xd07qfm1k3aa6dv1wy39gvci8hybss";
|
||||
url = "https://pub.dartlang.org/packages/matcher/versions/0.12.16.tar.gz";
|
||||
stripRoot = false;
|
||||
extension = "tar.gz";
|
||||
};
|
||||
|
||||
meta = fetchzip {
|
||||
sha256 = "1l3zaz6q2s9mnm7s674xshsfqspy79p5kdbbnc99rf2l76avv4h3";
|
||||
url = "https://pub.dartlang.org/packages/meta/versions/1.9.1.tar.gz";
|
||||
stripRoot = false;
|
||||
extension = "tar.gz";
|
||||
};
|
||||
|
||||
mime = fetchzip {
|
||||
sha256 = "1dha9z64bsz8xhi0p62vmlyikr8xwbdlrw90hxghmm3rdgd9h25w";
|
||||
url = "https://pub.dartlang.org/packages/mime/versions/1.0.4.tar.gz";
|
||||
stripRoot = false;
|
||||
extension = "tar.gz";
|
||||
};
|
||||
|
||||
moxlib = fetchzip {
|
||||
sha256 = "1qaacmcqhq33grn2nq8sn23ki62dcmw0fqy589xm1zv6w0pzfmsk";
|
||||
url = "https://git.polynom.me/api/packages/moxxy/pub/api/packages/moxlib/files/0.2.0.tar.gz";
|
||||
stripRoot = false;
|
||||
extension = "tar.gz";
|
||||
};
|
||||
|
||||
node_preamble = fetchzip {
|
||||
sha256 = "12ajg76r9aqmqkavvlxbnb3sszg1szcq3f30badkd0xc25mnhyh8";
|
||||
url = "https://pub.dartlang.org/packages/node_preamble/versions/2.0.2.tar.gz";
|
||||
stripRoot = false;
|
||||
extension = "tar.gz";
|
||||
};
|
||||
|
||||
omemo_dart = fetchzip {
|
||||
sha256 = "0fhf89ic5mdyld25l6rfb37a1fk1f0f2b4d72xi4r7pvr0ddjhz8";
|
||||
url = "https://git.polynom.me/api/packages/PapaTutuWawa/pub/api/packages/omemo_dart/files/0.5.1.tar.gz";
|
||||
stripRoot = false;
|
||||
extension = "tar.gz";
|
||||
};
|
||||
|
||||
package_config = fetchzip {
|
||||
sha256 = "1d4l0i4cby344zj45f5shrg2pkw1i1jn03kx0qqh0l7gh1ha7bpc";
|
||||
url = "https://pub.dartlang.org/packages/package_config/versions/2.1.0.tar.gz";
|
||||
stripRoot = false;
|
||||
extension = "tar.gz";
|
||||
};
|
||||
|
||||
path = fetchzip {
|
||||
sha256 = "1mjdhq2fsz6i9krhp2mnaks2bcw34sa4p7mg0v6njk8dgx2754iv";
|
||||
url = "https://pub.dartlang.org/packages/path/versions/1.8.3.tar.gz";
|
||||
stripRoot = false;
|
||||
extension = "tar.gz";
|
||||
};
|
||||
|
||||
pedantic = fetchzip {
|
||||
sha256 = "10ch0h3hi6cfwiz2ihfkh6m36m75c0m7fd0wwqaqggffsj2dn8ad";
|
||||
url = "https://pub.dartlang.org/packages/pedantic/versions/1.11.1.tar.gz";
|
||||
stripRoot = false;
|
||||
extension = "tar.gz";
|
||||
};
|
||||
|
||||
petitparser = fetchzip {
|
||||
sha256 = "19zqrpb1z77aw1k2s8rsxdfxczzv9934g2rdfj2jyiv3pqgdq8gh";
|
||||
url = "https://pub.dartlang.org/packages/petitparser/versions/5.4.0.tar.gz";
|
||||
stripRoot = false;
|
||||
extension = "tar.gz";
|
||||
};
|
||||
|
||||
pinenacl = fetchzip {
|
||||
sha256 = "0didjgva658z90hbcmhd0y8w1b8v86dp6gabfhylnw1aixl47cxg";
|
||||
url = "https://pub.dartlang.org/packages/pinenacl/versions/0.5.1.tar.gz";
|
||||
stripRoot = false;
|
||||
extension = "tar.gz";
|
||||
};
|
||||
|
||||
pool = fetchzip {
|
||||
sha256 = "0wmzs46hjszv3ayhr1p5l7xza7q9rkg2q9z4swmhdqmhlz3c50x4";
|
||||
url = "https://pub.dartlang.org/packages/pool/versions/1.5.1.tar.gz";
|
||||
stripRoot = false;
|
||||
extension = "tar.gz";
|
||||
};
|
||||
|
||||
protobuf = fetchzip {
|
||||
sha256 = "1jriyisf8bnvq5ygjk93mn2yzdlnii7xrhy6aabz54xr3y4dcy9x";
|
||||
url = "https://pub.dartlang.org/packages/protobuf/versions/2.1.0.tar.gz";
|
||||
stripRoot = false;
|
||||
extension = "tar.gz";
|
||||
};
|
||||
|
||||
protoc_plugin = fetchzip {
|
||||
sha256 = "0hjjd1xkv4s4g1d5n2aza0kdwlbfl2aivq99230m3yml7irn00jk";
|
||||
url = "https://pub.dartlang.org/packages/protoc_plugin/versions/20.0.1.tar.gz";
|
||||
stripRoot = false;
|
||||
extension = "tar.gz";
|
||||
};
|
||||
|
||||
pub_semver = fetchzip {
|
||||
sha256 = "0wpcfz1crxipbjm18m71pl4vl2ra8vw1n93ff8snr54mmlyfb9z1";
|
||||
url = "https://pub.dartlang.org/packages/pub_semver/versions/2.1.4.tar.gz";
|
||||
stripRoot = false;
|
||||
extension = "tar.gz";
|
||||
};
|
||||
|
||||
pubspec_parse = fetchzip {
|
||||
sha256 = "0dj8sf1w61g7vh1ly3sl690z0nwllzjzbapxswmgsglr0ndcyrs1";
|
||||
url = "https://pub.dartlang.org/packages/pubspec_parse/versions/1.2.3.tar.gz";
|
||||
stripRoot = false;
|
||||
extension = "tar.gz";
|
||||
};
|
||||
|
||||
random_string = fetchzip {
|
||||
sha256 = "11cjiv75sgldvk3x7w6j77lgi08r6737wm94m3ylabylsr6zdyff";
|
||||
url = "https://pub.dartlang.org/packages/random_string/versions/2.3.1.tar.gz";
|
||||
stripRoot = false;
|
||||
extension = "tar.gz";
|
||||
};
|
||||
|
||||
saslprep = fetchzip {
|
||||
sha256 = "04lss0xvm6p801p8306jdxg7k0b28kr6n65dz2f57dkca237kcw7";
|
||||
url = "https://pub.dartlang.org/packages/saslprep/versions/1.0.2.tar.gz";
|
||||
stripRoot = false;
|
||||
extension = "tar.gz";
|
||||
};
|
||||
|
||||
shelf = fetchzip {
|
||||
sha256 = "10yk98nadrgj5d3r3241kdaywjjs1j10mg8gacv80kg1mhcfdrxp";
|
||||
url = "https://pub.dartlang.org/packages/shelf/versions/1.4.1.tar.gz";
|
||||
stripRoot = false;
|
||||
extension = "tar.gz";
|
||||
};
|
||||
|
||||
shelf_packages_handler = fetchzip {
|
||||
sha256 = "1h8s42nff9ar0xn7yb42m64lpvmqzq8wranqrkkixdnp7w3pmv1x";
|
||||
url = "https://pub.dartlang.org/packages/shelf_packages_handler/versions/3.0.2.tar.gz";
|
||||
stripRoot = false;
|
||||
extension = "tar.gz";
|
||||
};
|
||||
|
||||
shelf_static = fetchzip {
|
||||
sha256 = "1bcqynn2z2syrigmrclxgg8hjhd1x9742938i62cicbaga6vclaz";
|
||||
url = "https://pub.dartlang.org/packages/shelf_static/versions/1.1.2.tar.gz";
|
||||
stripRoot = false;
|
||||
extension = "tar.gz";
|
||||
};
|
||||
|
||||
shelf_web_socket = fetchzip {
|
||||
sha256 = "110b5hrqwpnmq16shxxzjmcih5yfs5kh80dn8avfv0xj5iv7n94c";
|
||||
url = "https://pub.dartlang.org/packages/shelf_web_socket/versions/1.0.4.tar.gz";
|
||||
stripRoot = false;
|
||||
extension = "tar.gz";
|
||||
};
|
||||
|
||||
source_gen = fetchzip {
|
||||
sha256 = "1jql5zccv4vnbbvwcpyyvz8l27pg1rviqbp4vrks5313nf4b0kjg";
|
||||
url = "https://pub.dartlang.org/packages/source_gen/versions/1.3.2.tar.gz";
|
||||
stripRoot = false;
|
||||
extension = "tar.gz";
|
||||
};
|
||||
|
||||
source_helper = fetchzip {
|
||||
sha256 = "0mdd02vhcdcv9n58gzbx2q0bphwj0alz312ca1a8xpkf8jx3y8v4";
|
||||
url = "https://pub.dartlang.org/packages/source_helper/versions/1.3.4.tar.gz";
|
||||
stripRoot = false;
|
||||
extension = "tar.gz";
|
||||
};
|
||||
|
||||
source_map_stack_trace = fetchzip {
|
||||
sha256 = "0b5d4c5n5qd3j8n10gp1khhr508wfl3819bhk6xnl34qxz8n032k";
|
||||
url = "https://pub.dartlang.org/packages/source_map_stack_trace/versions/2.1.1.tar.gz";
|
||||
stripRoot = false;
|
||||
extension = "tar.gz";
|
||||
};
|
||||
|
||||
source_maps = fetchzip {
|
||||
sha256 = "004lcfka01agxjdw7zjhrffdkisvgx5s61b5gsl8qqk2jd1rswa7";
|
||||
url = "https://pub.dartlang.org/packages/source_maps/versions/0.10.12.tar.gz";
|
||||
stripRoot = false;
|
||||
extension = "tar.gz";
|
||||
};
|
||||
|
||||
source_span = fetchzip {
|
||||
sha256 = "1nybnf7l5chslp4fczhqnrgrhymy844lw7qrj6y08i626dshrd46";
|
||||
url = "https://pub.dartlang.org/packages/source_span/versions/1.10.0.tar.gz";
|
||||
stripRoot = false;
|
||||
extension = "tar.gz";
|
||||
};
|
||||
|
||||
stack_trace = fetchzip {
|
||||
sha256 = "0xpk2cvmgdh46iwip9jsb54fqx13jnina8pk03akxkmsxvag5izb";
|
||||
url = "https://pub.dartlang.org/packages/stack_trace/versions/1.11.1.tar.gz";
|
||||
stripRoot = false;
|
||||
extension = "tar.gz";
|
||||
};
|
||||
|
||||
stream_channel = fetchzip {
|
||||
sha256 = "0nrlw6zcscgnn6818krkbgs9qiv3f7q8pa7ljw1bqkrsb7xabm8s";
|
||||
url = "https://pub.dartlang.org/packages/stream_channel/versions/2.1.2.tar.gz";
|
||||
stripRoot = false;
|
||||
extension = "tar.gz";
|
||||
};
|
||||
|
||||
stream_transform = fetchzip {
|
||||
sha256 = "0jq6767v9ds17i2nd6mdd9i0f7nvsgg3dz74d0v54x66axjgr0gp";
|
||||
url = "https://pub.dartlang.org/packages/stream_transform/versions/2.1.0.tar.gz";
|
||||
stripRoot = false;
|
||||
extension = "tar.gz";
|
||||
};
|
||||
|
||||
string_scanner = fetchzip {
|
||||
sha256 = "0p1r0v2923avwfg03rk0pmc6f21m0zxpcx6i57xygd25k6hdfi00";
|
||||
url = "https://pub.dartlang.org/packages/string_scanner/versions/1.2.0.tar.gz";
|
||||
stripRoot = false;
|
||||
extension = "tar.gz";
|
||||
};
|
||||
|
||||
synchronized = fetchzip {
|
||||
sha256 = "1fx1z1p5qkn4qnq24riw5s86vmq645ppg8f74iyv2fc9rvr301ar";
|
||||
url = "https://pub.dartlang.org/packages/synchronized/versions/3.1.0.tar.gz";
|
||||
stripRoot = false;
|
||||
extension = "tar.gz";
|
||||
};
|
||||
|
||||
term_glyph = fetchzip {
|
||||
sha256 = "1x8nspxaccls0sxjamp703yp55yxdvhj6wg21lzwd296i9rwlxh9";
|
||||
url = "https://pub.dartlang.org/packages/term_glyph/versions/1.2.1.tar.gz";
|
||||
stripRoot = false;
|
||||
extension = "tar.gz";
|
||||
};
|
||||
|
||||
test = fetchzip {
|
||||
sha256 = "002phlj2pg6nll5hv449izxbqk29zwmwc77d0jx2iimz18dgy2r5";
|
||||
url = "https://pub.dartlang.org/packages/test/versions/1.24.3.tar.gz";
|
||||
stripRoot = false;
|
||||
extension = "tar.gz";
|
||||
};
|
||||
|
||||
test_api = fetchzip {
|
||||
sha256 = "0as1xcywjrd2zax3cm56qmnac12shf8c1ynnzzjwnggm23f61dxb";
|
||||
url = "https://pub.dartlang.org/packages/test_api/versions/0.6.0.tar.gz";
|
||||
stripRoot = false;
|
||||
extension = "tar.gz";
|
||||
};
|
||||
|
||||
test_core = fetchzip {
|
||||
sha256 = "1cx2rmz1xzk5z5yh8fpbsrsz4mgjanrw4xvnp0qzdnm2d7vhaq0y";
|
||||
url = "https://pub.dartlang.org/packages/test_core/versions/0.5.3.tar.gz";
|
||||
stripRoot = false;
|
||||
extension = "tar.gz";
|
||||
};
|
||||
|
||||
timing = fetchzip {
|
||||
sha256 = "15jvxsw7v0gwbdlykma60l1qlhlzb3brh6m0sg2bgbfir4l5s9gw";
|
||||
url = "https://pub.dartlang.org/packages/timing/versions/1.0.1.tar.gz";
|
||||
stripRoot = false;
|
||||
extension = "tar.gz";
|
||||
};
|
||||
|
||||
typed_data = fetchzip {
|
||||
sha256 = "0q6ggc52vfpr8kqaq69h757wy942hvgshhnsr2pjdinb4zk2sxl1";
|
||||
url = "https://pub.dartlang.org/packages/typed_data/versions/1.3.2.tar.gz";
|
||||
stripRoot = false;
|
||||
extension = "tar.gz";
|
||||
};
|
||||
|
||||
unorm_dart = fetchzip {
|
||||
sha256 = "05kyk2764yz14pzgx00i7h5b1lzh8kjqnxspfzyf8z920bcgbz0v";
|
||||
url = "https://pub.dartlang.org/packages/unorm_dart/versions/0.2.0.tar.gz";
|
||||
stripRoot = false;
|
||||
extension = "tar.gz";
|
||||
};
|
||||
|
||||
uuid = fetchzip {
|
||||
sha256 = "1nh1hxfr6bhyadqqcxrpwrphmm75f1iq4rzfjdwa2486xwlh7vx3";
|
||||
url = "https://pub.dartlang.org/packages/uuid/versions/3.0.7.tar.gz";
|
||||
stripRoot = false;
|
||||
extension = "tar.gz";
|
||||
};
|
||||
|
||||
very_good_analysis = fetchzip {
|
||||
sha256 = "1p2dh8aahbqyyqfzbsxswafgxnmxgisjq2xfp008skyh7imk6sz4";
|
||||
url = "https://pub.dartlang.org/packages/very_good_analysis/versions/3.1.0.tar.gz";
|
||||
stripRoot = false;
|
||||
extension = "tar.gz";
|
||||
};
|
||||
|
||||
vm_service = fetchzip {
|
||||
sha256 = "15ail7rbaq9ksg73cc0mw2k5imbiidl95yfd4v49k81gp5xmj92w";
|
||||
url = "https://pub.dartlang.org/packages/vm_service/versions/11.10.0.tar.gz";
|
||||
stripRoot = false;
|
||||
extension = "tar.gz";
|
||||
};
|
||||
|
||||
watcher = fetchzip {
|
||||
sha256 = "1sk7gvwa7s0h4l652qrgbh7l8wyqc6nr6lki8m4rj55720p0fnyg";
|
||||
url = "https://pub.dartlang.org/packages/watcher/versions/1.0.2.tar.gz";
|
||||
stripRoot = false;
|
||||
extension = "tar.gz";
|
||||
};
|
||||
|
||||
web_socket_channel = fetchzip {
|
||||
sha256 = "0f9441c4zifb5qadpjg319dcilimpkdhfacnkl543802bf8qjn4w";
|
||||
url = "https://pub.dartlang.org/packages/web_socket_channel/versions/2.4.0.tar.gz";
|
||||
stripRoot = false;
|
||||
extension = "tar.gz";
|
||||
};
|
||||
|
||||
webkit_inspection_protocol = fetchzip {
|
||||
sha256 = "0z400dzw7gf68a3wm95xi2mf461iigkyq6x69xgi7qs3fvpmn3hx";
|
||||
url = "https://pub.dartlang.org/packages/webkit_inspection_protocol/versions/1.2.0.tar.gz";
|
||||
stripRoot = false;
|
||||
extension = "tar.gz";
|
||||
};
|
||||
|
||||
xml = fetchzip {
|
||||
sha256 = "120azx71gazvrrn07vd83vrffzrhsqnmf9rdjxl73rra9py8ixiy";
|
||||
url = "https://pub.dartlang.org/packages/xml/versions/6.3.0.tar.gz";
|
||||
stripRoot = false;
|
||||
extension = "tar.gz";
|
||||
};
|
||||
|
||||
yaml = fetchzip {
|
||||
sha256 = "0awh9dynbhrlq8zgszaiyxbyyn9b6wyps1zww4z2lx62nbma0pda";
|
||||
url = "https://pub.dartlang.org/packages/yaml/versions/3.1.2.tar.gz";
|
||||
stripRoot = false;
|
||||
extension = "tar.gz";
|
||||
};
|
||||
|
||||
pubCache = runCommand "moxxmpp-pub-cache" {} ''
|
||||
mkdir -p $out/hosted/pub.dev
|
||||
ln -s ${_fe_analyzer_shared} $out/hosted/pub.dev/_fe_analyzer_shared-61.0.0
|
||||
|
||||
mkdir -p $out/hosted/pub.dev
|
||||
ln -s ${analyzer} $out/hosted/pub.dev/analyzer-5.13.0
|
||||
|
||||
mkdir -p $out/hosted/pub.dev
|
||||
ln -s ${args} $out/hosted/pub.dev/args-2.4.2
|
||||
|
||||
mkdir -p $out/hosted/pub.dev
|
||||
ln -s ${async} $out/hosted/pub.dev/async-2.11.0
|
||||
|
||||
mkdir -p $out/hosted/pub.dev
|
||||
ln -s ${boolean_selector} $out/hosted/pub.dev/boolean_selector-2.1.1
|
||||
|
||||
mkdir -p $out/hosted/pub.dev
|
||||
ln -s ${build} $out/hosted/pub.dev/build-2.3.1
|
||||
|
||||
mkdir -p $out/hosted/pub.dev
|
||||
ln -s ${build_config} $out/hosted/pub.dev/build_config-1.1.1
|
||||
|
||||
mkdir -p $out/hosted/pub.dev
|
||||
ln -s ${build_daemon} $out/hosted/pub.dev/build_daemon-3.1.1
|
||||
|
||||
mkdir -p $out/hosted/pub.dev
|
||||
ln -s ${build_resolvers} $out/hosted/pub.dev/build_resolvers-2.3.1
|
||||
|
||||
mkdir -p $out/hosted/pub.dev
|
||||
ln -s ${build_runner} $out/hosted/pub.dev/build_runner-2.3.3
|
||||
|
||||
mkdir -p $out/hosted/pub.dev
|
||||
ln -s ${build_runner_core} $out/hosted/pub.dev/build_runner_core-7.2.7+1
|
||||
|
||||
mkdir -p $out/hosted/pub.dev
|
||||
ln -s ${built_collection} $out/hosted/pub.dev/built_collection-5.1.1
|
||||
|
||||
mkdir -p $out/hosted/pub.dev
|
||||
ln -s ${built_value} $out/hosted/pub.dev/built_value-8.6.2
|
||||
|
||||
mkdir -p $out/hosted/pub.dev
|
||||
ln -s ${checked_yaml} $out/hosted/pub.dev/checked_yaml-2.0.3
|
||||
|
||||
mkdir -p $out/hosted/pub.dev
|
||||
ln -s ${code_builder} $out/hosted/pub.dev/code_builder-4.6.0
|
||||
|
||||
mkdir -p $out/hosted/pub.dev
|
||||
ln -s ${collection} $out/hosted/pub.dev/collection-1.18.0
|
||||
|
||||
mkdir -p $out/hosted/pub.dev
|
||||
ln -s ${convert} $out/hosted/pub.dev/convert-3.1.1
|
||||
|
||||
mkdir -p $out/hosted/pub.dev
|
||||
ln -s ${coverage} $out/hosted/pub.dev/coverage-1.6.3
|
||||
|
||||
mkdir -p $out/hosted/pub.dev
|
||||
ln -s ${crypto} $out/hosted/pub.dev/crypto-3.0.3
|
||||
|
||||
mkdir -p $out/hosted/pub.dev
|
||||
ln -s ${cryptography} $out/hosted/pub.dev/cryptography-2.5.0
|
||||
|
||||
mkdir -p $out/hosted/pub.dev
|
||||
ln -s ${dart_style} $out/hosted/pub.dev/dart_style-2.3.2
|
||||
|
||||
mkdir -p $out/hosted/pub.dev
|
||||
ln -s ${file} $out/hosted/pub.dev/file-6.1.4
|
||||
|
||||
mkdir -p $out/hosted/pub.dev
|
||||
ln -s ${fixnum} $out/hosted/pub.dev/fixnum-1.1.0
|
||||
|
||||
mkdir -p $out/hosted/pub.dev
|
||||
ln -s ${frontend_server_client} $out/hosted/pub.dev/frontend_server_client-3.2.0
|
||||
|
||||
mkdir -p $out/hosted/pub.dev
|
||||
ln -s ${glob} $out/hosted/pub.dev/glob-2.1.2
|
||||
|
||||
mkdir -p $out/hosted/pub.dev
|
||||
ln -s ${graphs} $out/hosted/pub.dev/graphs-2.3.1
|
||||
|
||||
mkdir -p $out/hosted/pub.dev
|
||||
ln -s ${hex} $out/hosted/pub.dev/hex-0.2.0
|
||||
|
||||
mkdir -p $out/hosted/pub.dev
|
||||
ln -s ${http_multi_server} $out/hosted/pub.dev/http_multi_server-3.2.1
|
||||
|
||||
mkdir -p $out/hosted/pub.dev
|
||||
ln -s ${http_parser} $out/hosted/pub.dev/http_parser-4.0.2
|
||||
|
||||
mkdir -p $out/hosted/pub.dev
|
||||
ln -s ${io} $out/hosted/pub.dev/io-1.0.4
|
||||
|
||||
mkdir -p $out/hosted/pub.dev
|
||||
ln -s ${js} $out/hosted/pub.dev/js-0.6.7
|
||||
|
||||
mkdir -p $out/hosted/pub.dev
|
||||
ln -s ${json_annotation} $out/hosted/pub.dev/json_annotation-4.8.1
|
||||
|
||||
mkdir -p $out/hosted/pub.dev
|
||||
ln -s ${json_serializable} $out/hosted/pub.dev/json_serializable-6.6.2
|
||||
|
||||
mkdir -p $out/hosted/pub.dev
|
||||
ln -s ${logging} $out/hosted/pub.dev/logging-1.2.0
|
||||
|
||||
mkdir -p $out/hosted/pub.dev
|
||||
ln -s ${matcher} $out/hosted/pub.dev/matcher-0.12.16
|
||||
|
||||
mkdir -p $out/hosted/pub.dev
|
||||
ln -s ${meta} $out/hosted/pub.dev/meta-1.9.1
|
||||
|
||||
mkdir -p $out/hosted/pub.dev
|
||||
ln -s ${mime} $out/hosted/pub.dev/mime-1.0.4
|
||||
|
||||
mkdir -p $out/hosted/git.polynom.me%47api%47packages%47Moxxy%47pub%47
|
||||
ln -s ${moxlib} $out/hosted/git.polynom.me%47api%47packages%47Moxxy%47pub%47/moxlib-0.2.0
|
||||
|
||||
mkdir -p $out/hosted/pub.dev
|
||||
ln -s ${node_preamble} $out/hosted/pub.dev/node_preamble-2.0.2
|
||||
|
||||
mkdir -p $out/hosted/git.polynom.me%47api%47packages%47PapaTutuWawa%47pub%47
|
||||
ln -s ${omemo_dart} $out/hosted/git.polynom.me%47api%47packages%47PapaTutuWawa%47pub%47/omemo_dart-0.5.1
|
||||
|
||||
mkdir -p $out/hosted/pub.dev
|
||||
ln -s ${package_config} $out/hosted/pub.dev/package_config-2.1.0
|
||||
|
||||
mkdir -p $out/hosted/pub.dev
|
||||
ln -s ${path} $out/hosted/pub.dev/path-1.8.3
|
||||
|
||||
mkdir -p $out/hosted/pub.dev
|
||||
ln -s ${pedantic} $out/hosted/pub.dev/pedantic-1.11.1
|
||||
|
||||
mkdir -p $out/hosted/pub.dev
|
||||
ln -s ${petitparser} $out/hosted/pub.dev/petitparser-5.4.0
|
||||
|
||||
mkdir -p $out/hosted/pub.dev
|
||||
ln -s ${pinenacl} $out/hosted/pub.dev/pinenacl-0.5.1
|
||||
|
||||
mkdir -p $out/hosted/pub.dev
|
||||
ln -s ${pool} $out/hosted/pub.dev/pool-1.5.1
|
||||
|
||||
mkdir -p $out/hosted/pub.dev
|
||||
ln -s ${protobuf} $out/hosted/pub.dev/protobuf-2.1.0
|
||||
|
||||
mkdir -p $out/hosted/pub.dev
|
||||
ln -s ${protoc_plugin} $out/hosted/pub.dev/protoc_plugin-20.0.1
|
||||
|
||||
mkdir -p $out/hosted/pub.dev
|
||||
ln -s ${pub_semver} $out/hosted/pub.dev/pub_semver-2.1.4
|
||||
|
||||
mkdir -p $out/hosted/pub.dev
|
||||
ln -s ${pubspec_parse} $out/hosted/pub.dev/pubspec_parse-1.2.3
|
||||
|
||||
mkdir -p $out/hosted/pub.dev
|
||||
ln -s ${random_string} $out/hosted/pub.dev/random_string-2.3.1
|
||||
|
||||
mkdir -p $out/hosted/pub.dev
|
||||
ln -s ${saslprep} $out/hosted/pub.dev/saslprep-1.0.2
|
||||
|
||||
mkdir -p $out/hosted/pub.dev
|
||||
ln -s ${shelf} $out/hosted/pub.dev/shelf-1.4.1
|
||||
|
||||
mkdir -p $out/hosted/pub.dev
|
||||
ln -s ${shelf_packages_handler} $out/hosted/pub.dev/shelf_packages_handler-3.0.2
|
||||
|
||||
mkdir -p $out/hosted/pub.dev
|
||||
ln -s ${shelf_static} $out/hosted/pub.dev/shelf_static-1.1.2
|
||||
|
||||
mkdir -p $out/hosted/pub.dev
|
||||
ln -s ${shelf_web_socket} $out/hosted/pub.dev/shelf_web_socket-1.0.4
|
||||
|
||||
mkdir -p $out/hosted/pub.dev
|
||||
ln -s ${source_gen} $out/hosted/pub.dev/source_gen-1.3.2
|
||||
|
||||
mkdir -p $out/hosted/pub.dev
|
||||
ln -s ${source_helper} $out/hosted/pub.dev/source_helper-1.3.4
|
||||
|
||||
mkdir -p $out/hosted/pub.dev
|
||||
ln -s ${source_map_stack_trace} $out/hosted/pub.dev/source_map_stack_trace-2.1.1
|
||||
|
||||
mkdir -p $out/hosted/pub.dev
|
||||
ln -s ${source_maps} $out/hosted/pub.dev/source_maps-0.10.12
|
||||
|
||||
mkdir -p $out/hosted/pub.dev
|
||||
ln -s ${source_span} $out/hosted/pub.dev/source_span-1.10.0
|
||||
|
||||
mkdir -p $out/hosted/pub.dev
|
||||
ln -s ${stack_trace} $out/hosted/pub.dev/stack_trace-1.11.1
|
||||
|
||||
mkdir -p $out/hosted/pub.dev
|
||||
ln -s ${stream_channel} $out/hosted/pub.dev/stream_channel-2.1.2
|
||||
|
||||
mkdir -p $out/hosted/pub.dev
|
||||
ln -s ${stream_transform} $out/hosted/pub.dev/stream_transform-2.1.0
|
||||
|
||||
mkdir -p $out/hosted/pub.dev
|
||||
ln -s ${string_scanner} $out/hosted/pub.dev/string_scanner-1.2.0
|
||||
|
||||
mkdir -p $out/hosted/pub.dev
|
||||
ln -s ${synchronized} $out/hosted/pub.dev/synchronized-3.1.0
|
||||
|
||||
mkdir -p $out/hosted/pub.dev
|
||||
ln -s ${term_glyph} $out/hosted/pub.dev/term_glyph-1.2.1
|
||||
|
||||
mkdir -p $out/hosted/pub.dev
|
||||
ln -s ${test} $out/hosted/pub.dev/test-1.24.3
|
||||
|
||||
mkdir -p $out/hosted/pub.dev
|
||||
ln -s ${test_api} $out/hosted/pub.dev/test_api-0.6.0
|
||||
|
||||
mkdir -p $out/hosted/pub.dev
|
||||
ln -s ${test_core} $out/hosted/pub.dev/test_core-0.5.3
|
||||
|
||||
mkdir -p $out/hosted/pub.dev
|
||||
ln -s ${timing} $out/hosted/pub.dev/timing-1.0.1
|
||||
|
||||
mkdir -p $out/hosted/pub.dev
|
||||
ln -s ${typed_data} $out/hosted/pub.dev/typed_data-1.3.2
|
||||
|
||||
mkdir -p $out/hosted/pub.dev
|
||||
ln -s ${unorm_dart} $out/hosted/pub.dev/unorm_dart-0.2.0
|
||||
|
||||
mkdir -p $out/hosted/pub.dev
|
||||
ln -s ${uuid} $out/hosted/pub.dev/uuid-3.0.7
|
||||
|
||||
mkdir -p $out/hosted/pub.dev
|
||||
ln -s ${very_good_analysis} $out/hosted/pub.dev/very_good_analysis-3.1.0
|
||||
|
||||
mkdir -p $out/hosted/pub.dev
|
||||
ln -s ${vm_service} $out/hosted/pub.dev/vm_service-11.10.0
|
||||
|
||||
mkdir -p $out/hosted/pub.dev
|
||||
ln -s ${watcher} $out/hosted/pub.dev/watcher-1.0.2
|
||||
|
||||
mkdir -p $out/hosted/pub.dev
|
||||
ln -s ${web_socket_channel} $out/hosted/pub.dev/web_socket_channel-2.4.0
|
||||
|
||||
mkdir -p $out/hosted/pub.dev
|
||||
ln -s ${webkit_inspection_protocol} $out/hosted/pub.dev/webkit_inspection_protocol-1.2.0
|
||||
|
||||
mkdir -p $out/hosted/pub.dev
|
||||
ln -s ${xml} $out/hosted/pub.dev/xml-6.3.0
|
||||
|
||||
mkdir -p $out/hosted/pub.dev
|
||||
ln -s ${yaml} $out/hosted/pub.dev/yaml-3.1.2
|
||||
'';
|
||||
|
||||
}
|
@ -1,3 +1,51 @@
|
||||
## 0.4.1
|
||||
- Moved FAST from staging to xep_0484.dart
|
||||
|
||||
## 0.4.0
|
||||
|
||||
- **BREAKING**: Remove `lastResource` from `XmppConnection`'s `connect` method. Instead, set the `StreamManagementNegotiator`'s `resource` attribute instead. Since the resource can only really be restored by stream management, this is no issue.
|
||||
- **BREAKING**: Changed order of parameters of `CryptographicHashManager.hashFromData`
|
||||
- **BREAKING**: Removed support for XEP-0414, as the (supported) hash computations are already implemented by `CryptographicHashManager.hashFromData`.
|
||||
- The `DiscoManager` now only handled entity capabilities if a `EntityCapabilityManager` is registered.
|
||||
- The `EntityCapabilityManager` now verifies and validates its data before caching.
|
||||
- **BREAKING**: Added the `resumed` parameter to `StreamNegotiationsDoneEvent`. Use this to check if the current stream is new or resumed instead of using the `ConnectionStateChangedEvent`.
|
||||
- **BREAKING**: Remove `DiscoManager.discoInfoCapHashQuery`.
|
||||
- **BREAKING**: The entity argument of `DiscoManager.discoInfoQuery` and `DiscoManager.discoItemsQuery` are now `JID` instead of `String`.
|
||||
- **BREAKING**: `PubSubManager` and `UserAvatarManager` now use `JID` instead of `String`.
|
||||
- **BREAKING**: `XmppConnection.sendStanza` not only takes a `StanzaDetails` argument.
|
||||
- Sent stanzas are now kept in a queue until sent.
|
||||
- **BREAKING**: `MessageManager.sendMessage` does not use `MessageDetails` anymore. Instead, use `TypedMap`.
|
||||
- `MessageManager` now allows registering callbacks for adding data whenever a message is sent.
|
||||
- **BREAKING**: `MessageEvent` now makes use of `TypedMap`.
|
||||
- **BREAKING**: Removed `PresenceReceivedEvent`. Use a manager registering handlers with priority greater than `[PresenceManager.presenceHandlerPriority]` instead.
|
||||
- **BREAKING**: `ChatState.toString()` is now `ChatState.toName()`
|
||||
- **BREAKING**: Overriding `BaseOmemoManager` is no longer required. `OmemoManager` now takes callback methods instead.
|
||||
- Removed `ErrorResponseDiscoError` from the possible XEP-0030 errors.
|
||||
- **BREAKING**: Removed "Extensible File Thumbnails" (The `Thumbnail` type).
|
||||
- *BREAKING*: Rename `UserAvatarManager`'s `getUserAvatar` to `getUserAvatarData`. It now also requires the id of the avatar to fetch
|
||||
- *BREAKING*: `UserAvatarManager`'s `getAvatarId` with `getLatestMetadata`.
|
||||
- The `PubSubManager` now supports PubSub's `max_items` in `getItems`.
|
||||
- *BREAKING*: `vCardManager`'s `VCardAvatarUpdatedEvent` no longer automatically requests the newest VCard avatar.
|
||||
- *BREAKING*: `XmppConnection` now tries to ensure that incoming data is processed in-order. The only exception are awaited stanzas as they are allowed to bypass the queue.
|
||||
- *BREAKING*: If a stanza handler causes an exception, the handler is simply skipped while processing.
|
||||
- Add better logging around what stanza handler is running and if they end processing early.
|
||||
|
||||
## 0.3.1
|
||||
|
||||
- Fix some issues with running moxxmpp as a component
|
||||
|
||||
## 0.3.0
|
||||
|
||||
- **BREAKING**: Removed `connectAwaitable` and merged it with `connect`.
|
||||
- **BREAKING**: Removed `allowPlainAuth` from `ConnectionSettings`. If you don't want to use SASL PLAIN, don't register the negotiator. If you want to only conditionally use SASL PLAIN, extend the `SaslPlainNegotiator` and override its `matchesFeature` method to only call the super method when SASL PLAIN should be used.
|
||||
- **BREAKING**: The user avatar's `subscribe` and `unsubscribe` no longer subscribe to the `:data` PubSub nodes
|
||||
- Renamed `ResourceBindingSuccessEvent` to `ResourceBoundEvent`
|
||||
- **BREAKING**: Removed `isFeatureSupported` from the manager attributes. The managers now all have a method `isFeatureSupported` that works the same
|
||||
- The `PresenceManager` is now optional
|
||||
- **BREAKING**: Removed `setConnectionSettings` and `getConnectionSettings`. Just directly acces the `connectionSettings` field.
|
||||
- Implement XEP-0114 for implementing components
|
||||
- **BREAKING**: Remove `useDirectTLS` from `ConnectionSettings`
|
||||
|
||||
## 0.1.6+1
|
||||
|
||||
- **FIX**: Fix LMC not working.
|
||||
|
@ -2,6 +2,24 @@
|
||||
|
||||
A pure-Dart XMPP library written for Moxxy.
|
||||
|
||||
## Usage
|
||||
|
||||
Include the following as a dependency in your pubspec file:
|
||||
|
||||
```
|
||||
moxxmpp:
|
||||
hosted: https://git.polynom.me/api/packages/Moxxy/pub
|
||||
version: 0.3.1
|
||||
```
|
||||
|
||||
You can find the documentation [here](https://moxxy.org/developers/docs/moxxmpp/).
|
||||
|
||||
## License
|
||||
|
||||
See `./LICENSE`.
|
||||
|
||||
## Support
|
||||
|
||||
If you like what I do and you want to support me, feel free to donate to me on Ko-Fi.
|
||||
|
||||
[<img src="https://codeberg.org/moxxy/moxxyv2/raw/branch/master/assets/repo/kofi.png" height="36" style="height: 36px; border: 0px;"></img>](https://ko-fi.com/papatutuwawa)
|
||||
|
@ -1,98 +0,0 @@
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:moxxmpp/moxxmpp.dart';
|
||||
import 'package:moxxmpp_socket_tcp/moxxmpp_socket_tcp.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
void main() {
|
||||
Logger.root.level = Level.ALL;
|
||||
Logger.root.onRecord.listen((record) {
|
||||
print('${record.level.name}: ${record.time}: ${record.message}');
|
||||
});
|
||||
final log = Logger('FailureReconnectionTest');
|
||||
|
||||
test('Failing an awaited connection with TestingSleepReconnectionPolicy', () async {
|
||||
var errors = 0;
|
||||
final connection = XmppConnection(
|
||||
TestingSleepReconnectionPolicy(10),
|
||||
TCPSocketWrapper(false),
|
||||
);
|
||||
connection.registerFeatureNegotiators([
|
||||
StartTlsNegotiator(),
|
||||
]);
|
||||
connection.registerManagers([
|
||||
DiscoManager(),
|
||||
RosterManager(),
|
||||
PingManager(),
|
||||
MessageManager(),
|
||||
PresenceManager('http://moxxmpp.example'),
|
||||
]);
|
||||
connection.asBroadcastStream().listen((event) {
|
||||
if (event is ConnectionStateChangedEvent) {
|
||||
if (event.state == XmppConnectionState.error) {
|
||||
errors++;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
connection.setConnectionSettings(
|
||||
ConnectionSettings(
|
||||
jid: JID.fromString('testuser@no-sasl.badxmpp.eu'),
|
||||
password: 'abc123',
|
||||
useDirectTLS: true,
|
||||
allowPlainAuth: true,
|
||||
),
|
||||
);
|
||||
|
||||
final result = await connection.connectAwaitable();
|
||||
log.info('Connection failed as expected');
|
||||
expect(result.success, false);
|
||||
expect(errors, 1);
|
||||
|
||||
log.info('Waiting 20 seconds for unexpected reconnections');
|
||||
await Future.delayed(const Duration(seconds: 20));
|
||||
expect(errors, 1);
|
||||
}, timeout: Timeout.factor(2));
|
||||
|
||||
test('Failing an awaited connection with ExponentialBackoffReconnectionPolicy', () async {
|
||||
var errors = 0;
|
||||
final connection = XmppConnection(
|
||||
ExponentialBackoffReconnectionPolicy(1),
|
||||
TCPSocketWrapper(false),
|
||||
);
|
||||
connection.registerFeatureNegotiators([
|
||||
StartTlsNegotiator(),
|
||||
]);
|
||||
connection.registerManagers([
|
||||
DiscoManager(),
|
||||
RosterManager(),
|
||||
PingManager(),
|
||||
MessageManager(),
|
||||
PresenceManager('http://moxxmpp.example'),
|
||||
]);
|
||||
connection.asBroadcastStream().listen((event) {
|
||||
if (event is ConnectionStateChangedEvent) {
|
||||
if (event.state == XmppConnectionState.error) {
|
||||
errors++;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
connection.setConnectionSettings(
|
||||
ConnectionSettings(
|
||||
jid: JID.fromString('testuser@no-sasl.badxmpp.eu'),
|
||||
password: 'abc123',
|
||||
useDirectTLS: true,
|
||||
allowPlainAuth: true,
|
||||
),
|
||||
);
|
||||
|
||||
final result = await connection.connectAwaitable();
|
||||
log.info('Connection failed as expected');
|
||||
expect(result.success, false);
|
||||
expect(errors, 1);
|
||||
|
||||
log.info('Waiting 20 seconds for unexpected reconnections');
|
||||
await Future.delayed(const Duration(seconds: 20));
|
||||
expect(errors, 1);
|
||||
}, timeout: Timeout.factor(2));
|
||||
}
|
@ -1,8 +1,13 @@
|
||||
library moxxmpp;
|
||||
|
||||
export 'package:moxxmpp/src/connection.dart';
|
||||
export 'package:moxxmpp/src/connection_errors.dart';
|
||||
export 'package:moxxmpp/src/connectivity.dart';
|
||||
export 'package:moxxmpp/src/errors.dart';
|
||||
export 'package:moxxmpp/src/events.dart';
|
||||
export 'package:moxxmpp/src/handlers/base.dart';
|
||||
export 'package:moxxmpp/src/handlers/client.dart';
|
||||
export 'package:moxxmpp/src/handlers/component.dart';
|
||||
export 'package:moxxmpp/src/iq.dart';
|
||||
export 'package:moxxmpp/src/jid.dart';
|
||||
export 'package:moxxmpp/src/managers/attributes.dart';
|
||||
@ -13,34 +18,37 @@ export 'package:moxxmpp/src/managers/namespaces.dart';
|
||||
export 'package:moxxmpp/src/managers/priorities.dart';
|
||||
export 'package:moxxmpp/src/message.dart';
|
||||
export 'package:moxxmpp/src/namespaces.dart';
|
||||
export 'package:moxxmpp/src/negotiators/manager.dart';
|
||||
export 'package:moxxmpp/src/negotiators/namespaces.dart';
|
||||
export 'package:moxxmpp/src/negotiators/negotiator.dart';
|
||||
export 'package:moxxmpp/src/negotiators/resource_binding.dart';
|
||||
export 'package:moxxmpp/src/negotiators/sasl/errors.dart';
|
||||
export 'package:moxxmpp/src/negotiators/sasl/negotiator.dart';
|
||||
export 'package:moxxmpp/src/negotiators/sasl/plain.dart';
|
||||
export 'package:moxxmpp/src/negotiators/sasl/scram.dart';
|
||||
export 'package:moxxmpp/src/negotiators/starttls.dart';
|
||||
export 'package:moxxmpp/src/ping.dart';
|
||||
export 'package:moxxmpp/src/presence.dart';
|
||||
export 'package:moxxmpp/src/reconnect.dart';
|
||||
export 'package:moxxmpp/src/rfcs/rfc_2782.dart';
|
||||
export 'package:moxxmpp/src/rfcs/rfc_4790.dart';
|
||||
export 'package:moxxmpp/src/rfcs/rfc_6120/resource_binding.dart';
|
||||
export 'package:moxxmpp/src/rfcs/rfc_6120/sasl/errors.dart';
|
||||
export 'package:moxxmpp/src/rfcs/rfc_6120/sasl/negotiator.dart';
|
||||
export 'package:moxxmpp/src/rfcs/rfc_6120/sasl/plain.dart';
|
||||
export 'package:moxxmpp/src/rfcs/rfc_6120/sasl/scram.dart';
|
||||
export 'package:moxxmpp/src/rfcs/rfc_6120/starttls.dart';
|
||||
export 'package:moxxmpp/src/roster/errors.dart';
|
||||
export 'package:moxxmpp/src/roster/roster.dart';
|
||||
export 'package:moxxmpp/src/roster/state.dart';
|
||||
export 'package:moxxmpp/src/settings.dart';
|
||||
export 'package:moxxmpp/src/socket.dart';
|
||||
export 'package:moxxmpp/src/stanza.dart';
|
||||
export 'package:moxxmpp/src/stringxml.dart';
|
||||
export 'package:moxxmpp/src/types/result.dart';
|
||||
export 'package:moxxmpp/src/xeps/staging/extensible_file_thumbnails.dart';
|
||||
export 'package:moxxmpp/src/util/typed_map.dart';
|
||||
export 'package:moxxmpp/src/xeps/staging/file_upload_notification.dart';
|
||||
export 'package:moxxmpp/src/xeps/xep_0004.dart';
|
||||
export 'package:moxxmpp/src/xeps/xep_0030/errors.dart';
|
||||
export 'package:moxxmpp/src/xeps/xep_0030/helpers.dart';
|
||||
export 'package:moxxmpp/src/xeps/xep_0030/types.dart';
|
||||
export 'package:moxxmpp/src/xeps/xep_0030/xep_0030.dart';
|
||||
export 'package:moxxmpp/src/xeps/xep_0045/errors.dart';
|
||||
export 'package:moxxmpp/src/xeps/xep_0045/events.dart';
|
||||
export 'package:moxxmpp/src/xeps/xep_0045/types.dart';
|
||||
export 'package:moxxmpp/src/xeps/xep_0045/xep_0045.dart';
|
||||
export 'package:moxxmpp/src/xeps/xep_0054.dart';
|
||||
export 'package:moxxmpp/src/xeps/xep_0060/errors.dart';
|
||||
export 'package:moxxmpp/src/xeps/xep_0060/helpers.dart';
|
||||
@ -56,6 +64,7 @@ export 'package:moxxmpp/src/xeps/xep_0198/nonzas.dart';
|
||||
export 'package:moxxmpp/src/xeps/xep_0198/state.dart';
|
||||
export 'package:moxxmpp/src/xeps/xep_0198/xep_0198.dart';
|
||||
export 'package:moxxmpp/src/xeps/xep_0203.dart';
|
||||
export 'package:moxxmpp/src/xeps/xep_0264.dart';
|
||||
export 'package:moxxmpp/src/xeps/xep_0280.dart';
|
||||
export 'package:moxxmpp/src/xeps/xep_0297.dart';
|
||||
export 'package:moxxmpp/src/xeps/xep_0300.dart';
|
||||
@ -73,9 +82,17 @@ export 'package:moxxmpp/src/xeps/xep_0384/helpers.dart';
|
||||
export 'package:moxxmpp/src/xeps/xep_0384/types.dart';
|
||||
export 'package:moxxmpp/src/xeps/xep_0384/xep_0384.dart';
|
||||
export 'package:moxxmpp/src/xeps/xep_0385.dart';
|
||||
export 'package:moxxmpp/src/xeps/xep_0414.dart';
|
||||
export 'package:moxxmpp/src/xeps/xep_0386.dart';
|
||||
export 'package:moxxmpp/src/xeps/xep_0388/errors.dart';
|
||||
export 'package:moxxmpp/src/xeps/xep_0388/negotiators.dart';
|
||||
export 'package:moxxmpp/src/xeps/xep_0388/user_agent.dart';
|
||||
export 'package:moxxmpp/src/xeps/xep_0388/xep_0388.dart';
|
||||
export 'package:moxxmpp/src/xeps/xep_0421.dart';
|
||||
export 'package:moxxmpp/src/xeps/xep_0424.dart';
|
||||
export 'package:moxxmpp/src/xeps/xep_0444.dart';
|
||||
export 'package:moxxmpp/src/xeps/xep_0446.dart';
|
||||
export 'package:moxxmpp/src/xeps/xep_0447.dart';
|
||||
export 'package:moxxmpp/src/xeps/xep_0448.dart';
|
||||
export 'package:moxxmpp/src/xeps/xep_0449.dart';
|
||||
export 'package:moxxmpp/src/xeps/xep_0461.dart';
|
||||
export 'package:moxxmpp/src/xeps/xep_0484.dart';
|
||||
|
94
packages/moxxmpp/lib/src/awaiter.dart
Normal file
@ -0,0 +1,94 @@
|
||||
import 'dart:async';
|
||||
import 'package:moxxmpp/src/stringxml.dart';
|
||||
import 'package:synchronized/synchronized.dart';
|
||||
|
||||
/// (JID we sent a stanza to, the id of the sent stanza, the tag of the sent stanza).
|
||||
// ignore: avoid_private_typedef_functions
|
||||
typedef _StanzaCompositeKey = (String?, String, String);
|
||||
|
||||
/// Callback function that returns the bare JID of the connection as a String.
|
||||
typedef GetBareJidCallback = String Function();
|
||||
|
||||
/// This class handles the await semantics for stanzas. Stanzas are given a "unique"
|
||||
/// key equal to the tuple (to, id, tag) with which their response is identified.
|
||||
///
|
||||
/// That means that when sending ```<iq to="example@some.server.example" id="abc123" />```,
|
||||
/// the response stanza must be from "example@some.server.example", have id "abc123" and
|
||||
/// be an iq stanza.
|
||||
///
|
||||
/// This class also handles some "edge cases" of RFC 6120, like an empty "from" attribute.
|
||||
class StanzaAwaiter {
|
||||
StanzaAwaiter(this._bareJidCallback);
|
||||
|
||||
final GetBareJidCallback _bareJidCallback;
|
||||
|
||||
/// The pending stanzas, identified by their surrogate key.
|
||||
final Map<_StanzaCompositeKey, Completer<XMLNode>> _pending = {};
|
||||
|
||||
/// The critical section for accessing [StanzaAwaiter._pending].
|
||||
final Lock _lock = Lock();
|
||||
|
||||
/// Register a stanza as pending.
|
||||
/// [to] is the value of the stanza's "to" attribute.
|
||||
/// [id] is the value of the stanza's "id" attribute.
|
||||
/// [tag] is the stanza's tag name.
|
||||
///
|
||||
/// Returns a future that might resolve to the response to the stanza.
|
||||
Future<Future<XMLNode>> addPending(String? to, String id, String tag) async {
|
||||
// Check if we want to send a stanza to our bare JID and replace it with null.
|
||||
final processedTo = to != null && to == _bareJidCallback() ? null : to;
|
||||
|
||||
final completer = await _lock.synchronized(() {
|
||||
final completer = Completer<XMLNode>();
|
||||
_pending[(processedTo, id, tag)] = completer;
|
||||
return completer;
|
||||
});
|
||||
|
||||
return completer.future;
|
||||
}
|
||||
|
||||
/// Checks if the stanza [stanza] is being awaited.
|
||||
/// If [stanza] is awaited, resolves the future and returns true. If not, returns
|
||||
/// false.
|
||||
Future<bool> onData(XMLNode stanza) async {
|
||||
final id = stanza.attributes['id'] as String?;
|
||||
if (id == null) return false;
|
||||
|
||||
// Check if we want to send a stanza to our bare JID and replace it with null.
|
||||
final from = stanza.attributes['from'] as String?;
|
||||
final processedFrom =
|
||||
from != null && from == _bareJidCallback() ? null : from;
|
||||
|
||||
final key = (
|
||||
processedFrom,
|
||||
id,
|
||||
stanza.tag,
|
||||
);
|
||||
|
||||
return _lock.synchronized(() {
|
||||
final completer = _pending[key];
|
||||
if (completer != null) {
|
||||
_pending.remove(key);
|
||||
completer.complete(stanza);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
/// Checks if [stanza] represents a stanza that is awaited. Returns true, if [stanza]
|
||||
/// is awaited. False, if not.
|
||||
Future<bool> isAwaited(XMLNode stanza) async {
|
||||
final id = stanza.attributes['id'] as String?;
|
||||
if (id == null) return false;
|
||||
|
||||
final key = (
|
||||
stanza.attributes['from'] as String?,
|
||||
id,
|
||||
stanza.tag,
|
||||
);
|
||||
|
||||
return _lock.synchronized(() => _pending.containsKey(key));
|
||||
}
|
||||
}
|
@ -1,27 +0,0 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:moxxmpp/src/stringxml.dart';
|
||||
|
||||
import 'package:xml/xml.dart';
|
||||
import 'package:xml/xml_events.dart';
|
||||
|
||||
class XmlStreamBuffer extends StreamTransformerBase<String, XMLNode> {
|
||||
|
||||
XmlStreamBuffer() : _streamController = StreamController(), _decoder = const XmlNodeDecoder();
|
||||
final StreamController<XMLNode> _streamController;
|
||||
final XmlNodeDecoder _decoder;
|
||||
|
||||
@override
|
||||
Stream<XMLNode> bind(Stream<String> stream) {
|
||||
stream.toXmlEvents().selectSubtreeEvents((event) {
|
||||
return event.qualifiedName != 'stream:stream';
|
||||
}).transform(_decoder).listen((nodes) {
|
||||
for (final node in nodes) {
|
||||
if (node.nodeType == XmlNodeType.ELEMENT) {
|
||||
_streamController.add(XMLNode.fromXmlElement(node as XmlElement));
|
||||
}
|
||||
}
|
||||
});
|
||||
return _streamController.stream;
|
||||
}
|
||||
}
|
60
packages/moxxmpp/lib/src/connection_errors.dart
Normal file
@ -0,0 +1,60 @@
|
||||
import 'package:moxxmpp/src/errors.dart';
|
||||
import 'package:moxxmpp/src/negotiators/negotiator.dart';
|
||||
|
||||
/// The reason a call to `XmppConnection.connect` failed.
|
||||
abstract class XmppConnectionError extends XmppError {}
|
||||
|
||||
/// Returned by `XmppConnection.connect` when a negotiator returned an unrecoverable
|
||||
/// error. Only returned when waitUntilLogin is true.
|
||||
class NegotiatorReturnedError extends XmppConnectionError {
|
||||
NegotiatorReturnedError(this.error);
|
||||
|
||||
@override
|
||||
bool isRecoverable() => error.isRecoverable();
|
||||
|
||||
/// The error returned by the negotiator.
|
||||
final NegotiatorError error;
|
||||
}
|
||||
|
||||
class StreamFailureError extends XmppConnectionError {
|
||||
StreamFailureError(this.error);
|
||||
|
||||
@override
|
||||
bool isRecoverable() => error.isRecoverable();
|
||||
|
||||
/// The error that causes a connection failure.
|
||||
final XmppError error;
|
||||
}
|
||||
|
||||
/// Returned by `XmppConnection.connect` when no connection could
|
||||
/// be established.
|
||||
class NoConnectionPossibleError extends XmppConnectionError {
|
||||
@override
|
||||
bool isRecoverable() => true;
|
||||
}
|
||||
|
||||
/// Returned if no matching authentication mechanism has been presented
|
||||
class NoMatchingAuthenticationMechanismAvailableError
|
||||
extends XmppConnectionError {
|
||||
@override
|
||||
bool isRecoverable() => false;
|
||||
}
|
||||
|
||||
/// Returned if no negotiator was picked, even though negotiations are not done
|
||||
/// yet.
|
||||
class NoAuthenticatorAvailableError extends XmppConnectionError {
|
||||
@override
|
||||
bool isRecoverable() => false;
|
||||
}
|
||||
|
||||
/// Returned by the negotiation handler if unexpected data has been received
|
||||
class UnexpectedDataError extends XmppConnectionError {
|
||||
@override
|
||||
bool isRecoverable() => false;
|
||||
}
|
||||
|
||||
/// Returned by the ComponentToServerNegotiator if the handshake is not successful.
|
||||
class InvalidHandshakeCredentialsError extends XmppConnectionError {
|
||||
@override
|
||||
bool isRecoverable() => false;
|
||||
}
|
18
packages/moxxmpp/lib/src/connectivity.dart
Normal file
@ -0,0 +1,18 @@
|
||||
/// This manager class is responsible to tell the moxxmpp XmppConnection
|
||||
/// when a connection can be established or not, regarding the network availability.
|
||||
abstract class ConnectivityManager {
|
||||
/// Returns true if a network connection is available. If not, returns false.
|
||||
Future<bool> hasConnection();
|
||||
|
||||
/// Returns a future that resolves once we have a network connection.
|
||||
Future<void> waitForConnection();
|
||||
}
|
||||
|
||||
/// An implementation of [ConnectivityManager] that is always connected.
|
||||
class AlwaysConnectedConnectivityManager extends ConnectivityManager {
|
||||
@override
|
||||
Future<bool> hasConnection() async => true;
|
||||
|
||||
@override
|
||||
Future<void> waitForConnection() async {}
|
||||
}
|
@ -1,20 +1,30 @@
|
||||
import 'package:moxxmpp/src/socket.dart';
|
||||
|
||||
/// An internal error class
|
||||
abstract class XmppError {}
|
||||
|
||||
/// Returned if we could not establish a TCP connection
|
||||
/// to the server.
|
||||
class NoConnectionError extends XmppError {}
|
||||
// ignore: one_member_abstracts
|
||||
abstract class XmppError {
|
||||
/// Return true if we can recover from the error by attempting a reconnection.
|
||||
bool isRecoverable();
|
||||
}
|
||||
|
||||
/// Returned if a socket error occured
|
||||
class SocketError extends XmppError {
|
||||
SocketError(this.event);
|
||||
final XmppSocketErrorEvent event;
|
||||
|
||||
@override
|
||||
bool isRecoverable() => true;
|
||||
}
|
||||
|
||||
/// Returned if we time out
|
||||
class TimeoutError extends XmppError {}
|
||||
class TimeoutError extends XmppError {
|
||||
@override
|
||||
bool isRecoverable() => true;
|
||||
}
|
||||
|
||||
/// Returned if we received a stream error
|
||||
class StreamError extends XmppError {}
|
||||
class StreamError extends XmppError {
|
||||
// TODO(PapaTutuWawa): Be more precise
|
||||
@override
|
||||
bool isRecoverable() => true;
|
||||
}
|
||||
|
@ -1,32 +1,34 @@
|
||||
import 'package:moxxmpp/src/connection.dart';
|
||||
import 'package:moxxmpp/src/errors.dart';
|
||||
import 'package:moxxmpp/src/jid.dart';
|
||||
import 'package:moxxmpp/src/managers/data.dart';
|
||||
import 'package:moxxmpp/src/roster/roster.dart';
|
||||
import 'package:moxxmpp/src/stanza.dart';
|
||||
import 'package:moxxmpp/src/util/typed_map.dart';
|
||||
import 'package:moxxmpp/src/xeps/xep_0030/types.dart';
|
||||
import 'package:moxxmpp/src/xeps/xep_0060/xep_0060.dart';
|
||||
import 'package:moxxmpp/src/xeps/xep_0066.dart';
|
||||
import 'package:moxxmpp/src/xeps/xep_0085.dart';
|
||||
import 'package:moxxmpp/src/xeps/xep_0359.dart';
|
||||
import 'package:moxxmpp/src/xeps/xep_0385.dart';
|
||||
import 'package:moxxmpp/src/xeps/xep_0424.dart';
|
||||
import 'package:moxxmpp/src/xeps/xep_0446.dart';
|
||||
import 'package:moxxmpp/src/xeps/xep_0447.dart';
|
||||
import 'package:moxxmpp/src/xeps/xep_0461.dart';
|
||||
import 'package:moxxmpp/src/xeps/xep_0084.dart';
|
||||
import 'package:moxxmpp/src/xeps/xep_0333.dart';
|
||||
|
||||
abstract class XmppEvent {}
|
||||
|
||||
/// Triggered when the connection state of the XmppConnection has
|
||||
/// changed.
|
||||
class ConnectionStateChangedEvent extends XmppEvent {
|
||||
ConnectionStateChangedEvent(this.state, this.before, this.resumed);
|
||||
ConnectionStateChangedEvent(this.state, this.before);
|
||||
final XmppConnectionState before;
|
||||
final XmppConnectionState state;
|
||||
final bool resumed;
|
||||
|
||||
/// Indicates whether the connection state switched from a not connected state to a
|
||||
/// connected state.
|
||||
bool get connectionEstablished =>
|
||||
before != XmppConnectionState.connected &&
|
||||
state == XmppConnectionState.connected;
|
||||
}
|
||||
|
||||
/// Triggered when we encounter a stream error.
|
||||
class StreamErrorEvent extends XmppEvent {
|
||||
StreamErrorEvent({ required this.error });
|
||||
StreamErrorEvent({required this.error});
|
||||
final String error;
|
||||
}
|
||||
|
||||
@ -39,91 +41,100 @@ class AuthenticationFailedEvent extends XmppEvent {
|
||||
/// Triggered after the SASL authentication has succeeded.
|
||||
class AuthenticationSuccessEvent extends XmppEvent {}
|
||||
|
||||
/// Triggered when we want to ping the connection open
|
||||
class SendPingEvent extends XmppEvent {}
|
||||
|
||||
/// Triggered when the stream resumption was successful
|
||||
class StreamResumedEvent extends XmppEvent {
|
||||
StreamResumedEvent({ required this.h });
|
||||
StreamResumedEvent({required this.h});
|
||||
final int h;
|
||||
}
|
||||
|
||||
/// Triggered when stream resumption failed
|
||||
class StreamResumeFailedEvent extends XmppEvent {}
|
||||
|
||||
/// Triggered when the roster has been modified
|
||||
class RosterUpdatedEvent extends XmppEvent {
|
||||
RosterUpdatedEvent(this.removed, this.modified, this.added);
|
||||
|
||||
/// A list of bare JIDs that are removed from the roster
|
||||
final List<String> removed;
|
||||
|
||||
/// A list of XmppRosterItems that are modified. Can be correlated with one's cache
|
||||
/// using the jid attribute.
|
||||
final List<XmppRosterItem> modified;
|
||||
|
||||
/// A list of XmppRosterItems that are added to the roster.
|
||||
final List<XmppRosterItem> added;
|
||||
}
|
||||
|
||||
/// Triggered when a message is received
|
||||
class MessageEvent extends XmppEvent {
|
||||
MessageEvent({
|
||||
required this.body,
|
||||
required this.fromJid,
|
||||
required this.toJid,
|
||||
required this.sid,
|
||||
required this.stanzaId,
|
||||
required this.isCarbon,
|
||||
required this.deliveryReceiptRequested,
|
||||
required this.isMarkable,
|
||||
required this.encrypted,
|
||||
required this.other,
|
||||
this.error,
|
||||
MessageEvent(
|
||||
this.from,
|
||||
this.to,
|
||||
this.encrypted,
|
||||
this.extensions, {
|
||||
this.id,
|
||||
this.type,
|
||||
this.oob,
|
||||
this.sfs,
|
||||
this.sims,
|
||||
this.reply,
|
||||
this.chatState,
|
||||
this.fun,
|
||||
this.funReplacement,
|
||||
this.funCancellation,
|
||||
this.messageRetraction,
|
||||
this.messageCorrectionId,
|
||||
this.error,
|
||||
this.encryptionError,
|
||||
});
|
||||
final StanzaError? error;
|
||||
final String body;
|
||||
final JID fromJid;
|
||||
final JID toJid;
|
||||
final String sid;
|
||||
|
||||
/// The from attribute of the message.
|
||||
final JID from;
|
||||
|
||||
/// The to attribute of the message.
|
||||
final JID to;
|
||||
|
||||
/// The id attribute of the message.
|
||||
final String? id;
|
||||
|
||||
/// The type attribute of the message.
|
||||
final String? type;
|
||||
final StableStanzaId stanzaId;
|
||||
final bool isCarbon;
|
||||
final bool deliveryReceiptRequested;
|
||||
final bool isMarkable;
|
||||
final OOBData? oob;
|
||||
final StatelessFileSharingData? sfs;
|
||||
final StatelessMediaSharingData? sims;
|
||||
final ReplyData? reply;
|
||||
final ChatState? chatState;
|
||||
final FileMetadataData? fun;
|
||||
final String? funReplacement;
|
||||
final String? funCancellation;
|
||||
|
||||
final StanzaError? error;
|
||||
|
||||
/// Flag indicating whether the message was encrypted.
|
||||
final bool encrypted;
|
||||
final MessageRetractionData? messageRetraction;
|
||||
final String? messageCorrectionId;
|
||||
final Map<String, dynamic> other;
|
||||
|
||||
/// The error in case an encryption error occurred.
|
||||
final Object? encryptionError;
|
||||
|
||||
/// Data added by other handlers.
|
||||
final TypedMap<StanzaHandlerExtension> extensions;
|
||||
|
||||
/// Shorthand for extensions.get<T>().
|
||||
T? get<T>() => extensions.get<T>();
|
||||
}
|
||||
|
||||
/// Triggered when a client responds to our delivery receipt request
|
||||
class DeliveryReceiptReceivedEvent extends XmppEvent {
|
||||
DeliveryReceiptReceivedEvent({ required this.from, required this.id });
|
||||
DeliveryReceiptReceivedEvent({required this.from, required this.id});
|
||||
final JID from;
|
||||
final String id;
|
||||
}
|
||||
|
||||
class ChatMarkerEvent extends XmppEvent {
|
||||
ChatMarkerEvent({
|
||||
required this.type,
|
||||
required this.from,
|
||||
required this.id,
|
||||
});
|
||||
ChatMarkerEvent(
|
||||
this.from,
|
||||
this.type,
|
||||
this.id,
|
||||
);
|
||||
|
||||
/// The entity that sent the chat marker.
|
||||
final JID from;
|
||||
final String type;
|
||||
|
||||
/// The type of chat marker that was sent.
|
||||
final ChatMarker type;
|
||||
|
||||
/// The id of the message that the marker applies to.
|
||||
final String id;
|
||||
}
|
||||
|
||||
// Triggered when we received a Stream resumption ID
|
||||
class StreamManagementEnabledEvent extends XmppEvent {
|
||||
StreamManagementEnabledEvent({
|
||||
required this.resource,
|
||||
this.id,
|
||||
this.location,
|
||||
required this.resource,
|
||||
this.id,
|
||||
this.location,
|
||||
});
|
||||
final String resource;
|
||||
final String? id;
|
||||
@ -131,16 +142,11 @@ class StreamManagementEnabledEvent extends XmppEvent {
|
||||
}
|
||||
|
||||
/// Triggered when we bound a resource
|
||||
class ResourceBindingSuccessEvent extends XmppEvent {
|
||||
ResourceBindingSuccessEvent({ required this.resource });
|
||||
final String resource;
|
||||
}
|
||||
class ResourceBoundEvent extends XmppEvent {
|
||||
ResourceBoundEvent(this.resource);
|
||||
|
||||
/// Triggered when we receive presence
|
||||
class PresenceReceivedEvent extends XmppEvent {
|
||||
PresenceReceivedEvent(this.jid, this.presence);
|
||||
final JID jid;
|
||||
final Stanza presence;
|
||||
/// The resource that was just bound.
|
||||
final String resource;
|
||||
}
|
||||
|
||||
/// Triggered when we are starting an connection attempt
|
||||
@ -156,21 +162,41 @@ class ServerItemDiscoEvent extends XmppEvent {
|
||||
|
||||
/// Triggered when we receive a subscription request
|
||||
class SubscriptionRequestReceivedEvent extends XmppEvent {
|
||||
SubscriptionRequestReceivedEvent({ required this.from });
|
||||
SubscriptionRequestReceivedEvent({required this.from});
|
||||
final JID from;
|
||||
}
|
||||
|
||||
/// Triggered when we receive a new or updated avatar
|
||||
class AvatarUpdatedEvent extends XmppEvent {
|
||||
AvatarUpdatedEvent({ required this.jid, required this.base64, required this.hash });
|
||||
final String jid;
|
||||
final String base64;
|
||||
/// Triggered when we receive a new or updated avatar via XEP-0084
|
||||
class UserAvatarUpdatedEvent extends XmppEvent {
|
||||
UserAvatarUpdatedEvent(
|
||||
this.jid,
|
||||
this.metadata,
|
||||
);
|
||||
|
||||
/// The JID of the user updating their avatar.
|
||||
final JID jid;
|
||||
|
||||
/// The metadata of the avatar.
|
||||
final List<UserAvatarMetadata> metadata;
|
||||
}
|
||||
|
||||
/// Triggered when we receive a new or updated avatar via XEP-0054
|
||||
class VCardAvatarUpdatedEvent extends XmppEvent {
|
||||
VCardAvatarUpdatedEvent(
|
||||
this.jid,
|
||||
this.hash,
|
||||
);
|
||||
|
||||
/// The JID of the entity that updated their avatar.
|
||||
final JID jid;
|
||||
|
||||
/// The SHA-1 hash of the avatar.
|
||||
final String hash;
|
||||
}
|
||||
|
||||
/// Triggered when a PubSub notification has been received
|
||||
class PubSubNotificationEvent extends XmppEvent {
|
||||
PubSubNotificationEvent({ required this.item, required this.from });
|
||||
PubSubNotificationEvent({required this.item, required this.from});
|
||||
final PubSubItem item;
|
||||
final String from;
|
||||
}
|
||||
@ -183,13 +209,13 @@ class StanzaAckedEvent extends XmppEvent {
|
||||
|
||||
/// Triggered when receiving a push of the blocklist
|
||||
class BlocklistBlockPushEvent extends XmppEvent {
|
||||
BlocklistBlockPushEvent({ required this.items });
|
||||
BlocklistBlockPushEvent({required this.items});
|
||||
final List<String> items;
|
||||
}
|
||||
|
||||
/// Triggered when receiving a push of the blocklist
|
||||
class BlocklistUnblockPushEvent extends XmppEvent {
|
||||
BlocklistUnblockPushEvent({ required this.items });
|
||||
BlocklistUnblockPushEvent({required this.items});
|
||||
final List<String> items;
|
||||
}
|
||||
|
||||
@ -211,3 +237,21 @@ class OmemoDeviceListUpdatedEvent extends XmppEvent {
|
||||
final JID jid;
|
||||
final List<int> deviceList;
|
||||
}
|
||||
|
||||
/// Triggered when a reconnection is not performed due to a non-recoverable
|
||||
/// error.
|
||||
class NonRecoverableErrorEvent extends XmppEvent {
|
||||
NonRecoverableErrorEvent(this.error);
|
||||
|
||||
/// The error in question.
|
||||
final XmppError error;
|
||||
}
|
||||
|
||||
/// Triggered when the stream negotiations are done.
|
||||
class StreamNegotiationsDoneEvent extends XmppEvent {
|
||||
StreamNegotiationsDoneEvent(this.resumed);
|
||||
|
||||
/// Flag indicating whether we resumed a previous stream (true) or are in a completely
|
||||
/// new stream (false).
|
||||
final bool resumed;
|
||||
}
|
||||
|
132
packages/moxxmpp/lib/src/handlers/base.dart
Normal file
@ -0,0 +1,132 @@
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:meta/meta.dart';
|
||||
import 'package:moxxmpp/src/errors.dart';
|
||||
import 'package:moxxmpp/src/events.dart';
|
||||
import 'package:moxxmpp/src/negotiators/negotiator.dart';
|
||||
import 'package:moxxmpp/src/parser.dart';
|
||||
import 'package:moxxmpp/src/settings.dart';
|
||||
import 'package:moxxmpp/src/stringxml.dart';
|
||||
|
||||
/// A callback for when the [NegotiationsHandler] is done.
|
||||
typedef NegotiationsDoneCallback = Future<void> Function();
|
||||
|
||||
/// A callback for the case that an error occurs while negotiating.
|
||||
typedef ErrorCallback = Future<void> Function(XmppError);
|
||||
|
||||
/// Return true if the current connection is authenticated. If not, return false.
|
||||
typedef IsAuthenticatedFunction = bool Function();
|
||||
|
||||
/// Send a nonza on the stream
|
||||
typedef SendNonzaFunction = void Function(XMLNode);
|
||||
|
||||
/// Returns the connection settings.
|
||||
typedef GetConnectionSettingsFunction = ConnectionSettings Function();
|
||||
|
||||
/// Resets the stream parser's state.
|
||||
typedef ResetStreamParserFunction = void Function();
|
||||
|
||||
/// This class implements the stream feature negotiation for XmppConnection.
|
||||
abstract class NegotiationsHandler {
|
||||
@protected
|
||||
late final Logger log;
|
||||
|
||||
/// Map of all negotiators registered against the handler.
|
||||
@protected
|
||||
final Map<String, XmppFeatureNegotiatorBase> negotiators = {};
|
||||
|
||||
/// Function that is called once the negotiator is done with its stream negotiations.
|
||||
@protected
|
||||
late final NegotiationsDoneCallback onNegotiationsDone;
|
||||
|
||||
/// XmppConnection's handleError method.
|
||||
@protected
|
||||
late final ErrorCallback handleError;
|
||||
|
||||
/// Returns true if the connection is authenticated. If not, returns false.
|
||||
@protected
|
||||
late final IsAuthenticatedFunction isAuthenticated;
|
||||
|
||||
/// Send a nonza over the stream.
|
||||
@protected
|
||||
late final SendNonzaFunction sendNonza;
|
||||
|
||||
/// Get the connection's settings.
|
||||
@protected
|
||||
late final GetConnectionSettingsFunction getConnectionSettings;
|
||||
|
||||
@protected
|
||||
late final ResetStreamParserFunction resetStreamParser;
|
||||
|
||||
/// The id included in the last stream header.
|
||||
@protected
|
||||
String? streamId;
|
||||
|
||||
/// Set the id of the last stream header.
|
||||
void setStreamHeaderId(String? id) {
|
||||
streamId = id;
|
||||
}
|
||||
|
||||
/// Returns, if registered, a negotiator with id [id].
|
||||
T? getNegotiatorById<T extends XmppFeatureNegotiatorBase>(String id) =>
|
||||
negotiators[id] as T?;
|
||||
|
||||
/// Register the parameters as the corresponding methods in this class. Also
|
||||
/// initializes the logger.
|
||||
void register(
|
||||
NegotiationsDoneCallback onNegotiationsDone,
|
||||
ErrorCallback handleError,
|
||||
IsAuthenticatedFunction isAuthenticated,
|
||||
SendNonzaFunction sendNonza,
|
||||
GetConnectionSettingsFunction getConnectionSettings,
|
||||
ResetStreamParserFunction resetStreamParser,
|
||||
) {
|
||||
this.onNegotiationsDone = onNegotiationsDone;
|
||||
this.handleError = handleError;
|
||||
this.isAuthenticated = isAuthenticated;
|
||||
this.sendNonza = sendNonza;
|
||||
this.getConnectionSettings = getConnectionSettings;
|
||||
this.resetStreamParser = resetStreamParser;
|
||||
log = Logger(toString());
|
||||
}
|
||||
|
||||
/// Returns the xmlns attribute that stanzas should have.
|
||||
String getStanzaNamespace();
|
||||
|
||||
/// Registers the negotiator [negotiator] against this negotiations handler.
|
||||
void registerNegotiator(XmppFeatureNegotiatorBase negotiator);
|
||||
|
||||
/// Sends the stream header.
|
||||
void sendStreamHeader();
|
||||
|
||||
/// Runs the post-register callback of all negotiators.
|
||||
Future<void> runPostRegisterCallback() async {
|
||||
for (final negotiator in negotiators.values) {
|
||||
await negotiator.postRegisterCallback();
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> sendEventToNegotiators(XmppEvent event) async {
|
||||
for (final negotiator in negotiators.values) {
|
||||
await negotiator.onXmppEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
/// Remove [feature] from the stream features we are currently negotiating.
|
||||
void removeNegotiatingFeature(String feature) {}
|
||||
|
||||
/// Resets all registered negotiators and the negotiation handler.
|
||||
@mustCallSuper
|
||||
void reset() {
|
||||
streamId = null;
|
||||
for (final negotiator in negotiators.values) {
|
||||
negotiator.reset();
|
||||
}
|
||||
}
|
||||
|
||||
/// Called whenever the stream buffer outputs a new event [event].
|
||||
Future<void> negotiate(XMPPStreamObject event) async {
|
||||
if (event is XMPPStreamHeader) {
|
||||
streamId = event.attributes['id'];
|
||||
}
|
||||
}
|
||||
}
|
227
packages/moxxmpp/lib/src/handlers/client.dart
Normal file
@ -0,0 +1,227 @@
|
||||
import 'package:meta/meta.dart';
|
||||
import 'package:moxxmpp/src/connection_errors.dart';
|
||||
import 'package:moxxmpp/src/handlers/base.dart';
|
||||
import 'package:moxxmpp/src/jid.dart';
|
||||
import 'package:moxxmpp/src/namespaces.dart';
|
||||
import 'package:moxxmpp/src/negotiators/negotiator.dart';
|
||||
import 'package:moxxmpp/src/parser.dart';
|
||||
import 'package:moxxmpp/src/stringxml.dart';
|
||||
|
||||
/// "Nonza" describing the XMPP stream header of a client-to-server connection.
|
||||
class ClientStreamHeaderNonza extends XMLNode {
|
||||
ClientStreamHeaderNonza(JID jid)
|
||||
: super(
|
||||
tag: 'stream:stream',
|
||||
attributes: <String, String>{
|
||||
'xmlns': stanzaXmlns,
|
||||
'version': '1.0',
|
||||
'xmlns:stream': streamXmlns,
|
||||
'to': jid.domain,
|
||||
'from': jid.toBare().toString(),
|
||||
'xml:lang': 'en',
|
||||
},
|
||||
closeTag: false,
|
||||
);
|
||||
}
|
||||
|
||||
/// This class implements the stream feature negotiation for usage in client to server
|
||||
/// connections.
|
||||
class ClientToServerNegotiator extends NegotiationsHandler {
|
||||
ClientToServerNegotiator() : super();
|
||||
|
||||
/// Cached list of stream features.
|
||||
final List<XMLNode> _streamFeatures = List.empty(growable: true);
|
||||
|
||||
/// The currently active negotiator.
|
||||
XmppFeatureNegotiatorBase? _currentNegotiator;
|
||||
|
||||
@override
|
||||
String getStanzaNamespace() => stanzaXmlns;
|
||||
|
||||
@override
|
||||
void registerNegotiator(XmppFeatureNegotiatorBase negotiator) {
|
||||
negotiators[negotiator.id] = negotiator;
|
||||
}
|
||||
|
||||
@override
|
||||
void reset() {
|
||||
super.reset();
|
||||
|
||||
// Prevent leaking the last active negotiator
|
||||
_currentNegotiator = null;
|
||||
}
|
||||
|
||||
@override
|
||||
void removeNegotiatingFeature(String feature) {
|
||||
_streamFeatures.removeWhere((node) {
|
||||
return node.attributes['xmlns'] == feature;
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void sendStreamHeader() {
|
||||
resetStreamParser();
|
||||
sendNonza(
|
||||
XMLNode(
|
||||
tag: 'xml',
|
||||
attributes: {'version': '1.0'},
|
||||
closeTag: false,
|
||||
isDeclaration: true,
|
||||
children: [
|
||||
ClientStreamHeaderNonza(getConnectionSettings().jid),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// Returns true if all mandatory features in [features] have been negotiated.
|
||||
/// Otherwise returns false.
|
||||
bool _isMandatoryNegotiationDone(List<XMLNode> features) {
|
||||
return features.every((XMLNode feature) {
|
||||
return feature.firstTag('required') == null &&
|
||||
feature.tag != 'mechanisms';
|
||||
});
|
||||
}
|
||||
|
||||
/// Returns true if we can still negotiate. Returns false if no negotiator is
|
||||
/// matching and ready.
|
||||
bool _isNegotiationPossible(List<XMLNode> features) {
|
||||
return getNextNegotiator(features, log: false) != null;
|
||||
}
|
||||
|
||||
/// Returns the next negotiator that matches [features]. Returns null if none can be
|
||||
/// picked. If [log] is true, then the list of matching negotiators will be logged.
|
||||
@visibleForTesting
|
||||
XmppFeatureNegotiatorBase? getNextNegotiator(
|
||||
List<XMLNode> features, {
|
||||
bool log = true,
|
||||
}) {
|
||||
final matchingNegotiators =
|
||||
negotiators.values.where((XmppFeatureNegotiatorBase negotiator) {
|
||||
return negotiator.state == NegotiatorState.ready &&
|
||||
negotiator.matchesFeature(features);
|
||||
}).toList()
|
||||
..sort((a, b) => b.priority.compareTo(a.priority));
|
||||
|
||||
if (log) {
|
||||
this.log.finest(
|
||||
'List of matching negotiators: ${matchingNegotiators.map((a) => a.id)}',
|
||||
);
|
||||
}
|
||||
|
||||
if (matchingNegotiators.isEmpty) return null;
|
||||
|
||||
return matchingNegotiators.first;
|
||||
}
|
||||
|
||||
Future<void> _executeCurrentNegotiator(XMLNode nonza) async {
|
||||
// If we don't have a negotiator, get one
|
||||
_currentNegotiator ??= getNextNegotiator(_streamFeatures);
|
||||
if (_currentNegotiator == null &&
|
||||
_isMandatoryNegotiationDone(_streamFeatures) &&
|
||||
!_isNegotiationPossible(_streamFeatures)) {
|
||||
log.finest('Negotiations done!');
|
||||
await onNegotiationsDone();
|
||||
return;
|
||||
}
|
||||
|
||||
// If we don't have a next negotiator, we have to bail
|
||||
if (_currentNegotiator == null &&
|
||||
!_isMandatoryNegotiationDone(_streamFeatures) &&
|
||||
!_isNegotiationPossible(_streamFeatures)) {
|
||||
// We failed before authenticating
|
||||
if (!isAuthenticated()) {
|
||||
log.severe('No negotiator could be picked while unauthenticated');
|
||||
await handleError(NoMatchingAuthenticationMechanismAvailableError());
|
||||
return;
|
||||
} else {
|
||||
log.severe(
|
||||
'No negotiator could be picked while negotiations are not done',
|
||||
);
|
||||
await handleError(NoAuthenticatorAvailableError());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
final result = await _currentNegotiator!.negotiate(nonza);
|
||||
if (result.isType<NegotiatorError>()) {
|
||||
log.severe('Negotiator returned an error');
|
||||
await handleError(result.get<NegotiatorError>());
|
||||
return;
|
||||
}
|
||||
|
||||
final state = result.get<NegotiatorState>();
|
||||
_currentNegotiator!.state = state;
|
||||
switch (state) {
|
||||
case NegotiatorState.ready:
|
||||
return;
|
||||
case NegotiatorState.done:
|
||||
if (_currentNegotiator!.sendStreamHeaderWhenDone) {
|
||||
_currentNegotiator = null;
|
||||
_streamFeatures.clear();
|
||||
sendStreamHeader();
|
||||
} else {
|
||||
removeNegotiatingFeature(_currentNegotiator!.negotiatingXmlns);
|
||||
_currentNegotiator = null;
|
||||
|
||||
if (_isMandatoryNegotiationDone(_streamFeatures) &&
|
||||
!_isNegotiationPossible(_streamFeatures)) {
|
||||
log.finest('Negotiations done!');
|
||||
await onNegotiationsDone();
|
||||
} else {
|
||||
_currentNegotiator = getNextNegotiator(_streamFeatures);
|
||||
log.finest('Chose ${_currentNegotiator!.id} as next negotiator');
|
||||
|
||||
final fakeStanza = XMLNode(
|
||||
tag: 'stream:features',
|
||||
children: _streamFeatures,
|
||||
);
|
||||
|
||||
await _executeCurrentNegotiator(fakeStanza);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case NegotiatorState.retryLater:
|
||||
log.finest('Negotiator wants to continue later. Picking new one...');
|
||||
_currentNegotiator!.state = NegotiatorState.ready;
|
||||
|
||||
if (_isMandatoryNegotiationDone(_streamFeatures) &&
|
||||
!_isNegotiationPossible(_streamFeatures)) {
|
||||
log.finest('Negotiations done!');
|
||||
|
||||
await onNegotiationsDone();
|
||||
} else {
|
||||
log.finest('Picking new negotiator...');
|
||||
_currentNegotiator = getNextNegotiator(_streamFeatures);
|
||||
log.finest('Chose $_currentNegotiator as next negotiator');
|
||||
final fakeStanza = XMLNode(
|
||||
tag: 'stream:features',
|
||||
children: _streamFeatures,
|
||||
);
|
||||
await _executeCurrentNegotiator(fakeStanza);
|
||||
}
|
||||
break;
|
||||
case NegotiatorState.skipRest:
|
||||
log.finest(
|
||||
'Negotiator wants to skip the remaining negotiation... Negotiations (assumed) done!',
|
||||
);
|
||||
|
||||
await onNegotiationsDone();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> negotiate(XMPPStreamObject event) async {
|
||||
if (event is XMPPStreamElement) {
|
||||
if (event.node.tag == 'stream:features') {
|
||||
// Store the received stream features
|
||||
_streamFeatures
|
||||
..clear()
|
||||
..addAll(event.node.children);
|
||||
}
|
||||
|
||||
await _executeCurrentNegotiator(event.node);
|
||||
}
|
||||
}
|
||||
}
|
121
packages/moxxmpp/lib/src/handlers/component.dart
Normal file
@ -0,0 +1,121 @@
|
||||
import 'dart:convert';
|
||||
import 'package:cryptography/cryptography.dart';
|
||||
import 'package:hex/hex.dart';
|
||||
import 'package:moxxmpp/src/connection_errors.dart';
|
||||
import 'package:moxxmpp/src/handlers/base.dart';
|
||||
import 'package:moxxmpp/src/jid.dart';
|
||||
import 'package:moxxmpp/src/namespaces.dart';
|
||||
import 'package:moxxmpp/src/negotiators/negotiator.dart';
|
||||
import 'package:moxxmpp/src/parser.dart';
|
||||
import 'package:moxxmpp/src/stringxml.dart';
|
||||
|
||||
/// Nonza describing the XMPP stream header.
|
||||
class ComponentStreamHeaderNonza extends XMLNode {
|
||||
ComponentStreamHeaderNonza(JID jid)
|
||||
: assert(jid.isBare(), 'Component JID must be bare'),
|
||||
super(
|
||||
tag: 'stream:stream',
|
||||
attributes: <String, String>{
|
||||
'xmlns': componentAcceptXmlns,
|
||||
'xmlns:stream': streamXmlns,
|
||||
'to': jid.domain,
|
||||
},
|
||||
closeTag: false,
|
||||
);
|
||||
}
|
||||
|
||||
/// The states the ComponentToServerNegotiator can be in.
|
||||
enum ComponentToServerState {
|
||||
/// No data has been sent or received yet
|
||||
idle,
|
||||
|
||||
/// Handshake has been sent
|
||||
handshakeSent,
|
||||
}
|
||||
|
||||
/// The ComponentToServerNegotiator is a NegotiationsHandler that allows writing
|
||||
/// components that adhere to XEP-0114.
|
||||
class ComponentToServerNegotiator extends NegotiationsHandler {
|
||||
ComponentToServerNegotiator();
|
||||
|
||||
/// The state the negotiation handler is currently in
|
||||
ComponentToServerState _state = ComponentToServerState.idle;
|
||||
|
||||
@override
|
||||
String getStanzaNamespace() => componentAcceptXmlns;
|
||||
|
||||
@override
|
||||
void registerNegotiator(XmppFeatureNegotiatorBase negotiator) {}
|
||||
|
||||
@override
|
||||
void sendStreamHeader() {
|
||||
resetStreamParser();
|
||||
sendNonza(
|
||||
XMLNode(
|
||||
tag: 'xml',
|
||||
attributes: {'version': '1.0'},
|
||||
closeTag: false,
|
||||
isDeclaration: true,
|
||||
children: [
|
||||
ComponentStreamHeaderNonza(getConnectionSettings().jid),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<String> _computeHandshake(String id) async {
|
||||
final secret = getConnectionSettings().password;
|
||||
return HEX.encode(
|
||||
(await Sha1().hash(utf8.encode('$streamId$secret'))).bytes,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> negotiate(XMPPStreamObject event) async {
|
||||
switch (_state) {
|
||||
case ComponentToServerState.idle:
|
||||
if (event is XMPPStreamHeader) {
|
||||
streamId = event.attributes['id'];
|
||||
assert(
|
||||
streamId != null,
|
||||
'The server must respond with a stream header that contains an id',
|
||||
);
|
||||
|
||||
_state = ComponentToServerState.handshakeSent;
|
||||
sendNonza(
|
||||
XMLNode(
|
||||
tag: 'handshake',
|
||||
text: await _computeHandshake(streamId!),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
log.severe('Unexpected data received');
|
||||
await handleError(UnexpectedDataError());
|
||||
}
|
||||
break;
|
||||
case ComponentToServerState.handshakeSent:
|
||||
if (event is XMPPStreamElement) {
|
||||
if (event.node.tag == 'handshake' &&
|
||||
event.node.children.isEmpty &&
|
||||
event.node.attributes.isEmpty) {
|
||||
log.info('Successfully authenticated as component');
|
||||
await onNegotiationsDone();
|
||||
} else {
|
||||
log.warning('Handshake failed');
|
||||
await handleError(InvalidHandshakeCredentialsError());
|
||||
}
|
||||
} else {
|
||||
log.severe('Unexpected data received');
|
||||
await handleError(UnexpectedDataError());
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void reset() {
|
||||
_state = ComponentToServerState.idle;
|
||||
|
||||
super.reset();
|
||||
}
|
||||
}
|
@ -1,10 +1,33 @@
|
||||
import 'package:moxxmpp/src/connection.dart';
|
||||
import 'package:moxxmpp/src/managers/data.dart';
|
||||
import 'package:moxxmpp/src/stanza.dart';
|
||||
|
||||
bool handleUnhandledStanza(XmppConnection conn, Stanza stanza) {
|
||||
if (stanza.type != 'error' && stanza.type != 'result') {
|
||||
conn.sendStanza(stanza.errorReply('cancel', 'feature-not-implemented'));
|
||||
}
|
||||
/// Bounce a stanza if it was not handled by any manager. [conn] is the connection object
|
||||
/// to use for sending the stanza. [data] is the StanzaHandlerData of the unhandled
|
||||
/// stanza.
|
||||
Future<void> handleUnhandledStanza(
|
||||
XmppConnection conn,
|
||||
StanzaHandlerData data,
|
||||
) async {
|
||||
if (data.stanza.type != 'error' && data.stanza.type != 'result') {
|
||||
final stanza = data.stanza.copyWith(
|
||||
to: data.stanza.from,
|
||||
from: data.stanza.to,
|
||||
type: 'error',
|
||||
children: [
|
||||
buildErrorElement(
|
||||
'cancel',
|
||||
'feature-not-implemented',
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
return true;
|
||||
await conn.sendStanza(
|
||||
StanzaDetails(
|
||||
stanza,
|
||||
awaitable: false,
|
||||
forceEncryption: data.encrypted,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -1,83 +1,82 @@
|
||||
import 'package:meta/meta.dart';
|
||||
|
||||
/// Represents a Jabber ID in parsed form.
|
||||
@immutable
|
||||
class JID {
|
||||
|
||||
const JID(this.local, this.domain, this.resource);
|
||||
|
||||
/// Parses the string [jid] into a JID instance.
|
||||
factory JID.fromString(String jid) {
|
||||
// 0: Parsing either the local or domain part
|
||||
// 1: Parsing the domain part
|
||||
// 2: Parsing the resource
|
||||
var state = 0;
|
||||
var buffer = '';
|
||||
var local_ = '';
|
||||
var domain_ = '';
|
||||
var resource_ = '';
|
||||
|
||||
for (var i = 0; i < jid.length; i++) {
|
||||
final c = jid[i];
|
||||
final eol = i == jid.length - 1;
|
||||
|
||||
switch (state) {
|
||||
case 0: {
|
||||
if (c == '@') {
|
||||
local_ = buffer;
|
||||
buffer = '';
|
||||
state = 1;
|
||||
} else if (c == '/') {
|
||||
domain_ = buffer;
|
||||
buffer = '';
|
||||
state = 2;
|
||||
} else if (eol) {
|
||||
domain_ = buffer + c;
|
||||
} else {
|
||||
buffer += c;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 1: {
|
||||
if (c == '/') {
|
||||
domain_ = buffer;
|
||||
buffer = '';
|
||||
state = 2;
|
||||
} else if (eol) {
|
||||
domain_ = buffer;
|
||||
// Algorithm taken from here: https://blog.samwhited.com/2021/02/xmpp-addresses/
|
||||
var localPart = '';
|
||||
var domainPart = '';
|
||||
var resourcePart = '';
|
||||
|
||||
if (c != ' ') {
|
||||
domain_ = domain_ + c;
|
||||
}
|
||||
} else if (c != ' ') {
|
||||
buffer += c;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 2: {
|
||||
if (eol) {
|
||||
resource_ = buffer;
|
||||
final slashParts = jid.split('/');
|
||||
if (slashParts.length == 1) {
|
||||
resourcePart = '';
|
||||
} else {
|
||||
resourcePart = slashParts.sublist(1).join('/');
|
||||
|
||||
if (c != ' ') {
|
||||
resource_ = resource_ + c;
|
||||
}
|
||||
} else if (c != ''){
|
||||
buffer += c;
|
||||
}
|
||||
}
|
||||
}
|
||||
assert(
|
||||
resourcePart.isNotEmpty,
|
||||
'Resource part cannot be there and empty',
|
||||
);
|
||||
}
|
||||
|
||||
return JID(local_, domain_, resource_);
|
||||
final atParts = slashParts.first.split('@');
|
||||
if (atParts.length == 1) {
|
||||
localPart = '';
|
||||
domainPart = atParts.first;
|
||||
} else {
|
||||
localPart = atParts.first;
|
||||
domainPart = atParts.sublist(1).join('@');
|
||||
|
||||
assert(localPart.isNotEmpty, 'Local part cannot be there and empty');
|
||||
}
|
||||
|
||||
return JID(
|
||||
localPart,
|
||||
domainPart.endsWith('.')
|
||||
? domainPart.substring(0, domainPart.length - 1)
|
||||
: domainPart,
|
||||
resourcePart,
|
||||
);
|
||||
}
|
||||
final String local;
|
||||
final String domain;
|
||||
final String resource;
|
||||
|
||||
/// Returns true if the JID is bare.
|
||||
bool isBare() => resource.isEmpty;
|
||||
|
||||
/// Returns true if the JID is full.
|
||||
bool isFull() => resource.isNotEmpty;
|
||||
|
||||
JID toBare() => JID(local, domain, '');
|
||||
/// Converts the JID into a bare JID.
|
||||
JID toBare() {
|
||||
return JID(local, domain, '');
|
||||
}
|
||||
|
||||
/// Converts the JID into one with a resource part of [resource].
|
||||
JID withResource(String resource) => JID(local, domain, resource);
|
||||
|
||||
|
||||
/// Convert the JID into the JID of the domain. For example, converts alice@example.org/abc123 to example.org.
|
||||
JID toDomain() {
|
||||
return JID('', domain, '');
|
||||
}
|
||||
|
||||
/// Compares the JID with [other]. This function assumes that JID and [other]
|
||||
/// are bare, i.e. only the domain- and localparts are compared. If [ensureBare]
|
||||
/// is optionally set to true, then [other] MUST be bare. Otherwise, false is returned.
|
||||
bool bareCompare(JID other, {bool ensureBare = false}) {
|
||||
if (ensureBare && !other.isBare()) return false;
|
||||
|
||||
return local == other.local && domain == other.domain;
|
||||
}
|
||||
|
||||
/// Converts to JID instance into its string representation of
|
||||
/// localpart@domainpart/resource.
|
||||
@override
|
||||
String toString() {
|
||||
var result = '';
|
||||
@ -97,7 +96,9 @@ class JID {
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
if (other is JID) {
|
||||
return other.local == local && other.domain == domain && other.resource == resource;
|
||||
return other.local == local &&
|
||||
other.domain == domain &&
|
||||
other.resource == resource;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
@ -1,5 +1,4 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:moxxmpp/src/connection.dart';
|
||||
import 'package:moxxmpp/src/events.dart';
|
||||
import 'package:moxxmpp/src/jid.dart';
|
||||
@ -11,21 +10,20 @@ import 'package:moxxmpp/src/stanza.dart';
|
||||
import 'package:moxxmpp/src/stringxml.dart';
|
||||
|
||||
class XmppManagerAttributes {
|
||||
|
||||
XmppManagerAttributes({
|
||||
required this.sendStanza,
|
||||
required this.sendNonza,
|
||||
required this.getManagerById,
|
||||
required this.sendEvent,
|
||||
required this.getConnectionSettings,
|
||||
required this.isFeatureSupported,
|
||||
required this.getFullJID,
|
||||
required this.getSocket,
|
||||
required this.getConnection,
|
||||
required this.getNegotiatorById,
|
||||
});
|
||||
|
||||
/// Send a stanza whose response can be awaited.
|
||||
final Future<XMLNode> Function(Stanza stanza, { StanzaFromType addFrom, bool addId, bool awaitable, bool encrypted}) sendStanza;
|
||||
final Future<XMLNode?> Function(StanzaDetails) sendStanza;
|
||||
|
||||
/// Send a nonza.
|
||||
final void Function(XMLNode) sendNonza;
|
||||
@ -39,9 +37,6 @@ class XmppManagerAttributes {
|
||||
/// (Maybe) Get a Manager attached to the connection by its Id.
|
||||
final T? Function<T extends XmppManagerBase>(String) getManagerById;
|
||||
|
||||
/// Returns true if a server feature is supported
|
||||
final bool Function(String) isFeatureSupported;
|
||||
|
||||
/// Returns the full JID of the current account
|
||||
final JID Function() getFullJID;
|
||||
|
||||
@ -51,5 +46,6 @@ class XmppManagerAttributes {
|
||||
/// Return the [XmppConnection] the manager is registered against.
|
||||
final XmppConnection Function() getConnection;
|
||||
|
||||
final T? Function<T extends XmppFeatureNegotiatorBase>(String) getNegotiatorById;
|
||||
final T? Function<T extends XmppFeatureNegotiatorBase>(String)
|
||||
getNegotiatorById;
|
||||
}
|
||||
|
@ -1,72 +1,178 @@
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:meta/meta.dart';
|
||||
import 'package:moxxmpp/src/events.dart';
|
||||
import 'package:moxxmpp/src/managers/attributes.dart';
|
||||
import 'package:moxxmpp/src/managers/data.dart';
|
||||
import 'package:moxxmpp/src/managers/handlers.dart';
|
||||
import 'package:moxxmpp/src/managers/namespaces.dart';
|
||||
import 'package:moxxmpp/src/stanza.dart';
|
||||
import 'package:moxxmpp/src/stringxml.dart';
|
||||
import 'package:moxxmpp/src/xeps/xep_0030/errors.dart';
|
||||
import 'package:moxxmpp/src/xeps/xep_0030/types.dart';
|
||||
import 'package:moxxmpp/src/xeps/xep_0030/xep_0030.dart';
|
||||
import 'package:moxxmpp/src/xeps/xep_0198/xep_0198.dart';
|
||||
|
||||
abstract class XmppManagerBase {
|
||||
XmppManagerBase(this.id);
|
||||
|
||||
late final XmppManagerAttributes _managerAttributes;
|
||||
late final Logger _log;
|
||||
|
||||
/// Flag indicating that the post registration callback has been called once.
|
||||
bool initialized = false;
|
||||
|
||||
/// Registers the callbacks from XmppConnection with the manager
|
||||
void register(XmppManagerAttributes attributes) {
|
||||
_managerAttributes = attributes;
|
||||
_log = Logger(getName());
|
||||
_log = Logger(name);
|
||||
}
|
||||
|
||||
|
||||
/// Returns the attributes that are registered with the manager.
|
||||
/// Must only be called after register has been called on it.
|
||||
XmppManagerAttributes getAttributes() {
|
||||
return _managerAttributes;
|
||||
}
|
||||
|
||||
/// Resolves to true when the server supports the disco feature [xmlns]. Resolves
|
||||
/// to false when either the disco request fails or the server does not
|
||||
/// support [xmlns].
|
||||
/// Note that this function requires a registered DiscoManager.
|
||||
@protected
|
||||
Future<bool> isFeatureSupported(String xmlns) async {
|
||||
final dm = _managerAttributes.getManagerById<DiscoManager>(discoManager);
|
||||
assert(
|
||||
dm != null,
|
||||
'The DiscoManager must be registered for isFeatureSupported to work',
|
||||
);
|
||||
|
||||
final result = await dm!.discoInfoQuery(
|
||||
_managerAttributes.getConnectionSettings().jid.toDomain(),
|
||||
);
|
||||
if (result.isType<DiscoError>()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return result.get<DiscoInfo>().features.contains(xmlns);
|
||||
}
|
||||
|
||||
/// Return the StanzaHandlers associated with this manager that deal with stanzas we
|
||||
/// send. These are run before the stanza is sent.
|
||||
/// send. These are run before the stanza is sent. The higher the value of the
|
||||
/// handler's priority, the earlier it is run.
|
||||
List<StanzaHandler> getOutgoingPreStanzaHandlers() => [];
|
||||
|
||||
/// Return the StanzaHandlers associated with this manager that deal with stanzas we
|
||||
/// send. These are run after the stanza is sent.
|
||||
/// send. These are run after the stanza is sent. The higher the value of the
|
||||
/// handler's priority, the earlier it is run.
|
||||
List<StanzaHandler> getOutgoingPostStanzaHandlers() => [];
|
||||
|
||||
|
||||
/// Return the StanzaHandlers associated with this manager that deal with stanzas we
|
||||
/// receive.
|
||||
/// receive. The higher the value of the
|
||||
/// handler's priority, the earlier it is run.
|
||||
List<StanzaHandler> getIncomingStanzaHandlers() => [];
|
||||
|
||||
/// Return the NonzaHandlers associated with this manager.
|
||||
|
||||
/// Return the StanzaHandlers associated with this manager that deal with stanza handlers
|
||||
/// that have to run before the main ones run. This is useful, for example, for OMEMO
|
||||
/// as we have to decrypt the stanza before we do anything else. The higher the value
|
||||
/// of the handler's priority, the earlier it is run.
|
||||
List<StanzaHandler> getIncomingPreStanzaHandlers() => [];
|
||||
|
||||
/// Return the NonzaHandlers associated with this manager. The higher the value of the
|
||||
/// handler's priority, the earlier it is run.
|
||||
List<NonzaHandler> getNonzaHandlers() => [];
|
||||
|
||||
/// Whenever the socket receives data, this method is called, if it is non-null.
|
||||
Future<void> onData() async {}
|
||||
|
||||
/// Return a list of features that should be included in a disco response.
|
||||
List<String> getDiscoFeatures() => [];
|
||||
|
||||
/// Return the Id (akin to xmlns) of this manager.
|
||||
String getId();
|
||||
|
||||
/// Return a name that will be used for logging.
|
||||
String getName();
|
||||
/// Return a list of identities that should be included in a disco response.
|
||||
List<Identity> getDiscoIdentities() => [];
|
||||
|
||||
/// Return the Id (akin to xmlns) of this manager.
|
||||
final String id;
|
||||
|
||||
/// The name of the manager.
|
||||
String get name => toString();
|
||||
|
||||
/// Return the logger for this manager.
|
||||
Logger get logger => _log;
|
||||
|
||||
|
||||
/// Called when XmppConnection triggers an event
|
||||
Future<void> onXmppEvent(XmppEvent event) async {}
|
||||
|
||||
/// Returns true if the XEP is supported on the server. If not, returns false
|
||||
Future<bool> isSupported();
|
||||
|
||||
|
||||
/// Called after the registration of all managers against the XmppConnection is done.
|
||||
/// This method is only called once during the entire lifetime of it.
|
||||
@mustCallSuper
|
||||
Future<void> postRegisterCallback() async {
|
||||
initialized = true;
|
||||
|
||||
final disco = getAttributes().getManagerById<DiscoManager>(discoManager);
|
||||
if (disco != null) {
|
||||
if (getDiscoFeatures().isNotEmpty) {
|
||||
disco.addFeatures(getDiscoFeatures());
|
||||
}
|
||||
|
||||
if (getDiscoIdentities().isNotEmpty) {
|
||||
disco.addIdentities(getDiscoIdentities());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Runs all NonzaHandlers of this Manager which match the nonza. Resolves to true if
|
||||
/// the nonza has been handled by one of the handlers. Resolves to false otherwise.
|
||||
Future<bool> runNonzaHandlers(XMLNode nonza) async {
|
||||
var handled = false;
|
||||
await Future.forEach(
|
||||
getNonzaHandlers(),
|
||||
(NonzaHandler handler) async {
|
||||
if (handler.matches(nonza)) {
|
||||
handled = true;
|
||||
await handler.callback(nonza);
|
||||
}
|
||||
await Future.forEach(getNonzaHandlers(), (NonzaHandler handler) async {
|
||||
if (handler.matches(nonza)) {
|
||||
handled = true;
|
||||
await handler.callback(nonza);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
return handled;
|
||||
}
|
||||
|
||||
/// Returns true, if the current stream negotiations resulted in a new stream. Useful
|
||||
/// for plugins to reset their cache in case of a new stream.
|
||||
/// The value only makes sense after receiving a StreamNegotiationsDoneEvent.
|
||||
Future<bool> isNewStream() async {
|
||||
final sm =
|
||||
getAttributes().getManagerById<StreamManagementManager>(smManager);
|
||||
|
||||
return sm?.streamResumed == false;
|
||||
}
|
||||
|
||||
/// Sends a reply of the stanza in [data] with [type]. Replaces the original stanza's
|
||||
/// children with [children].
|
||||
///
|
||||
/// Note that this function currently only accepts IQ stanzas.
|
||||
Future<void> reply(
|
||||
StanzaHandlerData data,
|
||||
String type,
|
||||
List<XMLNode> children,
|
||||
) async {
|
||||
assert(
|
||||
data.stanza.tag == 'iq',
|
||||
'Reply makes little sense for non-IQ stanzas',
|
||||
);
|
||||
|
||||
final stanza = data.stanza.copyWith(
|
||||
to: data.stanza.from,
|
||||
from: data.stanza.to,
|
||||
type: type,
|
||||
children: children,
|
||||
);
|
||||
|
||||
await getAttributes().sendStanza(
|
||||
StanzaDetails(
|
||||
stanza,
|
||||
awaitable: false,
|
||||
forceEncryption: data.encrypted,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -1,66 +1,59 @@
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
import 'package:moxxmpp/src/stanza.dart';
|
||||
import 'package:moxxmpp/src/xeps/xep_0066.dart';
|
||||
import 'package:moxxmpp/src/xeps/xep_0085.dart';
|
||||
import 'package:moxxmpp/src/xeps/xep_0203.dart';
|
||||
import 'package:moxxmpp/src/xeps/xep_0359.dart';
|
||||
import 'package:moxxmpp/src/xeps/xep_0380.dart';
|
||||
import 'package:moxxmpp/src/xeps/xep_0385.dart';
|
||||
import 'package:moxxmpp/src/xeps/xep_0424.dart';
|
||||
import 'package:moxxmpp/src/xeps/xep_0446.dart';
|
||||
import 'package:moxxmpp/src/xeps/xep_0447.dart';
|
||||
import 'package:moxxmpp/src/xeps/xep_0461.dart';
|
||||
import 'package:moxxmpp/src/util/typed_map.dart';
|
||||
|
||||
part 'data.freezed.dart';
|
||||
abstract class StanzaHandlerExtension {}
|
||||
|
||||
@freezed
|
||||
class StanzaHandlerData with _$StanzaHandlerData {
|
||||
factory StanzaHandlerData(
|
||||
// Indicates to the runner that processing is now done. This means that all
|
||||
// pre-processing is done and no other handlers should be consulted.
|
||||
bool done,
|
||||
// Indicates to the runner that processing is to be cancelled and no further handlers
|
||||
// should run. The stanza also will not be sent.
|
||||
bool cancel,
|
||||
// The reason why we cancelled the processing and sending
|
||||
dynamic cancelReason,
|
||||
// The stanza that is being dealt with. SHOULD NOT be overwritten, unless it is absolutely
|
||||
// necessary, e.g. with Message Carbons or OMEMO
|
||||
Stanza stanza,
|
||||
{
|
||||
// Whether the stanza is retransmitted. Only useful in the context of outgoing
|
||||
// stanza handlers. MUST NOT be overwritten.
|
||||
@Default(false) bool retransmitted,
|
||||
StatelessMediaSharingData? sims,
|
||||
StatelessFileSharingData? sfs,
|
||||
OOBData? oob,
|
||||
StableStanzaId? stableId,
|
||||
ReplyData? reply,
|
||||
ChatState? chatState,
|
||||
@Default(false) bool isCarbon,
|
||||
@Default(false) bool deliveryReceiptRequested,
|
||||
@Default(false) bool isMarkable,
|
||||
// File Upload Notifications
|
||||
// A notification
|
||||
FileMetadataData? fun,
|
||||
// The stanza id this replaces
|
||||
String? funReplacement,
|
||||
// The stanza id this cancels
|
||||
String? funCancellation,
|
||||
// Whether the stanza was received encrypted
|
||||
@Default(false) bool encrypted,
|
||||
// The stated type of encryption used, if any was used
|
||||
ExplicitEncryptionType? encryptionType,
|
||||
// Delayed Delivery
|
||||
DelayedDelivery? delayedDelivery,
|
||||
// This is for stanza handlers that are not part of the XMPP library but still need
|
||||
// pass data around.
|
||||
@Default(<String, dynamic>{}) Map<String, dynamic> other,
|
||||
// If non-null, then it indicates the origin Id of the message that should be
|
||||
// retracted
|
||||
MessageRetractionData? messageRetraction,
|
||||
// If non-null, then the message is a correction for the specified stanza Id
|
||||
String? lastMessageCorrectionSid,
|
||||
}
|
||||
) = _StanzaHandlerData;
|
||||
class StanzaHandlerData {
|
||||
StanzaHandlerData(
|
||||
this.done,
|
||||
this.cancel,
|
||||
this.stanza,
|
||||
this.extensions, {
|
||||
this.cancelReason,
|
||||
this.encryptionError,
|
||||
this.encrypted = false,
|
||||
this.forceEncryption = false,
|
||||
this.shouldEncrypt = true,
|
||||
this.skip = false,
|
||||
});
|
||||
|
||||
/// Indicates to the runner that processing is now done. This means that all
|
||||
/// pre-processing is done and no other handlers should be consulted.
|
||||
bool done;
|
||||
|
||||
/// Only useful in combination with [done] = true: When [skip] is set to true and
|
||||
/// this [StanzaHandlerData] object is returned from a IncomingPreStanzaHandler, then
|
||||
/// moxxmpp will skip checking whether the stanza was awaited and will not run any actual
|
||||
/// IncomingStanzaHandler callbacks.
|
||||
bool skip;
|
||||
|
||||
/// Indicates to the runner that processing is to be cancelled and no further handlers
|
||||
/// should run. The stanza also will not be sent.
|
||||
bool cancel;
|
||||
|
||||
/// The reason why we cancelled the processing and sending.
|
||||
Object? cancelReason;
|
||||
|
||||
/// The reason why an encryption or decryption failed.
|
||||
Object? encryptionError;
|
||||
|
||||
/// The stanza that is being dealt with. SHOULD NOT be overwritten, unless it is
|
||||
/// absolutely necessary, e.g. with Message Carbons or OMEMO.
|
||||
Stanza stanza;
|
||||
|
||||
/// Whether the stanza is already encrypted
|
||||
bool encrypted;
|
||||
|
||||
// If true, forces the encryption manager to encrypt to the JID, even if it
|
||||
// would not normally. In the case of OMEMO: If shouldEncrypt returns false
|
||||
// but forceEncryption is true, then the OMEMO manager will try to encrypt
|
||||
// to the JID anyway.
|
||||
bool forceEncryption;
|
||||
|
||||
/// Flag indicating whether a E2EE implementation should encrypt the stanza (true)
|
||||
/// or not (false).
|
||||
bool shouldEncrypt;
|
||||
|
||||
/// Additional data from other managers.
|
||||
final TypedMap<StanzaHandlerExtension> extensions;
|
||||
}
|
||||
|
@ -1,665 +0,0 @@
|
||||
// coverage:ignore-file
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
// ignore_for_file: type=lint
|
||||
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target
|
||||
|
||||
part of 'data.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// FreezedGenerator
|
||||
// **************************************************************************
|
||||
|
||||
T _$identity<T>(T value) => value;
|
||||
|
||||
final _privateConstructorUsedError = UnsupportedError(
|
||||
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#custom-getters-and-methods');
|
||||
|
||||
/// @nodoc
|
||||
mixin _$StanzaHandlerData {
|
||||
// Indicates to the runner that processing is now done. This means that all
|
||||
// pre-processing is done and no other handlers should be consulted.
|
||||
bool get done =>
|
||||
throw _privateConstructorUsedError; // Indicates to the runner that processing is to be cancelled and no further handlers
|
||||
// should run. The stanza also will not be sent.
|
||||
bool get cancel =>
|
||||
throw _privateConstructorUsedError; // The reason why we cancelled the processing and sending
|
||||
dynamic get cancelReason =>
|
||||
throw _privateConstructorUsedError; // The stanza that is being dealt with. SHOULD NOT be overwritten, unless it is absolutely
|
||||
// necessary, e.g. with Message Carbons or OMEMO
|
||||
Stanza get stanza =>
|
||||
throw _privateConstructorUsedError; // Whether the stanza is retransmitted. Only useful in the context of outgoing
|
||||
// stanza handlers. MUST NOT be overwritten.
|
||||
bool get retransmitted => throw _privateConstructorUsedError;
|
||||
StatelessMediaSharingData? get sims => throw _privateConstructorUsedError;
|
||||
StatelessFileSharingData? get sfs => throw _privateConstructorUsedError;
|
||||
OOBData? get oob => throw _privateConstructorUsedError;
|
||||
StableStanzaId? get stableId => throw _privateConstructorUsedError;
|
||||
ReplyData? get reply => throw _privateConstructorUsedError;
|
||||
ChatState? get chatState => throw _privateConstructorUsedError;
|
||||
bool get isCarbon => throw _privateConstructorUsedError;
|
||||
bool get deliveryReceiptRequested => throw _privateConstructorUsedError;
|
||||
bool get isMarkable =>
|
||||
throw _privateConstructorUsedError; // File Upload Notifications
|
||||
// A notification
|
||||
FileMetadataData? get fun =>
|
||||
throw _privateConstructorUsedError; // The stanza id this replaces
|
||||
String? get funReplacement =>
|
||||
throw _privateConstructorUsedError; // The stanza id this cancels
|
||||
String? get funCancellation =>
|
||||
throw _privateConstructorUsedError; // Whether the stanza was received encrypted
|
||||
bool get encrypted =>
|
||||
throw _privateConstructorUsedError; // The stated type of encryption used, if any was used
|
||||
ExplicitEncryptionType? get encryptionType =>
|
||||
throw _privateConstructorUsedError; // Delayed Delivery
|
||||
DelayedDelivery? get delayedDelivery =>
|
||||
throw _privateConstructorUsedError; // This is for stanza handlers that are not part of the XMPP library but still need
|
||||
// pass data around.
|
||||
Map<String, dynamic> get other =>
|
||||
throw _privateConstructorUsedError; // If non-null, then it indicates the origin Id of the message that should be
|
||||
// retracted
|
||||
MessageRetractionData? get messageRetraction =>
|
||||
throw _privateConstructorUsedError; // If non-null, then the message is a correction for the specified stanza Id
|
||||
String? get lastMessageCorrectionSid => throw _privateConstructorUsedError;
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
$StanzaHandlerDataCopyWith<StanzaHandlerData> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class $StanzaHandlerDataCopyWith<$Res> {
|
||||
factory $StanzaHandlerDataCopyWith(
|
||||
StanzaHandlerData value, $Res Function(StanzaHandlerData) then) =
|
||||
_$StanzaHandlerDataCopyWithImpl<$Res>;
|
||||
$Res call(
|
||||
{bool done,
|
||||
bool cancel,
|
||||
dynamic cancelReason,
|
||||
Stanza stanza,
|
||||
bool retransmitted,
|
||||
StatelessMediaSharingData? sims,
|
||||
StatelessFileSharingData? sfs,
|
||||
OOBData? oob,
|
||||
StableStanzaId? stableId,
|
||||
ReplyData? reply,
|
||||
ChatState? chatState,
|
||||
bool isCarbon,
|
||||
bool deliveryReceiptRequested,
|
||||
bool isMarkable,
|
||||
FileMetadataData? fun,
|
||||
String? funReplacement,
|
||||
String? funCancellation,
|
||||
bool encrypted,
|
||||
ExplicitEncryptionType? encryptionType,
|
||||
DelayedDelivery? delayedDelivery,
|
||||
Map<String, dynamic> other,
|
||||
MessageRetractionData? messageRetraction,
|
||||
String? lastMessageCorrectionSid});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class _$StanzaHandlerDataCopyWithImpl<$Res>
|
||||
implements $StanzaHandlerDataCopyWith<$Res> {
|
||||
_$StanzaHandlerDataCopyWithImpl(this._value, this._then);
|
||||
|
||||
final StanzaHandlerData _value;
|
||||
// ignore: unused_field
|
||||
final $Res Function(StanzaHandlerData) _then;
|
||||
|
||||
@override
|
||||
$Res call({
|
||||
Object? done = freezed,
|
||||
Object? cancel = freezed,
|
||||
Object? cancelReason = freezed,
|
||||
Object? stanza = freezed,
|
||||
Object? retransmitted = freezed,
|
||||
Object? sims = freezed,
|
||||
Object? sfs = freezed,
|
||||
Object? oob = freezed,
|
||||
Object? stableId = freezed,
|
||||
Object? reply = freezed,
|
||||
Object? chatState = freezed,
|
||||
Object? isCarbon = freezed,
|
||||
Object? deliveryReceiptRequested = freezed,
|
||||
Object? isMarkable = freezed,
|
||||
Object? fun = freezed,
|
||||
Object? funReplacement = freezed,
|
||||
Object? funCancellation = freezed,
|
||||
Object? encrypted = freezed,
|
||||
Object? encryptionType = freezed,
|
||||
Object? delayedDelivery = freezed,
|
||||
Object? other = freezed,
|
||||
Object? messageRetraction = freezed,
|
||||
Object? lastMessageCorrectionSid = freezed,
|
||||
}) {
|
||||
return _then(_value.copyWith(
|
||||
done: done == freezed
|
||||
? _value.done
|
||||
: done // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
cancel: cancel == freezed
|
||||
? _value.cancel
|
||||
: cancel // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
cancelReason: cancelReason == freezed
|
||||
? _value.cancelReason
|
||||
: cancelReason // ignore: cast_nullable_to_non_nullable
|
||||
as dynamic,
|
||||
stanza: stanza == freezed
|
||||
? _value.stanza
|
||||
: stanza // ignore: cast_nullable_to_non_nullable
|
||||
as Stanza,
|
||||
retransmitted: retransmitted == freezed
|
||||
? _value.retransmitted
|
||||
: retransmitted // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
sims: sims == freezed
|
||||
? _value.sims
|
||||
: sims // ignore: cast_nullable_to_non_nullable
|
||||
as StatelessMediaSharingData?,
|
||||
sfs: sfs == freezed
|
||||
? _value.sfs
|
||||
: sfs // ignore: cast_nullable_to_non_nullable
|
||||
as StatelessFileSharingData?,
|
||||
oob: oob == freezed
|
||||
? _value.oob
|
||||
: oob // ignore: cast_nullable_to_non_nullable
|
||||
as OOBData?,
|
||||
stableId: stableId == freezed
|
||||
? _value.stableId
|
||||
: stableId // ignore: cast_nullable_to_non_nullable
|
||||
as StableStanzaId?,
|
||||
reply: reply == freezed
|
||||
? _value.reply
|
||||
: reply // ignore: cast_nullable_to_non_nullable
|
||||
as ReplyData?,
|
||||
chatState: chatState == freezed
|
||||
? _value.chatState
|
||||
: chatState // ignore: cast_nullable_to_non_nullable
|
||||
as ChatState?,
|
||||
isCarbon: isCarbon == freezed
|
||||
? _value.isCarbon
|
||||
: isCarbon // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
deliveryReceiptRequested: deliveryReceiptRequested == freezed
|
||||
? _value.deliveryReceiptRequested
|
||||
: deliveryReceiptRequested // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
isMarkable: isMarkable == freezed
|
||||
? _value.isMarkable
|
||||
: isMarkable // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
fun: fun == freezed
|
||||
? _value.fun
|
||||
: fun // ignore: cast_nullable_to_non_nullable
|
||||
as FileMetadataData?,
|
||||
funReplacement: funReplacement == freezed
|
||||
? _value.funReplacement
|
||||
: funReplacement // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
funCancellation: funCancellation == freezed
|
||||
? _value.funCancellation
|
||||
: funCancellation // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
encrypted: encrypted == freezed
|
||||
? _value.encrypted
|
||||
: encrypted // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
encryptionType: encryptionType == freezed
|
||||
? _value.encryptionType
|
||||
: encryptionType // ignore: cast_nullable_to_non_nullable
|
||||
as ExplicitEncryptionType?,
|
||||
delayedDelivery: delayedDelivery == freezed
|
||||
? _value.delayedDelivery
|
||||
: delayedDelivery // ignore: cast_nullable_to_non_nullable
|
||||
as DelayedDelivery?,
|
||||
other: other == freezed
|
||||
? _value.other
|
||||
: other // ignore: cast_nullable_to_non_nullable
|
||||
as Map<String, dynamic>,
|
||||
messageRetraction: messageRetraction == freezed
|
||||
? _value.messageRetraction
|
||||
: messageRetraction // ignore: cast_nullable_to_non_nullable
|
||||
as MessageRetractionData?,
|
||||
lastMessageCorrectionSid: lastMessageCorrectionSid == freezed
|
||||
? _value.lastMessageCorrectionSid
|
||||
: lastMessageCorrectionSid // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class _$$_StanzaHandlerDataCopyWith<$Res>
|
||||
implements $StanzaHandlerDataCopyWith<$Res> {
|
||||
factory _$$_StanzaHandlerDataCopyWith(_$_StanzaHandlerData value,
|
||||
$Res Function(_$_StanzaHandlerData) then) =
|
||||
__$$_StanzaHandlerDataCopyWithImpl<$Res>;
|
||||
@override
|
||||
$Res call(
|
||||
{bool done,
|
||||
bool cancel,
|
||||
dynamic cancelReason,
|
||||
Stanza stanza,
|
||||
bool retransmitted,
|
||||
StatelessMediaSharingData? sims,
|
||||
StatelessFileSharingData? sfs,
|
||||
OOBData? oob,
|
||||
StableStanzaId? stableId,
|
||||
ReplyData? reply,
|
||||
ChatState? chatState,
|
||||
bool isCarbon,
|
||||
bool deliveryReceiptRequested,
|
||||
bool isMarkable,
|
||||
FileMetadataData? fun,
|
||||
String? funReplacement,
|
||||
String? funCancellation,
|
||||
bool encrypted,
|
||||
ExplicitEncryptionType? encryptionType,
|
||||
DelayedDelivery? delayedDelivery,
|
||||
Map<String, dynamic> other,
|
||||
MessageRetractionData? messageRetraction,
|
||||
String? lastMessageCorrectionSid});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class __$$_StanzaHandlerDataCopyWithImpl<$Res>
|
||||
extends _$StanzaHandlerDataCopyWithImpl<$Res>
|
||||
implements _$$_StanzaHandlerDataCopyWith<$Res> {
|
||||
__$$_StanzaHandlerDataCopyWithImpl(
|
||||
_$_StanzaHandlerData _value, $Res Function(_$_StanzaHandlerData) _then)
|
||||
: super(_value, (v) => _then(v as _$_StanzaHandlerData));
|
||||
|
||||
@override
|
||||
_$_StanzaHandlerData get _value => super._value as _$_StanzaHandlerData;
|
||||
|
||||
@override
|
||||
$Res call({
|
||||
Object? done = freezed,
|
||||
Object? cancel = freezed,
|
||||
Object? cancelReason = freezed,
|
||||
Object? stanza = freezed,
|
||||
Object? retransmitted = freezed,
|
||||
Object? sims = freezed,
|
||||
Object? sfs = freezed,
|
||||
Object? oob = freezed,
|
||||
Object? stableId = freezed,
|
||||
Object? reply = freezed,
|
||||
Object? chatState = freezed,
|
||||
Object? isCarbon = freezed,
|
||||
Object? deliveryReceiptRequested = freezed,
|
||||
Object? isMarkable = freezed,
|
||||
Object? fun = freezed,
|
||||
Object? funReplacement = freezed,
|
||||
Object? funCancellation = freezed,
|
||||
Object? encrypted = freezed,
|
||||
Object? encryptionType = freezed,
|
||||
Object? delayedDelivery = freezed,
|
||||
Object? other = freezed,
|
||||
Object? messageRetraction = freezed,
|
||||
Object? lastMessageCorrectionSid = freezed,
|
||||
}) {
|
||||
return _then(_$_StanzaHandlerData(
|
||||
done == freezed
|
||||
? _value.done
|
||||
: done // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
cancel == freezed
|
||||
? _value.cancel
|
||||
: cancel // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
cancelReason == freezed
|
||||
? _value.cancelReason
|
||||
: cancelReason // ignore: cast_nullable_to_non_nullable
|
||||
as dynamic,
|
||||
stanza == freezed
|
||||
? _value.stanza
|
||||
: stanza // ignore: cast_nullable_to_non_nullable
|
||||
as Stanza,
|
||||
retransmitted: retransmitted == freezed
|
||||
? _value.retransmitted
|
||||
: retransmitted // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
sims: sims == freezed
|
||||
? _value.sims
|
||||
: sims // ignore: cast_nullable_to_non_nullable
|
||||
as StatelessMediaSharingData?,
|
||||
sfs: sfs == freezed
|
||||
? _value.sfs
|
||||
: sfs // ignore: cast_nullable_to_non_nullable
|
||||
as StatelessFileSharingData?,
|
||||
oob: oob == freezed
|
||||
? _value.oob
|
||||
: oob // ignore: cast_nullable_to_non_nullable
|
||||
as OOBData?,
|
||||
stableId: stableId == freezed
|
||||
? _value.stableId
|
||||
: stableId // ignore: cast_nullable_to_non_nullable
|
||||
as StableStanzaId?,
|
||||
reply: reply == freezed
|
||||
? _value.reply
|
||||
: reply // ignore: cast_nullable_to_non_nullable
|
||||
as ReplyData?,
|
||||
chatState: chatState == freezed
|
||||
? _value.chatState
|
||||
: chatState // ignore: cast_nullable_to_non_nullable
|
||||
as ChatState?,
|
||||
isCarbon: isCarbon == freezed
|
||||
? _value.isCarbon
|
||||
: isCarbon // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
deliveryReceiptRequested: deliveryReceiptRequested == freezed
|
||||
? _value.deliveryReceiptRequested
|
||||
: deliveryReceiptRequested // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
isMarkable: isMarkable == freezed
|
||||
? _value.isMarkable
|
||||
: isMarkable // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
fun: fun == freezed
|
||||
? _value.fun
|
||||
: fun // ignore: cast_nullable_to_non_nullable
|
||||
as FileMetadataData?,
|
||||
funReplacement: funReplacement == freezed
|
||||
? _value.funReplacement
|
||||
: funReplacement // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
funCancellation: funCancellation == freezed
|
||||
? _value.funCancellation
|
||||
: funCancellation // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
encrypted: encrypted == freezed
|
||||
? _value.encrypted
|
||||
: encrypted // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
encryptionType: encryptionType == freezed
|
||||
? _value.encryptionType
|
||||
: encryptionType // ignore: cast_nullable_to_non_nullable
|
||||
as ExplicitEncryptionType?,
|
||||
delayedDelivery: delayedDelivery == freezed
|
||||
? _value.delayedDelivery
|
||||
: delayedDelivery // ignore: cast_nullable_to_non_nullable
|
||||
as DelayedDelivery?,
|
||||
other: other == freezed
|
||||
? _value._other
|
||||
: other // ignore: cast_nullable_to_non_nullable
|
||||
as Map<String, dynamic>,
|
||||
messageRetraction: messageRetraction == freezed
|
||||
? _value.messageRetraction
|
||||
: messageRetraction // ignore: cast_nullable_to_non_nullable
|
||||
as MessageRetractionData?,
|
||||
lastMessageCorrectionSid: lastMessageCorrectionSid == freezed
|
||||
? _value.lastMessageCorrectionSid
|
||||
: lastMessageCorrectionSid // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
|
||||
class _$_StanzaHandlerData implements _StanzaHandlerData {
|
||||
_$_StanzaHandlerData(this.done, this.cancel, this.cancelReason, this.stanza,
|
||||
{this.retransmitted = false,
|
||||
this.sims,
|
||||
this.sfs,
|
||||
this.oob,
|
||||
this.stableId,
|
||||
this.reply,
|
||||
this.chatState,
|
||||
this.isCarbon = false,
|
||||
this.deliveryReceiptRequested = false,
|
||||
this.isMarkable = false,
|
||||
this.fun,
|
||||
this.funReplacement,
|
||||
this.funCancellation,
|
||||
this.encrypted = false,
|
||||
this.encryptionType,
|
||||
this.delayedDelivery,
|
||||
final Map<String, dynamic> other = const <String, dynamic>{},
|
||||
this.messageRetraction,
|
||||
this.lastMessageCorrectionSid})
|
||||
: _other = other;
|
||||
|
||||
// Indicates to the runner that processing is now done. This means that all
|
||||
// pre-processing is done and no other handlers should be consulted.
|
||||
@override
|
||||
final bool done;
|
||||
// Indicates to the runner that processing is to be cancelled and no further handlers
|
||||
// should run. The stanza also will not be sent.
|
||||
@override
|
||||
final bool cancel;
|
||||
// The reason why we cancelled the processing and sending
|
||||
@override
|
||||
final dynamic cancelReason;
|
||||
// The stanza that is being dealt with. SHOULD NOT be overwritten, unless it is absolutely
|
||||
// necessary, e.g. with Message Carbons or OMEMO
|
||||
@override
|
||||
final Stanza stanza;
|
||||
// Whether the stanza is retransmitted. Only useful in the context of outgoing
|
||||
// stanza handlers. MUST NOT be overwritten.
|
||||
@override
|
||||
@JsonKey()
|
||||
final bool retransmitted;
|
||||
@override
|
||||
final StatelessMediaSharingData? sims;
|
||||
@override
|
||||
final StatelessFileSharingData? sfs;
|
||||
@override
|
||||
final OOBData? oob;
|
||||
@override
|
||||
final StableStanzaId? stableId;
|
||||
@override
|
||||
final ReplyData? reply;
|
||||
@override
|
||||
final ChatState? chatState;
|
||||
@override
|
||||
@JsonKey()
|
||||
final bool isCarbon;
|
||||
@override
|
||||
@JsonKey()
|
||||
final bool deliveryReceiptRequested;
|
||||
@override
|
||||
@JsonKey()
|
||||
final bool isMarkable;
|
||||
// File Upload Notifications
|
||||
// A notification
|
||||
@override
|
||||
final FileMetadataData? fun;
|
||||
// The stanza id this replaces
|
||||
@override
|
||||
final String? funReplacement;
|
||||
// The stanza id this cancels
|
||||
@override
|
||||
final String? funCancellation;
|
||||
// Whether the stanza was received encrypted
|
||||
@override
|
||||
@JsonKey()
|
||||
final bool encrypted;
|
||||
// The stated type of encryption used, if any was used
|
||||
@override
|
||||
final ExplicitEncryptionType? encryptionType;
|
||||
// Delayed Delivery
|
||||
@override
|
||||
final DelayedDelivery? delayedDelivery;
|
||||
// This is for stanza handlers that are not part of the XMPP library but still need
|
||||
// pass data around.
|
||||
final Map<String, dynamic> _other;
|
||||
// This is for stanza handlers that are not part of the XMPP library but still need
|
||||
// pass data around.
|
||||
@override
|
||||
@JsonKey()
|
||||
Map<String, dynamic> get other {
|
||||
// ignore: implicit_dynamic_type
|
||||
return EqualUnmodifiableMapView(_other);
|
||||
}
|
||||
|
||||
// If non-null, then it indicates the origin Id of the message that should be
|
||||
// retracted
|
||||
@override
|
||||
final MessageRetractionData? messageRetraction;
|
||||
// If non-null, then the message is a correction for the specified stanza Id
|
||||
@override
|
||||
final String? lastMessageCorrectionSid;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'StanzaHandlerData(done: $done, cancel: $cancel, cancelReason: $cancelReason, stanza: $stanza, retransmitted: $retransmitted, sims: $sims, sfs: $sfs, oob: $oob, stableId: $stableId, reply: $reply, chatState: $chatState, isCarbon: $isCarbon, deliveryReceiptRequested: $deliveryReceiptRequested, isMarkable: $isMarkable, fun: $fun, funReplacement: $funReplacement, funCancellation: $funCancellation, encrypted: $encrypted, encryptionType: $encryptionType, delayedDelivery: $delayedDelivery, other: $other, messageRetraction: $messageRetraction, lastMessageCorrectionSid: $lastMessageCorrectionSid)';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(dynamic other) {
|
||||
return identical(this, other) ||
|
||||
(other.runtimeType == runtimeType &&
|
||||
other is _$_StanzaHandlerData &&
|
||||
const DeepCollectionEquality().equals(other.done, done) &&
|
||||
const DeepCollectionEquality().equals(other.cancel, cancel) &&
|
||||
const DeepCollectionEquality()
|
||||
.equals(other.cancelReason, cancelReason) &&
|
||||
const DeepCollectionEquality().equals(other.stanza, stanza) &&
|
||||
const DeepCollectionEquality()
|
||||
.equals(other.retransmitted, retransmitted) &&
|
||||
const DeepCollectionEquality().equals(other.sims, sims) &&
|
||||
const DeepCollectionEquality().equals(other.sfs, sfs) &&
|
||||
const DeepCollectionEquality().equals(other.oob, oob) &&
|
||||
const DeepCollectionEquality().equals(other.stableId, stableId) &&
|
||||
const DeepCollectionEquality().equals(other.reply, reply) &&
|
||||
const DeepCollectionEquality().equals(other.chatState, chatState) &&
|
||||
const DeepCollectionEquality().equals(other.isCarbon, isCarbon) &&
|
||||
const DeepCollectionEquality().equals(
|
||||
other.deliveryReceiptRequested, deliveryReceiptRequested) &&
|
||||
const DeepCollectionEquality()
|
||||
.equals(other.isMarkable, isMarkable) &&
|
||||
const DeepCollectionEquality().equals(other.fun, fun) &&
|
||||
const DeepCollectionEquality()
|
||||
.equals(other.funReplacement, funReplacement) &&
|
||||
const DeepCollectionEquality()
|
||||
.equals(other.funCancellation, funCancellation) &&
|
||||
const DeepCollectionEquality().equals(other.encrypted, encrypted) &&
|
||||
const DeepCollectionEquality()
|
||||
.equals(other.encryptionType, encryptionType) &&
|
||||
const DeepCollectionEquality()
|
||||
.equals(other.delayedDelivery, delayedDelivery) &&
|
||||
const DeepCollectionEquality().equals(other._other, this._other) &&
|
||||
const DeepCollectionEquality()
|
||||
.equals(other.messageRetraction, messageRetraction) &&
|
||||
const DeepCollectionEquality().equals(
|
||||
other.lastMessageCorrectionSid, lastMessageCorrectionSid));
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hashAll([
|
||||
runtimeType,
|
||||
const DeepCollectionEquality().hash(done),
|
||||
const DeepCollectionEquality().hash(cancel),
|
||||
const DeepCollectionEquality().hash(cancelReason),
|
||||
const DeepCollectionEquality().hash(stanza),
|
||||
const DeepCollectionEquality().hash(retransmitted),
|
||||
const DeepCollectionEquality().hash(sims),
|
||||
const DeepCollectionEquality().hash(sfs),
|
||||
const DeepCollectionEquality().hash(oob),
|
||||
const DeepCollectionEquality().hash(stableId),
|
||||
const DeepCollectionEquality().hash(reply),
|
||||
const DeepCollectionEquality().hash(chatState),
|
||||
const DeepCollectionEquality().hash(isCarbon),
|
||||
const DeepCollectionEquality().hash(deliveryReceiptRequested),
|
||||
const DeepCollectionEquality().hash(isMarkable),
|
||||
const DeepCollectionEquality().hash(fun),
|
||||
const DeepCollectionEquality().hash(funReplacement),
|
||||
const DeepCollectionEquality().hash(funCancellation),
|
||||
const DeepCollectionEquality().hash(encrypted),
|
||||
const DeepCollectionEquality().hash(encryptionType),
|
||||
const DeepCollectionEquality().hash(delayedDelivery),
|
||||
const DeepCollectionEquality().hash(_other),
|
||||
const DeepCollectionEquality().hash(messageRetraction),
|
||||
const DeepCollectionEquality().hash(lastMessageCorrectionSid)
|
||||
]);
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
@override
|
||||
_$$_StanzaHandlerDataCopyWith<_$_StanzaHandlerData> get copyWith =>
|
||||
__$$_StanzaHandlerDataCopyWithImpl<_$_StanzaHandlerData>(
|
||||
this, _$identity);
|
||||
}
|
||||
|
||||
abstract class _StanzaHandlerData implements StanzaHandlerData {
|
||||
factory _StanzaHandlerData(final bool done, final bool cancel,
|
||||
final dynamic cancelReason, final Stanza stanza,
|
||||
{final bool retransmitted,
|
||||
final StatelessMediaSharingData? sims,
|
||||
final StatelessFileSharingData? sfs,
|
||||
final OOBData? oob,
|
||||
final StableStanzaId? stableId,
|
||||
final ReplyData? reply,
|
||||
final ChatState? chatState,
|
||||
final bool isCarbon,
|
||||
final bool deliveryReceiptRequested,
|
||||
final bool isMarkable,
|
||||
final FileMetadataData? fun,
|
||||
final String? funReplacement,
|
||||
final String? funCancellation,
|
||||
final bool encrypted,
|
||||
final ExplicitEncryptionType? encryptionType,
|
||||
final DelayedDelivery? delayedDelivery,
|
||||
final Map<String, dynamic> other,
|
||||
final MessageRetractionData? messageRetraction,
|
||||
final String? lastMessageCorrectionSid}) = _$_StanzaHandlerData;
|
||||
|
||||
@override // Indicates to the runner that processing is now done. This means that all
|
||||
// pre-processing is done and no other handlers should be consulted.
|
||||
bool get done;
|
||||
@override // Indicates to the runner that processing is to be cancelled and no further handlers
|
||||
// should run. The stanza also will not be sent.
|
||||
bool get cancel;
|
||||
@override // The reason why we cancelled the processing and sending
|
||||
dynamic get cancelReason;
|
||||
@override // The stanza that is being dealt with. SHOULD NOT be overwritten, unless it is absolutely
|
||||
// necessary, e.g. with Message Carbons or OMEMO
|
||||
Stanza get stanza;
|
||||
@override // Whether the stanza is retransmitted. Only useful in the context of outgoing
|
||||
// stanza handlers. MUST NOT be overwritten.
|
||||
bool get retransmitted;
|
||||
@override
|
||||
StatelessMediaSharingData? get sims;
|
||||
@override
|
||||
StatelessFileSharingData? get sfs;
|
||||
@override
|
||||
OOBData? get oob;
|
||||
@override
|
||||
StableStanzaId? get stableId;
|
||||
@override
|
||||
ReplyData? get reply;
|
||||
@override
|
||||
ChatState? get chatState;
|
||||
@override
|
||||
bool get isCarbon;
|
||||
@override
|
||||
bool get deliveryReceiptRequested;
|
||||
@override
|
||||
bool get isMarkable;
|
||||
@override // File Upload Notifications
|
||||
// A notification
|
||||
FileMetadataData? get fun;
|
||||
@override // The stanza id this replaces
|
||||
String? get funReplacement;
|
||||
@override // The stanza id this cancels
|
||||
String? get funCancellation;
|
||||
@override // Whether the stanza was received encrypted
|
||||
bool get encrypted;
|
||||
@override // The stated type of encryption used, if any was used
|
||||
ExplicitEncryptionType? get encryptionType;
|
||||
@override // Delayed Delivery
|
||||
DelayedDelivery? get delayedDelivery;
|
||||
@override // This is for stanza handlers that are not part of the XMPP library but still need
|
||||
// pass data around.
|
||||
Map<String, dynamic> get other;
|
||||
@override // If non-null, then it indicates the origin Id of the message that should be
|
||||
// retracted
|
||||
MessageRetractionData? get messageRetraction;
|
||||
@override // If non-null, then the message is a correction for the specified stanza Id
|
||||
String? get lastMessageCorrectionSid;
|
||||
@override
|
||||
@JsonKey(ignore: true)
|
||||
_$$_StanzaHandlerDataCopyWith<_$_StanzaHandlerData> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
@ -1,93 +1,114 @@
|
||||
import 'package:moxlib/moxlib.dart';
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:moxxmpp/src/managers/data.dart';
|
||||
import 'package:moxxmpp/src/namespaces.dart';
|
||||
import 'package:moxxmpp/src/stanza.dart';
|
||||
import 'package:moxxmpp/src/stringxml.dart';
|
||||
|
||||
/// A Handler is responsible for matching any kind of toplevel item in the XML stream
|
||||
/// (stanzas and Nonzas). For that, its [matches] method is called. What happens
|
||||
/// next depends on the subclass.
|
||||
// ignore: one_member_abstracts
|
||||
abstract class Handler {
|
||||
|
||||
const Handler(this.matchStanzas, { this.nonzaTag, this.nonzaXmlns });
|
||||
final String? nonzaTag;
|
||||
final String? nonzaXmlns;
|
||||
final bool matchStanzas;
|
||||
|
||||
/// Returns true if the node matches the description provided by this [Handler].
|
||||
bool matches(XMLNode node) {
|
||||
var matches = false;
|
||||
|
||||
if (nonzaTag == null && nonzaXmlns == null) {
|
||||
matches = true;
|
||||
}
|
||||
|
||||
if (nonzaXmlns != null && nonzaTag != null) {
|
||||
matches = (node.attributes['xmlns'] ?? '') == nonzaXmlns! && node.tag == nonzaTag!;
|
||||
}
|
||||
|
||||
if (matchStanzas && nonzaTag == null) {
|
||||
matches = [ 'iq', 'presence', 'message' ].contains(node.tag);
|
||||
}
|
||||
|
||||
return matches;
|
||||
}
|
||||
bool matches(XMLNode node);
|
||||
}
|
||||
|
||||
/// A Handler that specialises in matching Nonzas (and stanzas).
|
||||
class NonzaHandler extends Handler {
|
||||
|
||||
NonzaHandler({
|
||||
required this.callback,
|
||||
String? nonzaTag,
|
||||
String? nonzaXmlns,
|
||||
}) : super(
|
||||
false,
|
||||
nonzaTag: nonzaTag,
|
||||
nonzaXmlns: nonzaXmlns,
|
||||
);
|
||||
required this.callback,
|
||||
this.nonzaTag,
|
||||
this.nonzaXmlns,
|
||||
});
|
||||
|
||||
/// The function to call when a nonza matches the description.
|
||||
final Future<bool> Function(XMLNode) callback;
|
||||
}
|
||||
|
||||
class StanzaHandler extends Handler {
|
||||
/// The expected tag of a matching nonza.
|
||||
final String? nonzaTag;
|
||||
|
||||
// The expected xmlns attribute of a matching nonza.
|
||||
final String? nonzaXmlns;
|
||||
|
||||
StanzaHandler({
|
||||
required this.callback,
|
||||
this.tagXmlns,
|
||||
this.tagName,
|
||||
this.priority = 0,
|
||||
String? stanzaTag,
|
||||
}) : super(
|
||||
true,
|
||||
nonzaTag: stanzaTag,
|
||||
nonzaXmlns: stanzaXmlns,
|
||||
);
|
||||
final String? tagName;
|
||||
final String? tagXmlns;
|
||||
final int priority;
|
||||
final Future<StanzaHandlerData> Function(Stanza, StanzaHandlerData) callback;
|
||||
|
||||
@override
|
||||
bool matches(XMLNode node) {
|
||||
var matches = super.matches(node);
|
||||
|
||||
if (matches == false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (tagName != null) {
|
||||
final firstTag = node.firstTag(tagName!, xmlns: tagXmlns);
|
||||
|
||||
matches = firstTag != null;
|
||||
} else if (tagXmlns != null) {
|
||||
return listContains(
|
||||
node.children,
|
||||
(XMLNode node_) => node_.attributes.containsKey('xmlns') && node_.attributes['xmlns'] == tagXmlns,
|
||||
);
|
||||
var matches = true;
|
||||
if (nonzaTag == null && nonzaXmlns == null) {
|
||||
return true;
|
||||
} else {
|
||||
if (nonzaXmlns != null) {
|
||||
matches &= node.attributes['xmlns'] == nonzaXmlns;
|
||||
}
|
||||
if (nonzaTag != null) {
|
||||
matches &= node.tag == nonzaTag;
|
||||
}
|
||||
}
|
||||
|
||||
if (tagName == null && tagXmlns == null) {
|
||||
matches = true;
|
||||
}
|
||||
|
||||
return matches;
|
||||
}
|
||||
}
|
||||
|
||||
int stanzaHandlerSortComparator(StanzaHandler a, StanzaHandler b) => b.priority.compareTo(a.priority);
|
||||
/// A Handler that only matches stanzas.
|
||||
class StanzaHandler extends Handler {
|
||||
StanzaHandler({
|
||||
required this.callback,
|
||||
this.tagXmlns,
|
||||
this.tagName,
|
||||
this.priority = 0,
|
||||
this.stanzaTag,
|
||||
this.xmlns,
|
||||
});
|
||||
|
||||
/// If specified, then the stanza must contain a direct child with a tag equal to
|
||||
/// [tagName].
|
||||
final String? tagName;
|
||||
|
||||
/// If specified, then the stanza must contain a direct child with a xmlns attribute
|
||||
/// equal to [tagXmlns]. If [tagName] is also non-null, then the element must also
|
||||
/// have a tag equal to [tagName].
|
||||
final String? tagXmlns;
|
||||
|
||||
/// If specified, the matching stanza must have a tag equal to [stanzaTag].
|
||||
final String? stanzaTag;
|
||||
|
||||
/// If specified, then the stanza must have a xmlns attribute equal to [xmlns].
|
||||
/// This defaults to [stanzaXmlns], but can be set to any other value or null. This
|
||||
/// is useful, for example, for components.
|
||||
final String? xmlns;
|
||||
|
||||
/// The priority after which [StanzaHandler]s are sorted.
|
||||
final int priority;
|
||||
|
||||
/// The function to call when a stanza matches the description.
|
||||
final Future<StanzaHandlerData> Function(Stanza, StanzaHandlerData) callback;
|
||||
|
||||
@override
|
||||
bool matches(XMLNode node) {
|
||||
var matches = ['iq', 'message', 'presence'].contains(node.tag);
|
||||
if (stanzaTag != null) {
|
||||
matches &= node.tag == stanzaTag;
|
||||
}
|
||||
if (xmlns != null) {
|
||||
matches &= node.xmlns == xmlns;
|
||||
}
|
||||
|
||||
if (tagName != null) {
|
||||
final firstTag = node.firstTag(tagName!, xmlns: tagXmlns);
|
||||
matches &= firstTag != null;
|
||||
|
||||
if (tagXmlns != null) {
|
||||
matches &= firstTag?.xmlns == tagXmlns;
|
||||
}
|
||||
} else if (tagXmlns != null) {
|
||||
matches &= node.children.firstWhereOrNull(
|
||||
(XMLNode node_) => node_.attributes['xmlns'] == tagXmlns,
|
||||
) !=
|
||||
null;
|
||||
}
|
||||
|
||||
return matches;
|
||||
}
|
||||
}
|
||||
|
||||
int stanzaHandlerSortComparator(StanzaHandler a, StanzaHandler b) =>
|
||||
b.priority.compareTo(a.priority);
|
||||
|
@ -1,28 +1,36 @@
|
||||
const smManager = 'im.moxxmpp.streammangementmanager';
|
||||
const discoManager = 'im.moxxmpp.discomanager';
|
||||
const messageManager = 'im.moxxmpp.messagemanager';
|
||||
const rosterManager = 'im.moxxmpp.rostermanager';
|
||||
const presenceManager = 'im.moxxmpp.presencemanager';
|
||||
const csiManager = 'im.moxxmpp.csimanager';
|
||||
const carbonsManager = 'im.moxxmpp.carbonsmanager';
|
||||
const vcardManager = 'im.moxxmpp.vcardmanager';
|
||||
const pubsubManager = 'im.moxxmpp.pubsubmanager';
|
||||
const userAvatarManager = 'im.moxxmpp.useravatarmanager';
|
||||
const stableIdManager = 'im.moxxmpp.stableidmanager';
|
||||
const simsManager = 'im.moxxmpp.simsmanager';
|
||||
const messageDeliveryReceiptManager = 'im.moxxmpp.messagedeliveryreceiptmanager';
|
||||
const chatMarkerManager = 'im.moxxmpp.chatmarkermanager';
|
||||
const oobManager = 'im.moxxmpp.oobmanager';
|
||||
const sfsManager = 'im.moxxmpp.sfsmanager';
|
||||
const messageRepliesManager = 'im.moxxmpp.messagerepliesmanager';
|
||||
const blockingManager = 'im.moxxmpp.blockingmanager';
|
||||
const httpFileUploadManager = 'im.moxxmpp.httpfileuploadmanager';
|
||||
const chatStateManager = 'im.moxxmpp.chatstatemanager';
|
||||
const pingManager = 'im.moxxmpp.ping';
|
||||
const fileUploadNotificationManager = 'im.moxxmpp.fileuploadnotificationmanager';
|
||||
const smManager = 'org.moxxmpp.streammangementmanager';
|
||||
const discoManager = 'org.moxxmpp.discomanager';
|
||||
const messageManager = 'org.moxxmpp.messagemanager';
|
||||
const rosterManager = 'org.moxxmpp.rostermanager';
|
||||
const presenceManager = 'org.moxxmpp.presencemanager';
|
||||
const csiManager = 'org.moxxmpp.csimanager';
|
||||
const carbonsManager = 'org.moxxmpp.carbonsmanager';
|
||||
const vcardManager = 'org.moxxmpp.vcardmanager';
|
||||
const pubsubManager = 'org.moxxmpp.pubsubmanager';
|
||||
const userAvatarManager = 'org.moxxmpp.useravatarmanager';
|
||||
const stableIdManager = 'org.moxxmpp.stableidmanager';
|
||||
const simsManager = 'org.moxxmpp.simsmanager';
|
||||
const messageDeliveryReceiptManager =
|
||||
'org.moxxmpp.messagedeliveryreceiptmanager';
|
||||
const chatMarkerManager = 'org.moxxmpp.chatmarkermanager';
|
||||
const oobManager = 'org.moxxmpp.oobmanager';
|
||||
const sfsManager = 'org.moxxmpp.sfsmanager';
|
||||
const messageRepliesManager = 'org.moxxmpp.messagerepliesmanager';
|
||||
const blockingManager = 'org.moxxmpp.blockingmanager';
|
||||
const httpFileUploadManager = 'org.moxxmpp.httpfileuploadmanager';
|
||||
const chatStateManager = 'org.moxxmpp.chatstatemanager';
|
||||
const pingManager = 'org.moxxmpp.ping';
|
||||
const fileUploadNotificationManager =
|
||||
'org.moxxmpp.fileuploadnotificationmanager';
|
||||
const omemoManager = 'org.moxxmpp.omemomanager';
|
||||
const emeManager = 'org.moxxmpp.ememanager';
|
||||
const cryptographicHashManager = 'org.moxxmpp.cryptographichashmanager';
|
||||
const delayedDeliveryManager = 'org.moxxmpp.delayeddeliverymanager';
|
||||
const messageRetractionManager = 'org.moxxmpp.messageretractionmanager';
|
||||
const lastMessageCorrectionManager = 'org.moxxmpp.lastmessagecorrectionmanager';
|
||||
const messageReactionsManager = 'org.moxxmpp.messagereactionsmanager';
|
||||
const stickersManager = 'org.moxxmpp.stickersmanager';
|
||||
const entityCapabilitiesManager = 'org.moxxmpp.entitycapabilities';
|
||||
const messageProcessingHintManager = 'org.moxxmpp.messageprocessinghint';
|
||||
const occupantIdManager = 'org.moxxmpp.occupantidmanager';
|
||||
const mucManager = 'org.moxxmpp.mucmanager';
|
||||
|
@ -0,0 +1 @@
|
||||
|
@ -1,267 +1,153 @@
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:moxxmpp/src/events.dart';
|
||||
import 'package:moxxmpp/src/jid.dart';
|
||||
import 'package:moxxmpp/src/managers/base.dart';
|
||||
import 'package:moxxmpp/src/managers/data.dart';
|
||||
import 'package:moxxmpp/src/managers/handlers.dart';
|
||||
import 'package:moxxmpp/src/managers/namespaces.dart';
|
||||
import 'package:moxxmpp/src/namespaces.dart';
|
||||
import 'package:moxxmpp/src/stanza.dart';
|
||||
import 'package:moxxmpp/src/stringxml.dart';
|
||||
import 'package:moxxmpp/src/xeps/staging/file_upload_notification.dart';
|
||||
import 'package:moxxmpp/src/util/typed_map.dart';
|
||||
import 'package:moxxmpp/src/xeps/xep_0066.dart';
|
||||
import 'package:moxxmpp/src/xeps/xep_0085.dart';
|
||||
import 'package:moxxmpp/src/xeps/xep_0184.dart';
|
||||
import 'package:moxxmpp/src/xeps/xep_0308.dart';
|
||||
import 'package:moxxmpp/src/xeps/xep_0333.dart';
|
||||
import 'package:moxxmpp/src/xeps/xep_0359.dart';
|
||||
import 'package:moxxmpp/src/xeps/xep_0424.dart';
|
||||
import 'package:moxxmpp/src/xeps/xep_0446.dart';
|
||||
import 'package:moxxmpp/src/xeps/xep_0447.dart';
|
||||
import 'package:moxxmpp/src/xeps/xep_0448.dart';
|
||||
import 'package:moxxmpp/src/xeps/xep_0449.dart';
|
||||
import 'package:moxxmpp/src/xeps/xep_0461.dart';
|
||||
|
||||
class MessageDetails {
|
||||
const MessageDetails({
|
||||
required this.to,
|
||||
this.body,
|
||||
this.requestDeliveryReceipt = false,
|
||||
this.requestChatMarkers = true,
|
||||
this.id,
|
||||
this.originId,
|
||||
this.quoteBody,
|
||||
this.quoteId,
|
||||
this.quoteFrom,
|
||||
this.chatState,
|
||||
this.sfs,
|
||||
this.fun,
|
||||
this.funReplacement,
|
||||
this.funCancellation,
|
||||
this.shouldEncrypt = false,
|
||||
this.messageRetraction,
|
||||
this.lastMessageCorrectionId,
|
||||
});
|
||||
final String to;
|
||||
/// A callback that is called whenever a message is sent using
|
||||
/// [MessageManager.sendMessage]. The input the typed map that is passed to
|
||||
/// sendMessage.
|
||||
typedef MessageSendingCallback = List<XMLNode> Function(
|
||||
TypedMap<StanzaHandlerExtension>,
|
||||
);
|
||||
|
||||
/// The raw content of the <body /> element.
|
||||
class MessageBodyData implements StanzaHandlerExtension {
|
||||
const MessageBodyData(this.body);
|
||||
|
||||
/// The content of the <body /> element.
|
||||
final String? body;
|
||||
final bool requestDeliveryReceipt;
|
||||
final bool requestChatMarkers;
|
||||
final String? id;
|
||||
final String? originId;
|
||||
final String? quoteBody;
|
||||
final String? quoteId;
|
||||
final String? quoteFrom;
|
||||
final ChatState? chatState;
|
||||
final StatelessFileSharingData? sfs;
|
||||
final FileMetadataData? fun;
|
||||
final String? funReplacement;
|
||||
final String? funCancellation;
|
||||
final bool shouldEncrypt;
|
||||
final MessageRetractionData? messageRetraction;
|
||||
final String? lastMessageCorrectionId;
|
||||
|
||||
XMLNode toXML() {
|
||||
return XMLNode(
|
||||
tag: 'body',
|
||||
text: body,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// The id attribute of the message stanza.
|
||||
class MessageIdData implements StanzaHandlerExtension {
|
||||
const MessageIdData(this.id);
|
||||
|
||||
/// The id attribute of the stanza.
|
||||
final String id;
|
||||
}
|
||||
|
||||
class MessageManager extends XmppManagerBase {
|
||||
@override
|
||||
String getId() => messageManager;
|
||||
MessageManager() : super(messageManager);
|
||||
|
||||
@override
|
||||
String getName() => 'MessageManager';
|
||||
/// The priority of the message handler. If a handler should run before this one,
|
||||
/// which emits the [MessageEvent] event and terminates processing, make sure it
|
||||
/// has a priority greater than [messageHandlerPriority].
|
||||
static int messageHandlerPriority = -100;
|
||||
|
||||
/// A list of callbacks that are called when a message is sent in order to add
|
||||
/// appropriate child elements.
|
||||
final List<MessageSendingCallback> _messageSendingCallbacks =
|
||||
List<MessageSendingCallback>.empty(growable: true);
|
||||
|
||||
void registerMessageSendingCallback(MessageSendingCallback callback) {
|
||||
_messageSendingCallbacks.add(callback);
|
||||
}
|
||||
|
||||
@override
|
||||
List<StanzaHandler> getIncomingStanzaHandlers() => [
|
||||
StanzaHandler(
|
||||
stanzaTag: 'message',
|
||||
callback: _onMessage,
|
||||
priority: -100,
|
||||
)
|
||||
];
|
||||
StanzaHandler(
|
||||
stanzaTag: 'message',
|
||||
callback: _onMessage,
|
||||
priority: messageHandlerPriority,
|
||||
),
|
||||
];
|
||||
|
||||
@override
|
||||
Future<bool> isSupported() async => true;
|
||||
|
||||
Future<StanzaHandlerData> _onMessage(Stanza _, StanzaHandlerData state) async {
|
||||
final message = state.stanza;
|
||||
final body = message.firstTag('body');
|
||||
|
||||
getAttributes().sendEvent(MessageEvent(
|
||||
body: body != null ? body.innerText() : '',
|
||||
fromJid: JID.fromString(message.attributes['from']! as String),
|
||||
toJid: JID.fromString(message.attributes['to']! as String),
|
||||
sid: message.attributes['id']! as String,
|
||||
stanzaId: state.stableId ?? const StableStanzaId(),
|
||||
isCarbon: state.isCarbon,
|
||||
deliveryReceiptRequested: state.deliveryReceiptRequested,
|
||||
isMarkable: state.isMarkable,
|
||||
type: message.attributes['type'] as String?,
|
||||
oob: state.oob,
|
||||
sfs: state.sfs,
|
||||
sims: state.sims,
|
||||
reply: state.reply,
|
||||
chatState: state.chatState,
|
||||
fun: state.fun,
|
||||
funReplacement: state.funReplacement,
|
||||
funCancellation: state.funCancellation,
|
||||
encrypted: state.encrypted,
|
||||
messageRetraction: state.messageRetraction,
|
||||
messageCorrectionId: state.lastMessageCorrectionSid,
|
||||
other: state.other,
|
||||
error: StanzaError.fromStanza(message),
|
||||
),);
|
||||
Future<StanzaHandlerData> _onMessage(
|
||||
Stanza stanza,
|
||||
StanzaHandlerData state,
|
||||
) async {
|
||||
final body = stanza.firstTag('body');
|
||||
if (body != null) {
|
||||
state.extensions.set(
|
||||
MessageBodyData(body.innerText()),
|
||||
);
|
||||
}
|
||||
|
||||
return state.copyWith(done: true);
|
||||
}
|
||||
|
||||
/// Send a message to to with the content body. If deliveryRequest is true, then
|
||||
/// the message will also request a delivery receipt from the receiver.
|
||||
/// If id is non-null, then it will be the id of the message stanza.
|
||||
/// element to this id. If originId is non-null, then it will create an "origin-id"
|
||||
/// child in the message stanza and set its id to originId.
|
||||
void sendMessage(MessageDetails details) {
|
||||
final stanza = Stanza.message(
|
||||
to: details.to,
|
||||
type: 'chat',
|
||||
id: details.id,
|
||||
children: [],
|
||||
getAttributes().sendEvent(
|
||||
MessageEvent(
|
||||
JID.fromString(state.stanza.attributes['from']! as String),
|
||||
JID.fromString(state.stanza.attributes['to']! as String),
|
||||
state.encrypted,
|
||||
state.extensions,
|
||||
id: state.stanza.attributes['id'] as String?,
|
||||
type: state.stanza.attributes['type'] as String?,
|
||||
error: StanzaError.fromStanza(state.stanza),
|
||||
encryptionError: state.encryptionError,
|
||||
),
|
||||
);
|
||||
|
||||
if (details.quoteBody != null) {
|
||||
final fallback = '> ${details.quoteBody!}';
|
||||
return state..done = true;
|
||||
}
|
||||
|
||||
stanza
|
||||
..addChild(
|
||||
XMLNode(tag: 'body', text: '$fallback\n${details.body}'),
|
||||
)
|
||||
..addChild(
|
||||
XMLNode.xmlns(
|
||||
tag: 'reply',
|
||||
xmlns: replyXmlns,
|
||||
attributes: {
|
||||
'to': details.quoteFrom!,
|
||||
'id': details.quoteId!
|
||||
},
|
||||
),
|
||||
)
|
||||
..addChild(
|
||||
XMLNode.xmlns(
|
||||
tag: 'fallback',
|
||||
xmlns: fallbackXmlns,
|
||||
attributes: {
|
||||
'for': replyXmlns
|
||||
},
|
||||
children: [
|
||||
XMLNode(
|
||||
tag: 'body',
|
||||
attributes: <String, String>{
|
||||
'start': '0',
|
||||
'end': '${fallback.length}'
|
||||
},
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
} else {
|
||||
var body = details.body;
|
||||
if (details.sfs != null) {
|
||||
// TODO(Unknown): Maybe find a better solution
|
||||
final firstSource = details.sfs!.sources.first;
|
||||
if (firstSource is StatelessFileSharingUrlSource) {
|
||||
body = firstSource.url;
|
||||
} else if (firstSource is StatelessFileSharingEncryptedSource) {
|
||||
body = firstSource.source.url;
|
||||
}
|
||||
} else if (details.messageRetraction?.fallback != null) {
|
||||
body = details.messageRetraction!.fallback;
|
||||
}
|
||||
|
||||
stanza.addChild(
|
||||
XMLNode(tag: 'body', text: body),
|
||||
);
|
||||
}
|
||||
|
||||
if (details.requestDeliveryReceipt) {
|
||||
stanza.addChild(makeMessageDeliveryRequest());
|
||||
}
|
||||
if (details.requestChatMarkers) {
|
||||
stanza.addChild(makeChatMarkerMarkable());
|
||||
}
|
||||
if (details.originId != null) {
|
||||
stanza.addChild(makeOriginIdElement(details.originId!));
|
||||
}
|
||||
|
||||
if (details.sfs != null) {
|
||||
stanza.addChild(details.sfs!.toXML());
|
||||
|
||||
final source = details.sfs!.sources.first;
|
||||
if (source is StatelessFileSharingUrlSource) {
|
||||
// SFS recommends OOB as a fallback
|
||||
stanza.addChild(constructOOBNode(OOBData(url: source.url)));
|
||||
}
|
||||
}
|
||||
|
||||
if (details.chatState != null) {
|
||||
stanza.addChild(
|
||||
// TODO(Unknown): Move this into xep_0085.dart
|
||||
XMLNode.xmlns(tag: chatStateToString(details.chatState!), xmlns: chatStateXmlns),
|
||||
);
|
||||
}
|
||||
|
||||
if (details.fun != null) {
|
||||
stanza.addChild(
|
||||
XMLNode.xmlns(
|
||||
tag: 'file-upload',
|
||||
xmlns: fileUploadNotificationXmlns,
|
||||
children: [
|
||||
details.fun!.toXML(),
|
||||
],
|
||||
/// Send an unawaitable message to [to]. [extensions] is a typed map that contains
|
||||
/// data for building the message.
|
||||
Future<void> sendMessage(
|
||||
JID to,
|
||||
TypedMap<StanzaHandlerExtension> extensions, {
|
||||
String type = 'chat',
|
||||
}) async {
|
||||
await getAttributes().sendStanza(
|
||||
StanzaDetails(
|
||||
Stanza.message(
|
||||
to: to.toString(),
|
||||
id: extensions.get<MessageIdData>()?.id,
|
||||
type: type,
|
||||
children: _messageSendingCallbacks
|
||||
.map((c) => c(extensions))
|
||||
.flattened
|
||||
.toList(),
|
||||
),
|
||||
);
|
||||
extensions: extensions,
|
||||
awaitable: false,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
List<XMLNode> _messageSendingCallback(
|
||||
TypedMap<StanzaHandlerExtension> extensions,
|
||||
) {
|
||||
if (extensions.get<ReplyData>() != null) {
|
||||
return [];
|
||||
}
|
||||
if (extensions.get<StickersData>() != null) {
|
||||
return [];
|
||||
}
|
||||
if (extensions.get<StatelessFileSharingData>() != null) {
|
||||
return [];
|
||||
}
|
||||
if (extensions.get<OOBData>() != null) {
|
||||
return [];
|
||||
}
|
||||
|
||||
if (details.funReplacement != null) {
|
||||
stanza.addChild(
|
||||
XMLNode.xmlns(
|
||||
tag: 'replaces',
|
||||
xmlns: fileUploadNotificationXmlns,
|
||||
attributes: <String, String>{
|
||||
'id': details.funReplacement!,
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
final data = extensions.get<MessageBodyData>();
|
||||
return data != null ? [data.toXML()] : [];
|
||||
}
|
||||
|
||||
if (details.messageRetraction != null) {
|
||||
stanza.addChild(
|
||||
XMLNode.xmlns(
|
||||
tag: 'apply-to',
|
||||
xmlns: fasteningXmlns,
|
||||
attributes: <String, String>{
|
||||
'id': details.messageRetraction!.id,
|
||||
},
|
||||
children: [
|
||||
XMLNode.xmlns(
|
||||
tag: 'retract',
|
||||
xmlns: messageRetractionXmlns,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
@override
|
||||
Future<void> postRegisterCallback() async {
|
||||
await super.postRegisterCallback();
|
||||
|
||||
if (details.messageRetraction!.fallback != null) {
|
||||
stanza.addChild(
|
||||
XMLNode.xmlns(
|
||||
tag: 'fallback',
|
||||
xmlns: fallbackIndicationXmlns,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (details.lastMessageCorrectionId != null) {
|
||||
stanza.addChild(
|
||||
makeLastMessageCorrectionEdit(
|
||||
details.lastMessageCorrectionId!,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
getAttributes().sendStanza(stanza, awaitable: false);
|
||||
// Register the sending callback
|
||||
registerMessageSendingCallback(_messageSendingCallback);
|
||||
}
|
||||
}
|
||||
|
@ -9,9 +9,11 @@ const fullStanzaXmlns = 'urn:ietf:params:xml:ns:xmpp-stanzas';
|
||||
// RFC 6121
|
||||
const rosterXmlns = 'jabber:iq:roster';
|
||||
const rosterVersioningXmlns = 'urn:xmpp:features:rosterver';
|
||||
const subscriptionPreApprovalXmlns = 'urn:xmpp:features:pre-approval';
|
||||
|
||||
// XEP-0004
|
||||
const dataFormsXmlns = 'jabber:x:data';
|
||||
const formVarFormType = 'FORM_TYPE';
|
||||
|
||||
// XEP-0030
|
||||
const discoInfoXmlns = 'http://jabber.org/protocol/disco#info';
|
||||
@ -20,6 +22,11 @@ const discoItemsXmlns = 'http://jabber.org/protocol/disco#items';
|
||||
// XEP-0033
|
||||
const extendedAddressingXmlns = 'http://jabber.org/protocol/address';
|
||||
|
||||
// XEP-0045
|
||||
const mucXmlns = 'http://jabber.org/protocol/muc';
|
||||
const mucUserXmlns = 'http://jabber.org/protocol/muc#user';
|
||||
const roomInfoFormType = 'http://jabber.org/protocol/muc#roominfo';
|
||||
|
||||
// XEP-0054
|
||||
const vCardTempXmlns = 'vcard-temp';
|
||||
const vCardTempUpdate = 'vcard-temp:x:update';
|
||||
@ -28,9 +35,11 @@ const vCardTempUpdate = 'vcard-temp:x:update';
|
||||
const pubsubXmlns = 'http://jabber.org/protocol/pubsub';
|
||||
const pubsubEventXmlns = 'http://jabber.org/protocol/pubsub#event';
|
||||
const pubsubOwnerXmlns = 'http://jabber.org/protocol/pubsub#owner';
|
||||
const pubsubPublishOptionsXmlns = 'http://jabber.org/protocol/pubsub#publish-options';
|
||||
const pubsubPublishOptionsXmlns =
|
||||
'http://jabber.org/protocol/pubsub#publish-options';
|
||||
const pubsubNodeConfigMax = 'http://jabber.org/protocol/pubsub#config-node-max';
|
||||
const pubsubNodeConfigMultiItems = 'http://jabber.org/protocol/pubsub#multi-items';
|
||||
const pubsubNodeConfigMultiItems =
|
||||
'http://jabber.org/protocol/pubsub#multi-items';
|
||||
|
||||
// XEP-0066
|
||||
const oobDataXmlns = 'jabber:x:oob';
|
||||
@ -42,6 +51,9 @@ const userAvatarMetadataXmlns = 'urn:xmpp:avatar:metadata';
|
||||
// XEP-0085
|
||||
const chatStateXmlns = 'http://jabber.org/protocol/chatstates';
|
||||
|
||||
// XEP-0114
|
||||
const componentAcceptXmlns = 'jabber:component:accept';
|
||||
|
||||
// XEP-0115
|
||||
const capsXmlns = 'http://jabber.org/protocol/caps';
|
||||
|
||||
@ -60,6 +72,9 @@ const delayedDeliveryXmlns = 'urn:xmpp:delay';
|
||||
// XEP-0234
|
||||
const jingleFileTransferXmlns = 'urn:xmpp:jingle:apps:file-transfer:5';
|
||||
|
||||
// XEP-0264
|
||||
const jingleContentThumbnailXmlns = 'urn:xmpp:thumbs:1';
|
||||
|
||||
// XEP-0280
|
||||
const carbonsXmlns = 'urn:xmpp:carbons:2';
|
||||
|
||||
@ -69,12 +84,6 @@ const forwardedXmlns = 'urn:xmpp:forward:0';
|
||||
// XEP-0300
|
||||
const hashXmlns = 'urn:xmpp:hashes:2';
|
||||
const hashFunctionNameBaseXmlns = 'urn:xmpp:hash-function-text-names';
|
||||
const hashSha256 = 'sha-256';
|
||||
const hashSha512 = 'sha-512';
|
||||
const hashSha3256 = 'sha3-256';
|
||||
const hashSha3512 = 'sha3-512';
|
||||
const hashBlake2b256 = 'blake2b-256';
|
||||
const hashBlake2b512 = 'blake2b-512';
|
||||
|
||||
// XEP-0308
|
||||
const lmcXmlns = 'urn:xmpp:message-correct:0';
|
||||
@ -97,7 +106,7 @@ const httpFileUploadXmlns = 'urn:xmpp:http:upload:0';
|
||||
// XEP-0372
|
||||
const referenceXmlns = 'urn:xmpp:reference:0';
|
||||
|
||||
// XEP-380
|
||||
// XEP-0380
|
||||
const emeXmlns = 'urn:xmpp:eme:0';
|
||||
const emeOtr = 'urn:xmpp:otr:0';
|
||||
const emeLegacyOpenPGP = 'jabber:x:encrypted';
|
||||
@ -114,18 +123,30 @@ const omemoBundlesXmlns = 'urn:xmpp:omemo:2:bundles';
|
||||
// XEP-0385
|
||||
const simsXmlns = 'urn:xmpp:sims:1';
|
||||
|
||||
// XEP-0386
|
||||
const bind2Xmlns = 'urn:xmpp:bind:0';
|
||||
|
||||
// XEP-0388
|
||||
const sasl2Xmlns = 'urn:xmpp:sasl:2';
|
||||
|
||||
// XEP-0420
|
||||
const sceXmlns = 'urn:xmpp:sce:1';
|
||||
|
||||
// XEP-0421
|
||||
const occupantIdXmlns = 'urn:xmpp:occupant-id:0';
|
||||
|
||||
// XEP-0422
|
||||
const fasteningXmlns = 'urn:xmpp:fasten:0';
|
||||
|
||||
// XEP-0424
|
||||
const messageRetractionXmlns = 'urn:xmpp:message-retract:0';
|
||||
|
||||
// XEp-0428
|
||||
// XEP-0428
|
||||
const fallbackIndicationXmlns = 'urn:xmpp:fallback:0';
|
||||
|
||||
// XEP-0444
|
||||
const messageReactionsXmlns = 'urn:xmpp:reactions:0';
|
||||
|
||||
// XEP-0446
|
||||
const fileMetadataXmlns = 'urn:xmpp:file:metadata:0';
|
||||
|
||||
@ -134,13 +155,20 @@ const sfsXmlns = 'urn:xmpp:sfs:0';
|
||||
|
||||
// XEP-0448
|
||||
const sfsEncryptionXmlns = 'urn:xmpp:esfs:0';
|
||||
const sfsEncryptionAes128GcmNoPaddingXmlns = 'urn:xmpp:ciphers:aes-128-gcm-nopadding:0';
|
||||
const sfsEncryptionAes256GcmNoPaddingXmlns = 'urn:xmpp:ciphers:aes-256-gcm-nopadding:0';
|
||||
const sfsEncryptionAes128GcmNoPaddingXmlns =
|
||||
'urn:xmpp:ciphers:aes-128-gcm-nopadding:0';
|
||||
const sfsEncryptionAes256GcmNoPaddingXmlns =
|
||||
'urn:xmpp:ciphers:aes-256-gcm-nopadding:0';
|
||||
const sfsEncryptionAes256CbcPkcs7Xmlns = 'urn:xmpp:ciphers:aes-256-cbc-pkcs7:0';
|
||||
|
||||
// XEP-0449
|
||||
const stickersXmlns = 'urn:xmpp:stickers:0';
|
||||
|
||||
// XEP-0461
|
||||
const replyXmlns = 'urn:xmpp:reply:0';
|
||||
const fallbackXmlns = 'urn:xmpp:feature-fallback:0';
|
||||
|
||||
// ???
|
||||
const urlDataXmlns = 'http://jabber.org/protocol/url-data';
|
||||
|
||||
// XEP-XXXX
|
||||
const fastXmlns = 'urn:xmpp:fast:0';
|
||||
|
@ -7,3 +7,8 @@ const rosterNegotiator = 'im.moxxmpp.core.roster';
|
||||
const resourceBindingNegotiator = 'im.moxxmpp.core.resource';
|
||||
const streamManagementNegotiator = 'im.moxxmpp.xeps.sm';
|
||||
const startTlsNegotiator = 'im.moxxmpp.core.starttls';
|
||||
const sasl2Negotiator = 'org.moxxmpp.sasl.sasl2';
|
||||
const bind2Negotiator = 'org.moxxmpp.bind2';
|
||||
const saslFASTNegotiator = 'org.moxxmpp.sasl.fast';
|
||||
const carbonsNegotiator = 'org.moxxmpp.bind2.carbons';
|
||||
const presenceNegotiator = 'org.moxxmpp.core.presence';
|
||||
|
@ -1,4 +1,7 @@
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:meta/meta.dart';
|
||||
import 'package:moxlib/moxlib.dart';
|
||||
import 'package:moxxmpp/src/connection.dart';
|
||||
import 'package:moxxmpp/src/errors.dart';
|
||||
import 'package:moxxmpp/src/events.dart';
|
||||
import 'package:moxxmpp/src/jid.dart';
|
||||
@ -6,7 +9,6 @@ import 'package:moxxmpp/src/managers/base.dart';
|
||||
import 'package:moxxmpp/src/settings.dart';
|
||||
import 'package:moxxmpp/src/socket.dart';
|
||||
import 'package:moxxmpp/src/stringxml.dart';
|
||||
import 'package:moxxmpp/src/types/result.dart';
|
||||
|
||||
/// The state a negotiator is currently in
|
||||
enum NegotiatorState {
|
||||
@ -27,6 +29,7 @@ abstract class NegotiatorError extends XmppError {}
|
||||
class NegotiatorAttributes {
|
||||
const NegotiatorAttributes(
|
||||
this.sendNonza,
|
||||
this.getConnection,
|
||||
this.getConnectionSettings,
|
||||
this.sendEvent,
|
||||
this.getNegotiatorById,
|
||||
@ -34,29 +37,59 @@ class NegotiatorAttributes {
|
||||
this.getFullJID,
|
||||
this.getSocket,
|
||||
this.isAuthenticated,
|
||||
this.setAuthenticated,
|
||||
this.setResource,
|
||||
this.removeNegotiatingFeature,
|
||||
);
|
||||
|
||||
/// Sends the nonza nonza and optionally redacts it in logs if redact is not null.
|
||||
final void Function(XMLNode nonza, {String? redact}) sendNonza;
|
||||
final void Function(XMLNode nonza) sendNonza;
|
||||
|
||||
/// Returns the connection settings.
|
||||
final ConnectionSettings Function() getConnectionSettings;
|
||||
/// Send an event event to the connection's event bus
|
||||
|
||||
/// Returns the connection object.
|
||||
final XmppConnection Function() getConnection;
|
||||
|
||||
/// Send an event event to the connection's event bus.
|
||||
final Future<void> Function(XmppEvent event) sendEvent;
|
||||
|
||||
/// Returns the negotiator with id id of the connection or null.
|
||||
final T? Function<T extends XmppFeatureNegotiatorBase>(String) getNegotiatorById;
|
||||
final T? Function<T extends XmppFeatureNegotiatorBase>(String)
|
||||
getNegotiatorById;
|
||||
|
||||
/// Returns the manager with id id of the connection or null.
|
||||
final T? Function<T extends XmppManagerBase>(String) getManagerById;
|
||||
|
||||
/// Returns the full JID of the current account
|
||||
final JID Function() getFullJID;
|
||||
|
||||
/// Returns the socket the negotiator is attached to
|
||||
final BaseSocketWrapper Function() getSocket;
|
||||
|
||||
/// Returns true if the stream is authenticated. Returns false if not.
|
||||
final bool Function() isAuthenticated;
|
||||
|
||||
/// Sets the resource of the connection. If triggerEvent is true, then a
|
||||
/// [ResourceBoundEvent] is triggered.
|
||||
final void Function(String, {bool triggerEvent}) setResource;
|
||||
|
||||
/// Sets the authentication state of the connection to true.
|
||||
final void Function() setAuthenticated;
|
||||
|
||||
/// Remove a stream feature from our internal cache. This is useful for when you
|
||||
/// negotiated a feature for another negotiator, like SASL2.
|
||||
final void Function(String) removeNegotiatingFeature;
|
||||
}
|
||||
|
||||
abstract class XmppFeatureNegotiatorBase {
|
||||
XmppFeatureNegotiatorBase(
|
||||
this.priority,
|
||||
this.sendStreamHeaderWhenDone,
|
||||
this.negotiatingXmlns,
|
||||
this.id,
|
||||
) : state = NegotiatorState.ready;
|
||||
|
||||
XmppFeatureNegotiatorBase(this.priority, this.sendStreamHeaderWhenDone, this.negotiatingXmlns, this.id)
|
||||
: state = NegotiatorState.ready;
|
||||
/// The priority regarding other negotiators. The higher, the earlier will the
|
||||
/// negotiator be used
|
||||
final int priority;
|
||||
@ -70,26 +103,29 @@ abstract class XmppFeatureNegotiatorBase {
|
||||
|
||||
/// The Id of the negotiator
|
||||
final String id;
|
||||
|
||||
|
||||
/// The state the negotiator is currently in
|
||||
NegotiatorState state;
|
||||
|
||||
|
||||
late NegotiatorAttributes _attributes;
|
||||
|
||||
/// Register the negotiator against a connection class by means of [attributes].
|
||||
void register(NegotiatorAttributes attributes) {
|
||||
_attributes = attributes;
|
||||
}
|
||||
|
||||
|
||||
/// Returns true if a feature in [features], which are the children of the
|
||||
/// <stream:features /> nonza, can be negotiated. Otherwise, returns false.
|
||||
bool matchesFeature(List<XMLNode> features) {
|
||||
return firstWhereOrNull(
|
||||
features,
|
||||
(XMLNode feature) => feature.attributes['xmlns'] == negotiatingXmlns,
|
||||
) != null;
|
||||
return features.firstWhereOrNull(
|
||||
(XMLNode feature) => feature.attributes['xmlns'] == negotiatingXmlns,
|
||||
) !=
|
||||
null;
|
||||
}
|
||||
|
||||
/// Called when an event is triggered in the [XmppConnection].
|
||||
Future<void> onXmppEvent(XmppEvent event) async {}
|
||||
|
||||
/// Called with the currently received nonza [nonza] when the negotiator is active.
|
||||
/// If the negotiator is just elected to be the next one, then [nonza] is equal to
|
||||
/// the <stream:features /> nonza.
|
||||
@ -105,6 +141,11 @@ abstract class XmppFeatureNegotiatorBase {
|
||||
void reset() {
|
||||
state = NegotiatorState.ready;
|
||||
}
|
||||
|
||||
|
||||
@protected
|
||||
NegotiatorAttributes get attributes => _attributes;
|
||||
|
||||
/// Run after all negotiators are registered. Useful for registering callbacks against
|
||||
/// other negotiators. By default this function does nothing.
|
||||
Future<void> postRegisterCallback() async {}
|
||||
}
|
||||
|
@ -1,3 +0,0 @@
|
||||
import 'package:moxxmpp/src/negotiators/negotiator.dart';
|
||||
|
||||
class SaslFailedError extends NegotiatorError {}
|
@ -1,46 +0,0 @@
|
||||
enum ParserState {
|
||||
variableName,
|
||||
variableValue
|
||||
}
|
||||
|
||||
/// Parse a string like "n,,n=user,r=fyko+d2lbbFgONRv9qkxdawL" into
|
||||
/// { "n": "user", "r": "fyko+d2lbbFgONRv9qkxdawL"}.
|
||||
Map<String, String> parseKeyValue(String keyValueString) {
|
||||
var state = ParserState.variableName;
|
||||
var name = '';
|
||||
var value = '';
|
||||
final values = <String, String>{};
|
||||
|
||||
for (var i = 0; i < keyValueString.length; i++) {
|
||||
final char = keyValueString[i];
|
||||
switch (state) {
|
||||
case ParserState.variableName: {
|
||||
if (char == '=') {
|
||||
state = ParserState.variableValue;
|
||||
} else if (char == ',') {
|
||||
name = '';
|
||||
} else {
|
||||
name += char;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ParserState.variableValue: {
|
||||
if (char == ',' || i == keyValueString.length - 1) {
|
||||
if (char != ',') {
|
||||
value += char;
|
||||
}
|
||||
|
||||
values[name] = value;
|
||||
value = '';
|
||||
name = '';
|
||||
state = ParserState.variableName;
|
||||
} else {
|
||||
value += char;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return values;
|
||||
}
|
@ -1,13 +0,0 @@
|
||||
import 'package:moxxmpp/src/namespaces.dart';
|
||||
import 'package:moxxmpp/src/stringxml.dart';
|
||||
|
||||
class SaslAuthNonza extends XMLNode {
|
||||
SaslAuthNonza(String mechanism, String body) : super(
|
||||
tag: 'auth',
|
||||
attributes: <String, String>{
|
||||
'xmlns': saslXmlns,
|
||||
'mechanism': mechanism ,
|
||||
},
|
||||
text: body,
|
||||
);
|
||||
}
|
@ -1,73 +0,0 @@
|
||||
import 'dart:convert';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:moxxmpp/src/events.dart';
|
||||
import 'package:moxxmpp/src/negotiators/namespaces.dart';
|
||||
import 'package:moxxmpp/src/negotiators/negotiator.dart';
|
||||
import 'package:moxxmpp/src/negotiators/sasl/errors.dart';
|
||||
import 'package:moxxmpp/src/negotiators/sasl/negotiator.dart';
|
||||
import 'package:moxxmpp/src/negotiators/sasl/nonza.dart';
|
||||
import 'package:moxxmpp/src/stringxml.dart';
|
||||
import 'package:moxxmpp/src/types/result.dart';
|
||||
|
||||
class SaslPlainAuthNonza extends SaslAuthNonza {
|
||||
SaslPlainAuthNonza(String username, String password) : super(
|
||||
'PLAIN', base64.encode(utf8.encode('\u0000$username\u0000$password')),
|
||||
);
|
||||
}
|
||||
|
||||
class SaslPlainNegotiator extends SaslNegotiator {
|
||||
SaslPlainNegotiator()
|
||||
: _authSent = false,
|
||||
_log = Logger('SaslPlainNegotiator'),
|
||||
super(0, saslPlainNegotiator, 'PLAIN');
|
||||
bool _authSent;
|
||||
|
||||
final Logger _log;
|
||||
|
||||
@override
|
||||
bool matchesFeature(List<XMLNode> features) {
|
||||
if (!attributes.getConnectionSettings().allowPlainAuth) return false;
|
||||
|
||||
if (super.matchesFeature(features)) {
|
||||
if (!attributes.getSocket().isSecure()) {
|
||||
_log.warning('Refusing to match SASL feature due to unsecured connection');
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Result<NegotiatorState, NegotiatorError>> negotiate(XMLNode nonza) async {
|
||||
if (!_authSent) {
|
||||
final settings = attributes.getConnectionSettings();
|
||||
attributes.sendNonza(
|
||||
SaslPlainAuthNonza(settings.jid.local, settings.password),
|
||||
redact: SaslPlainAuthNonza('******', '******').toXml(),
|
||||
);
|
||||
_authSent = true;
|
||||
return const Result(NegotiatorState.ready);
|
||||
} else {
|
||||
final tag = nonza.tag;
|
||||
if (tag == 'success') {
|
||||
await attributes.sendEvent(AuthenticationSuccessEvent());
|
||||
return const Result(NegotiatorState.done);
|
||||
} else {
|
||||
// We assume it's a <failure/>
|
||||
final error = nonza.children.first.tag;
|
||||
await attributes.sendEvent(AuthenticationFailedEvent(error));
|
||||
return Result(SaslFailedError());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void reset() {
|
||||
_authSent = false;
|
||||
|
||||
super.reset();
|
||||
}
|
||||
}
|
@ -1,265 +0,0 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:math' show Random;
|
||||
import 'package:cryptography/cryptography.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:moxxmpp/src/events.dart';
|
||||
import 'package:moxxmpp/src/namespaces.dart';
|
||||
import 'package:moxxmpp/src/negotiators/namespaces.dart';
|
||||
import 'package:moxxmpp/src/negotiators/negotiator.dart';
|
||||
import 'package:moxxmpp/src/negotiators/sasl/errors.dart';
|
||||
import 'package:moxxmpp/src/negotiators/sasl/kv.dart';
|
||||
import 'package:moxxmpp/src/negotiators/sasl/negotiator.dart';
|
||||
import 'package:moxxmpp/src/negotiators/sasl/nonza.dart';
|
||||
import 'package:moxxmpp/src/stringxml.dart';
|
||||
import 'package:moxxmpp/src/types/result.dart';
|
||||
import 'package:random_string/random_string.dart';
|
||||
import 'package:saslprep/saslprep.dart';
|
||||
|
||||
// NOTE: Inspired by https://github.com/vukoye/xmpp_dart/blob/3b1a0588562b9e591488c99d834088391840911d/lib/src/features/sasl/ScramSaslHandler.dart
|
||||
|
||||
enum ScramHashType {
|
||||
sha1,
|
||||
sha256,
|
||||
sha512
|
||||
}
|
||||
|
||||
HashAlgorithm hashFromType(ScramHashType type) {
|
||||
switch (type) {
|
||||
case ScramHashType.sha1: return Sha1();
|
||||
case ScramHashType.sha256: return Sha256();
|
||||
case ScramHashType.sha512: return Sha512();
|
||||
}
|
||||
}
|
||||
|
||||
int pbkdfBitsFromHash(ScramHashType type) {
|
||||
switch (type) {
|
||||
// NOTE: SHA1 is 20 octets long => 20 octets * 8 bits/octet
|
||||
case ScramHashType.sha1: return 160;
|
||||
// NOTE: SHA256 is 32 octets long => 32 octets * 8 bits/octet
|
||||
case ScramHashType.sha256: return 256;
|
||||
// NOTE: SHA512 is 64 octets long => 64 octets * 8 bits/octet
|
||||
case ScramHashType.sha512: return 512;
|
||||
}
|
||||
}
|
||||
|
||||
const scramSha1Mechanism = 'SCRAM-SHA-1';
|
||||
const scramSha256Mechanism = 'SCRAM-SHA-256';
|
||||
const scramSha512Mechanism = 'SCRAM-SHA-512';
|
||||
|
||||
String mechanismNameFromType(ScramHashType type) {
|
||||
switch (type) {
|
||||
case ScramHashType.sha1: return scramSha1Mechanism;
|
||||
case ScramHashType.sha256: return scramSha256Mechanism;
|
||||
case ScramHashType.sha512: return scramSha512Mechanism;
|
||||
}
|
||||
}
|
||||
|
||||
String namespaceFromType(ScramHashType type) {
|
||||
switch (type) {
|
||||
case ScramHashType.sha1: return saslScramSha1Negotiator;
|
||||
case ScramHashType.sha256: return saslScramSha256Negotiator;
|
||||
case ScramHashType.sha512: return saslScramSha512Negotiator;
|
||||
}
|
||||
}
|
||||
|
||||
class SaslScramAuthNonza extends SaslAuthNonza {
|
||||
// This subclassing makes less sense here, but this is since the auth nonza here
|
||||
// requires knowledge of the inner state of the Negotiator.
|
||||
SaslScramAuthNonza({ required ScramHashType type, required String body }) : super(
|
||||
mechanismNameFromType(type), body,
|
||||
);
|
||||
}
|
||||
|
||||
class SaslScramResponseNonza extends XMLNode {
|
||||
SaslScramResponseNonza({ required String body }) : super(
|
||||
tag: 'response',
|
||||
attributes: <String, String>{
|
||||
'xmlns': saslXmlns,
|
||||
},
|
||||
text: body,
|
||||
);
|
||||
}
|
||||
|
||||
enum ScramState {
|
||||
preSent,
|
||||
initialMessageSent,
|
||||
challengeResponseSent,
|
||||
error
|
||||
}
|
||||
|
||||
const gs2Header = 'n,,';
|
||||
|
||||
class SaslScramNegotiator extends SaslNegotiator {
|
||||
// NOTE: NEVER, and I mean, NEVER set clientNonce or initalMessageNoGS2. They are just there for testing
|
||||
SaslScramNegotiator(
|
||||
int priority,
|
||||
this.initialMessageNoGS2,
|
||||
this.clientNonce,
|
||||
this.hashType,
|
||||
) :
|
||||
_hash = hashFromType(hashType),
|
||||
_serverSignature = '',
|
||||
_scramState = ScramState.preSent,
|
||||
_log = Logger('SaslScramNegotiator(${mechanismNameFromType(hashType)})'),
|
||||
super(priority, namespaceFromType(hashType), mechanismNameFromType(hashType));
|
||||
String? clientNonce;
|
||||
String initialMessageNoGS2;
|
||||
final ScramHashType hashType;
|
||||
final HashAlgorithm _hash;
|
||||
String _serverSignature;
|
||||
|
||||
// The internal state for performing the negotiation
|
||||
ScramState _scramState;
|
||||
|
||||
final Logger _log;
|
||||
|
||||
Future<List<int>> calculateSaltedPassword(String salt, int iterations) async {
|
||||
final pbkdf2 = Pbkdf2(
|
||||
macAlgorithm: Hmac(_hash),
|
||||
iterations: iterations,
|
||||
bits: pbkdfBitsFromHash(hashType),
|
||||
);
|
||||
|
||||
final saltedPasswordRaw = await pbkdf2.deriveKey(
|
||||
secretKey: SecretKey(
|
||||
utf8.encode(Saslprep.saslprep(attributes.getConnectionSettings().password)),
|
||||
),
|
||||
nonce: base64.decode(salt),
|
||||
);
|
||||
return saltedPasswordRaw.extractBytes();
|
||||
}
|
||||
|
||||
Future<List<int>> calculateClientKey(List<int> saltedPassword) async {
|
||||
return (await Hmac(_hash).calculateMac(
|
||||
utf8.encode('Client Key'), secretKey: SecretKey(saltedPassword),
|
||||
)).bytes;
|
||||
}
|
||||
|
||||
Future<List<int>> calculateClientSignature(String authMessage, List<int> storedKey) async {
|
||||
return (await Hmac(_hash).calculateMac(
|
||||
utf8.encode(authMessage),
|
||||
secretKey: SecretKey(storedKey),
|
||||
)).bytes;
|
||||
}
|
||||
|
||||
Future<List<int>> calculateServerKey(List<int> saltedPassword) async {
|
||||
return (await Hmac(_hash).calculateMac(
|
||||
utf8.encode('Server Key'),
|
||||
secretKey: SecretKey(saltedPassword),
|
||||
)).bytes;
|
||||
}
|
||||
|
||||
Future<List<int>> calculateServerSignature(String authMessage, List<int> serverKey) async {
|
||||
return (await Hmac(_hash).calculateMac(
|
||||
utf8.encode(authMessage),
|
||||
secretKey: SecretKey(serverKey),
|
||||
)).bytes;
|
||||
}
|
||||
|
||||
List<int> calculateClientProof(List<int> clientKey, List<int> clientSignature) {
|
||||
final clientProof = List<int>.filled(clientKey.length, 0);
|
||||
for (var i = 0; i < clientKey.length; i++) {
|
||||
clientProof[i] = clientKey[i] ^ clientSignature[i];
|
||||
}
|
||||
|
||||
return clientProof;
|
||||
}
|
||||
|
||||
Future<String> calculateChallengeResponse(String base64Challenge) async {
|
||||
final challengeString = utf8.decode(base64.decode(base64Challenge));
|
||||
final challenge = parseKeyValue(challengeString);
|
||||
final clientFinalMessageBare = 'c=biws,r=${challenge['r']!}';
|
||||
|
||||
final saltedPassword = await calculateSaltedPassword(challenge['s']!, int.parse(challenge['i']!));
|
||||
final clientKey = await calculateClientKey(saltedPassword);
|
||||
final storedKey = (await _hash.hash(clientKey)).bytes;
|
||||
final authMessage = '$initialMessageNoGS2,$challengeString,$clientFinalMessageBare';
|
||||
final clientSignature = await calculateClientSignature(authMessage, storedKey);
|
||||
final clientProof = calculateClientProof(clientKey, clientSignature);
|
||||
final serverKey = await calculateServerKey(saltedPassword);
|
||||
_serverSignature = base64.encode(await calculateServerSignature(authMessage, serverKey));
|
||||
|
||||
return '$clientFinalMessageBare,p=${base64.encode(clientProof)}';
|
||||
}
|
||||
|
||||
@override
|
||||
bool matchesFeature(List<XMLNode> features) {
|
||||
if (super.matchesFeature(features)) {
|
||||
if (!attributes.getSocket().isSecure()) {
|
||||
_log.warning('Refusing to match SASL feature due to unsecured connection');
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Result<NegotiatorState, NegotiatorError>> negotiate(XMLNode nonza) async {
|
||||
switch (_scramState) {
|
||||
case ScramState.preSent:
|
||||
if (clientNonce == null || clientNonce == '') {
|
||||
clientNonce = randomAlphaNumeric(40, provider: CoreRandomProvider.from(Random.secure()));
|
||||
}
|
||||
|
||||
initialMessageNoGS2 = 'n=${attributes.getConnectionSettings().jid.local},r=$clientNonce';
|
||||
|
||||
_scramState = ScramState.initialMessageSent;
|
||||
attributes.sendNonza(
|
||||
SaslScramAuthNonza(body: base64.encode(utf8.encode(gs2Header + initialMessageNoGS2)), type: hashType),
|
||||
redact: SaslScramAuthNonza(body: '******', type: hashType).toXml(),
|
||||
);
|
||||
return const Result(NegotiatorState.ready);
|
||||
case ScramState.initialMessageSent:
|
||||
if (nonza.tag != 'challenge') {
|
||||
final error = nonza.children.first.tag;
|
||||
await attributes.sendEvent(AuthenticationFailedEvent(error));
|
||||
|
||||
_scramState = ScramState.error;
|
||||
return Result(SaslFailedError());
|
||||
}
|
||||
|
||||
final challengeBase64 = nonza.innerText();
|
||||
final response = await calculateChallengeResponse(challengeBase64);
|
||||
final responseBase64 = base64.encode(utf8.encode(response));
|
||||
_scramState = ScramState.challengeResponseSent;
|
||||
attributes.sendNonza(
|
||||
SaslScramResponseNonza(body: responseBase64),
|
||||
redact: SaslScramResponseNonza(body: '******').toXml(),
|
||||
);
|
||||
return const Result(NegotiatorState.ready);
|
||||
case ScramState.challengeResponseSent:
|
||||
if (nonza.tag != 'success') {
|
||||
// We assume it's a <failure />
|
||||
final error = nonza.children.first.tag;
|
||||
await attributes.sendEvent(AuthenticationFailedEvent(error));
|
||||
_scramState = ScramState.error;
|
||||
return Result(SaslFailedError());
|
||||
}
|
||||
|
||||
// NOTE: This assumes that the string is always "v=..." and contains no other parameters
|
||||
final signature = parseKeyValue(utf8.decode(base64.decode(nonza.innerText())));
|
||||
if (signature['v']! != _serverSignature) {
|
||||
// TODO(Unknown): Notify of a signature mismatch
|
||||
//final error = nonza.children.first.tag;
|
||||
//attributes.sendEvent(AuthenticationFailedEvent(error));
|
||||
_scramState = ScramState.error;
|
||||
return Result(SaslFailedError());
|
||||
}
|
||||
|
||||
await attributes.sendEvent(AuthenticationSuccessEvent());
|
||||
return const Result(NegotiatorState.done);
|
||||
case ScramState.error:
|
||||
return Result(SaslFailedError());
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void reset() {
|
||||
_scramState = ScramState.preSent;
|
||||
|
||||
super.reset();
|
||||
}
|
||||
}
|