Skip to content

Commit 94b13c3

Browse files
committed
Refactor _parseContactMessage to improve error handling and message parsing logic
1 parent 9470809 commit 94b13c3

File tree

1 file changed

+80
-57
lines changed

1 file changed

+80
-57
lines changed

lib/connector/meshcore_connector.dart

Lines changed: 80 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -2459,70 +2459,93 @@ class MeshCoreConnector extends ChangeNotifier {
24592459
}
24602460

24612461
Message? _parseContactMessage(Uint8List frame) {
2462-
if (frame.isEmpty) return null;
2463-
final code = frame[0];
2464-
if (code != respCodeContactMsgRecv && code != respCodeContactMsgRecvV3) {
2462+
if (frame.isEmpty) {
2463+
appLogger.warn('Received empty frame, ignoring');
24652464
return null;
24662465
}
2466+
final reader = BufferReader(frame);
24672467

2468-
// Companion radio layout:
2469-
// [code][snr?][res?][res?][prefix x6][path_len][txt_type][timestamp x4][extra?][text...]
2470-
final prefixOffset = code == respCodeContactMsgRecvV3 ? 4 : 1;
2471-
const prefixLen = 6;
2472-
final pathLenOffset = prefixOffset + prefixLen;
2473-
final txtTypeOffset = pathLenOffset + 1;
2474-
final timestampOffset = txtTypeOffset + 1;
2475-
final baseTextOffset = timestampOffset + 4;
2476-
2477-
if (frame.length <= baseTextOffset) return null;
2478-
final fourBytePubMSG = frame.sublist(baseTextOffset, baseTextOffset + 4);
2479-
final senderPrefix = frame.sublist(prefixOffset, prefixOffset + prefixLen);
2480-
final flags = frame[txtTypeOffset];
2481-
final shiftedType = flags >> 2;
2482-
final rawType = flags;
2483-
final isPlain = shiftedType == txtTypePlain || rawType == txtTypePlain;
2484-
final isCli = shiftedType == txtTypeCliData || rawType == txtTypeCliData;
2485-
if (!isPlain && !isCli) {
2486-
return null;
2487-
}
2468+
try {
2469+
final code = reader.readByte();
2470+
if (code != respCodeContactMsgRecv && code != respCodeContactMsgRecvV3) {
2471+
appLogger.warn(
2472+
'Unexpected message code: $code, expected contact message receive codes',
2473+
);
2474+
return null;
2475+
}
24882476

2489-
// Try base text offset; if empty and there is room for the optional 4-byte extra
2490-
// (used by signed/plain variants), try again skipping those bytes.
2491-
var text = readCString(
2492-
frame,
2493-
baseTextOffset,
2494-
frame.length - baseTextOffset,
2495-
);
2496-
if (text.isEmpty && frame.length > baseTextOffset + 4) {
2497-
text = readCString(
2498-
frame,
2499-
baseTextOffset + 4,
2500-
frame.length - (baseTextOffset + 4),
2477+
// Companion radio layout:
2478+
// [code][snr?][res?][res?][prefix x6][path_len][txt_type][timestamp x4][extra?][text...]
2479+
// double snr = 0;
2480+
if (code == respCodeContactMsgRecvV3) {
2481+
// Older firmware layout with SNR as a signed byte after the code
2482+
// snr = reader.readInt8().toDouble() * 4; // SNR in dB, scaled by 4
2483+
reader.skipBytes(1); // Skip SNR byte
2484+
reader.skipBytes(2); // Skip reserved bytes
2485+
}
2486+
2487+
final senderPrefix = reader.readBytes(6);
2488+
final pathLength = reader.readByte();
2489+
final txtType = reader.readByte();
2490+
final timestampRaw = reader.readInt32LE();
2491+
final timestamp = DateTime.fromMillisecondsSinceEpoch(
2492+
timestampRaw * 1000,
25012493
);
2502-
}
2503-
if (text.isEmpty) return null;
2504-
final decodedText = isCli ? text : (Smaz.tryDecodePrefixed(text) ?? text);
25052494

2506-
final timestampRaw = readUint32LE(frame, timestampOffset);
2507-
final pathLenByte = frame[pathLenOffset];
2495+
if (txtType == 2) {
2496+
reader.skipBytes(4); // Skip extra 4 bytes for signed/plain variants
2497+
}
25082498

2509-
final contact = _contacts.cast<Contact?>().firstWhere(
2510-
(c) => c != null && _matchesPrefix(c.publicKey, senderPrefix),
2511-
orElse: () => null,
2512-
);
2513-
if (contact == null) return null;
2514-
2515-
return Message(
2516-
senderKey: contact.publicKey,
2517-
text: decodedText,
2518-
timestamp: DateTime.fromMillisecondsSinceEpoch(timestampRaw * 1000),
2519-
isOutgoing: false,
2520-
isCli: isCli,
2521-
status: MessageStatus.delivered,
2522-
pathLength: pathLenByte == 0xFF ? 0 : pathLenByte,
2523-
pathBytes: Uint8List(0),
2524-
fourByteRoomContactKey: fourBytePubMSG,
2525-
);
2499+
final msgText = reader.readString();
2500+
2501+
final flags = txtType;
2502+
final shiftedType = flags >> 2;
2503+
final rawType = flags;
2504+
final isPlain = shiftedType == txtTypePlain || rawType == txtTypePlain;
2505+
final isCli = shiftedType == txtTypeCliData || rawType == txtTypeCliData;
2506+
if (!isPlain && !isCli) {
2507+
appLogger.warn(
2508+
'Unknown message type received: txtType=$txtType, shifted=$shiftedType, raw=$rawType',
2509+
);
2510+
return null;
2511+
}
2512+
2513+
if (msgText.isEmpty) {
2514+
appLogger.warn('Received message with empty text, ignoring');
2515+
return null;
2516+
}
2517+
final decodedText = isCli
2518+
? msgText
2519+
: (Smaz.tryDecodePrefixed(msgText) ?? msgText);
2520+
2521+
final contact = _contacts.cast<Contact?>().firstWhere(
2522+
(c) => c != null && _matchesPrefix(c.publicKey, senderPrefix),
2523+
orElse: () => null,
2524+
);
2525+
if (contact == null) {
2526+
appLogger.warn(
2527+
'Received message from unknown contact with prefix: ${senderPrefix.map((b) => b.toRadixString(16).padLeft(2, '0').toUpperCase()).join('')}',
2528+
);
2529+
return null;
2530+
}
2531+
2532+
return Message(
2533+
senderKey: contact.publicKey,
2534+
text: decodedText,
2535+
timestamp: timestamp,
2536+
isOutgoing: false,
2537+
isCli: isCli,
2538+
status: MessageStatus.delivered,
2539+
pathLength: pathLength == 0xFF ? 0 : pathLength,
2540+
pathBytes: Uint8List(0),
2541+
fourByteRoomContactKey: msgText.length >= 4
2542+
? Uint8List.fromList(msgText.substring(0, 4).codeUnits)
2543+
: null,
2544+
);
2545+
} catch (e) {
2546+
appLogger.warn('Error parsing contact direct message: $e');
2547+
return null;
2548+
}
25262549
}
25272550

25282551
bool _matchesPrefix(Uint8List fullKey, Uint8List prefix) {

0 commit comments

Comments
 (0)