Rebrand application as Qlyra
Build Android (FCM) / build-android-fcm (push) Canceled after 0s
Build Android / build-android (push) Canceled after 0s
Build iOS / build-ios (push) Canceled after 0s
Build Linux / build-linux (push) Canceled after 0s
Build macOS / build-macos (push) Canceled after 0s
Build Windows / build-windows (push) Canceled after 0s
Release (main) / android (oneme) (push) Canceled after 0s
Release (main) / android (qlyra) (push) Canceled after 0s
Release (main) / windows (push) Canceled after 0s
Release (main) / linux (push) Canceled after 0s
Release (main) / macos (push) Canceled after 0s
Release (main) / ios (push) Canceled after 0s
Release (main) / release (push) Canceled after 0s
Build Android (FCM) / build-android-fcm (push) Canceled after 0s
Build Android / build-android (push) Canceled after 0s
Build iOS / build-ios (push) Canceled after 0s
Build Linux / build-linux (push) Canceled after 0s
Build macOS / build-macos (push) Canceled after 0s
Build Windows / build-windows (push) Canceled after 0s
Release (main) / android (oneme) (push) Canceled after 0s
Release (main) / android (qlyra) (push) Canceled after 0s
Release (main) / windows (push) Canceled after 0s
Release (main) / linux (push) Canceled after 0s
Release (main) / macos (push) Canceled after 0s
Release (main) / ios (push) Canceled after 0s
Release (main) / release (push) Canceled after 0s
This commit is contained in:
@@ -28,6 +28,8 @@ abstract class MaxWebCmd {
|
||||
abstract class MaxWebFraming {
|
||||
static const int protocolVersion = 10;
|
||||
static const int headerSize = 10;
|
||||
static const int maxPayloadSize = 0x00ffffff;
|
||||
static const int maxDecompressedPayloadSize = 64 * 1024 * 1024;
|
||||
|
||||
static Uint8List encode({
|
||||
required int cmd,
|
||||
@@ -35,9 +37,14 @@ abstract class MaxWebFraming {
|
||||
required int opcode,
|
||||
Object? payload,
|
||||
}) {
|
||||
final body = payload == null
|
||||
? Uint8List(0)
|
||||
: MaxMsgpack.encode(payload);
|
||||
final body = payload == null ? Uint8List(0) : MaxMsgpack.encode(payload);
|
||||
if (body.length > maxPayloadSize) {
|
||||
throw ArgumentError.value(
|
||||
body.length,
|
||||
'payload',
|
||||
'превышает допустимый размер кадра',
|
||||
);
|
||||
}
|
||||
final frame = Uint8List(headerSize + body.length);
|
||||
final view = ByteData.view(frame.buffer);
|
||||
|
||||
@@ -66,6 +73,12 @@ abstract class MaxWebFraming {
|
||||
final compressionRatio = view.getUint8(6);
|
||||
final length =
|
||||
(view.getUint8(7) << 16) | (view.getUint8(8) << 8) | view.getUint8(9);
|
||||
final expectedLength = headerSize + length;
|
||||
if (frame.length != expectedLength) {
|
||||
throw FormatException(
|
||||
'MaxWebFraming: длина кадра ${frame.length} не совпадает с $expectedLength',
|
||||
);
|
||||
}
|
||||
|
||||
if (length <= 0) {
|
||||
return MaxWebFrame(cmd: cmd, seq: seq, opcode: opcode);
|
||||
@@ -73,7 +86,7 @@ abstract class MaxWebFraming {
|
||||
|
||||
var body = Uint8List.sublistView(frame, headerSize, headerSize + length);
|
||||
if (compressionRatio > 0) {
|
||||
body = Lz4Block.decompress(body, length * compressionRatio * 16);
|
||||
body = Lz4Block.decompress(body, maxDecompressedPayloadSize);
|
||||
}
|
||||
|
||||
return MaxWebFrame(
|
||||
@@ -87,51 +100,67 @@ abstract class MaxWebFraming {
|
||||
|
||||
abstract class Lz4Block {
|
||||
static Uint8List decompress(Uint8List source, int maxOutputSize) {
|
||||
final output = Uint8List(maxOutputSize);
|
||||
if (maxOutputSize < 0) {
|
||||
throw ArgumentError.value(maxOutputSize, 'maxOutputSize');
|
||||
}
|
||||
final output = <int>[];
|
||||
var input = 0;
|
||||
var written = 0;
|
||||
|
||||
while (input < source.length) {
|
||||
final token = source[input++];
|
||||
|
||||
var literalLength = token >> 4;
|
||||
if (literalLength == 15) {
|
||||
literalLength += _readLengthExtension(source, () => input, (v) => input = v);
|
||||
literalLength += _readLengthExtension(
|
||||
source,
|
||||
() => input,
|
||||
(v) => input = v,
|
||||
);
|
||||
}
|
||||
|
||||
if (written + literalLength > output.length) {
|
||||
if (literalLength > source.length - input) {
|
||||
throw const FormatException('Lz4Block: обрыв литералов');
|
||||
}
|
||||
if (literalLength > maxOutputSize - output.length) {
|
||||
throw const FormatException('Lz4Block: литералы не помещаются');
|
||||
}
|
||||
output.setRange(written, written + literalLength,
|
||||
Uint8List.sublistView(source, input, input + literalLength));
|
||||
written += literalLength;
|
||||
output.addAll(
|
||||
Uint8List.sublistView(source, input, input + literalLength),
|
||||
);
|
||||
input += literalLength;
|
||||
|
||||
if (input >= source.length) break;
|
||||
|
||||
if (source.length - input < 2) {
|
||||
throw const FormatException('Lz4Block: обрыв смещения совпадения');
|
||||
}
|
||||
|
||||
final offset = source[input] | (source[input + 1] << 8);
|
||||
input += 2;
|
||||
if (offset == 0 || offset > written) {
|
||||
if (offset == 0 || offset > output.length) {
|
||||
throw const FormatException('Lz4Block: неверное смещение совпадения');
|
||||
}
|
||||
|
||||
var matchLength = token & 0x0F;
|
||||
if (matchLength == 15) {
|
||||
matchLength += _readLengthExtension(source, () => input, (v) => input = v);
|
||||
matchLength += _readLengthExtension(
|
||||
source,
|
||||
() => input,
|
||||
(v) => input = v,
|
||||
);
|
||||
}
|
||||
matchLength += 4;
|
||||
|
||||
if (written + matchLength > output.length) {
|
||||
if (matchLength > maxOutputSize - output.length) {
|
||||
throw const FormatException('Lz4Block: совпадение не помещается');
|
||||
}
|
||||
|
||||
var from = written - offset;
|
||||
for (var i = 0; i < matchLength; i++) {
|
||||
output[written++] = output[from++];
|
||||
output.add(output[output.length - offset]);
|
||||
}
|
||||
}
|
||||
|
||||
return Uint8List.sublistView(output, 0, written);
|
||||
return Uint8List.fromList(output);
|
||||
}
|
||||
|
||||
static int _readLengthExtension(
|
||||
@@ -191,7 +220,9 @@ abstract class MaxMsgpack {
|
||||
_write(sink, item);
|
||||
});
|
||||
} else {
|
||||
throw ArgumentError('MaxMsgpack: неподдерживаемый тип ${value.runtimeType}');
|
||||
throw ArgumentError(
|
||||
'MaxMsgpack: неподдерживаемый тип ${value.runtimeType}',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -306,11 +337,8 @@ abstract class MaxMsgpack {
|
||||
}
|
||||
|
||||
class _Reader {
|
||||
_Reader(this._bytes) : _view = ByteData.view(
|
||||
_bytes.buffer,
|
||||
_bytes.offsetInBytes,
|
||||
_bytes.length,
|
||||
);
|
||||
_Reader(this._bytes)
|
||||
: _view = ByteData.view(_bytes.buffer, _bytes.offsetInBytes, _bytes.length);
|
||||
|
||||
final Uint8List _bytes;
|
||||
final ByteData _view;
|
||||
@@ -393,7 +421,9 @@ class _Reader {
|
||||
if (byte == 0xC8) return _ext(_u16());
|
||||
if (byte == 0xC9) return _ext(_u32());
|
||||
|
||||
throw FormatException('MaxMsgpack: неизвестный маркер 0x${byte.toRadixString(16)}');
|
||||
throw FormatException(
|
||||
'MaxMsgpack: неизвестный маркер 0x${byte.toRadixString(16)}',
|
||||
);
|
||||
}
|
||||
|
||||
static const int _numberExtType = 1;
|
||||
|
||||
@@ -81,7 +81,8 @@ class MaxWebSocketSession {
|
||||
_subscription = socket.listen(
|
||||
_onFrame,
|
||||
onError: (Object error) => _failAll(MaxWebException('сокет: $error')),
|
||||
onDone: () => _failAll(const MaxWebException('соединение закрыто сервером')),
|
||||
onDone: () =>
|
||||
_failAll(const MaxWebException('соединение закрыто сервером')),
|
||||
cancelOnError: true,
|
||||
);
|
||||
|
||||
@@ -168,7 +169,9 @@ class MaxWebSocketSession {
|
||||
return MaxWebException(code, code: code);
|
||||
}
|
||||
}
|
||||
return MaxWebException('опкод ${frame.opcode}: ошибка сервера (cmd=${frame.cmd})');
|
||||
return MaxWebException(
|
||||
'опкод ${frame.opcode}: ошибка сервера (cmd=${frame.cmd})',
|
||||
);
|
||||
}
|
||||
|
||||
void _failAll(MaxWebException error) {
|
||||
|
||||
@@ -194,7 +194,10 @@ class WebPushService {
|
||||
throw const MaxWebException('время подтверждения истекло');
|
||||
}
|
||||
|
||||
Future<WebPushAuthStep> submitPassword(String trackId, String password) async {
|
||||
Future<WebPushAuthStep> submitPassword(
|
||||
String trackId,
|
||||
String password,
|
||||
) async {
|
||||
final socket = _requireSocket();
|
||||
final payload = _asMap(
|
||||
await socket.request(Opcode.authLoginCheckPassword, <String, Object?>{
|
||||
@@ -222,7 +225,9 @@ class WebPushService {
|
||||
Future<void> registerSubscription(WebPushSubscription subscription) async {
|
||||
final token = await TokenStorage.readSecure(_tokenKey);
|
||||
if (token == null || token.isEmpty) {
|
||||
throw const MaxWebException('сначала подключите уведомления в настройках');
|
||||
throw const MaxWebException(
|
||||
'сначала подключите уведомления в настройках',
|
||||
);
|
||||
}
|
||||
|
||||
final socket = MaxWebSocketSession(device: await device());
|
||||
|
||||
Reference in New Issue
Block a user