eufy-security-client
Version:
Client to comunicate with Eufy-Security devices
217 lines • 8.48 kB
JavaScript
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.PushClientParser = void 0;
const path = __importStar(require("path"));
const protobufjs_1 = require("protobufjs");
const tiny_typed_emitter_1 = require("tiny-typed-emitter");
const models_1 = require("./models");
const error_1 = require("../error");
const error_2 = require("./error");
const logging_1 = require("../logging");
class PushClientParser extends tiny_typed_emitter_1.TypedEmitter {
static proto = null;
state = models_1.ProcessingState.MCS_VERSION_TAG_AND_SIZE;
data = Buffer.alloc(0);
isWaitingForData = true;
sizePacketSoFar = 0;
messageSize = 0;
messageTag = 0;
handshakeComplete = false;
constructor() {
super();
}
resetState() {
this.state = models_1.ProcessingState.MCS_VERSION_TAG_AND_SIZE;
this.data = Buffer.alloc(0);
this.isWaitingForData = true;
this.sizePacketSoFar = 0;
this.messageSize = 0;
this.messageTag = 0;
this.handshakeComplete = false;
this.removeAllListeners();
}
static async init() {
this.proto = await (0, protobufjs_1.load)(path.join(__dirname, "./proto/mcs.proto"));
return new PushClientParser();
}
handleData(newData) {
this.data = Buffer.concat([this.data, newData]);
if (this.isWaitingForData) {
this.isWaitingForData = false;
this.waitForData();
}
}
waitForData() {
const minBytesNeeded = this.getMinBytesNeeded();
// If we don't have all bytes yet, wait some more
if (this.data.length < minBytesNeeded) {
this.isWaitingForData = true;
return;
}
else {
this.handleFullMessage();
}
}
handleFullMessage() {
switch (this.state) {
case models_1.ProcessingState.MCS_VERSION_TAG_AND_SIZE:
this.onGotVersion();
break;
case models_1.ProcessingState.MCS_TAG_AND_SIZE:
this.onGotMessageTag();
break;
case models_1.ProcessingState.MCS_SIZE:
this.onGotMessageSize();
break;
case models_1.ProcessingState.MCS_PROTO_BYTES:
this.onGotMessageBytes();
break;
default:
logging_1.rootPushLogger.warn("Push Parser - Unknown state", { state: this.state });
break;
}
}
onGotVersion() {
const version = this.data.readInt8(0);
this.data = this.data.subarray(1);
if (version < 41 && version !== 38) {
throw new error_2.MCSProtocolVersionError("Got wrong protocol version", { context: { version: version } });
}
// Process the LoginResponse message tag.
this.onGotMessageTag();
}
onGotMessageTag() {
this.messageTag = this.data.readInt8(0);
this.data = this.data.subarray(1);
this.onGotMessageSize();
}
onGotMessageSize() {
let incompleteSizePacket = false;
const reader = new protobufjs_1.BufferReader(this.data);
try {
this.messageSize = reader.int32();
}
catch (err) {
const error = (0, error_1.ensureError)(err);
if (error instanceof Error && error.message.startsWith("index out of range:")) {
incompleteSizePacket = true;
}
else {
throw error;
}
}
if (incompleteSizePacket) {
this.sizePacketSoFar = reader.pos;
this.state = models_1.ProcessingState.MCS_SIZE;
this.waitForData();
return;
}
this.data = this.data.subarray(reader.pos);
this.sizePacketSoFar = 0;
if (this.messageSize > 0) {
this.state = models_1.ProcessingState.MCS_PROTO_BYTES;
this.waitForData();
}
else {
this.onGotMessageBytes();
}
}
onGotMessageBytes() {
const protobuf = this.buildProtobufFromTag(this.messageTag);
if (this.messageSize === 0) {
this.emit("message", { tag: this.messageTag, object: {} });
this.getNextMessage();
return;
}
if (this.data.length < this.messageSize) {
this.state = models_1.ProcessingState.MCS_PROTO_BYTES;
this.waitForData();
return;
}
const buffer = this.data.subarray(0, this.messageSize);
this.data = this.data.subarray(this.messageSize);
const message = protobuf.decode(buffer);
const object = protobuf.toObject(message, {
longs: String,
enums: String,
bytes: Buffer,
});
this.emit("message", { tag: this.messageTag, object: object });
if (this.messageTag === models_1.MessageTag.LoginResponse) {
if (this.handshakeComplete) {
logging_1.rootPushLogger.error("Push Parser - Unexpected login response!");
}
else {
this.handshakeComplete = true;
}
}
this.getNextMessage();
}
getNextMessage() {
this.messageTag = 0;
this.messageSize = 0;
this.state = models_1.ProcessingState.MCS_TAG_AND_SIZE;
this.waitForData();
}
getMinBytesNeeded() {
switch (this.state) {
case models_1.ProcessingState.MCS_VERSION_TAG_AND_SIZE:
return 1 + 1 + 1;
case models_1.ProcessingState.MCS_TAG_AND_SIZE:
return 1 + 1;
case models_1.ProcessingState.MCS_SIZE:
return this.sizePacketSoFar + 1;
case models_1.ProcessingState.MCS_PROTO_BYTES:
return this.messageSize;
default:
throw new error_2.MCSProtocolProcessingStateError("Unknown protocol processing state", { context: { state: this.state } });
}
}
buildProtobufFromTag(messageTag) {
switch (messageTag) {
case models_1.MessageTag.HeartbeatPing:
return PushClientParser.proto.lookupType("mcs_proto.HeartbeatPing");
case models_1.MessageTag.HeartbeatAck:
return PushClientParser.proto.lookupType("mcs_proto.HeartbeatAck");
case models_1.MessageTag.LoginRequest:
return PushClientParser.proto.lookupType("mcs_proto.LoginRequest");
case models_1.MessageTag.LoginResponse:
return PushClientParser.proto.lookupType("mcs_proto.LoginResponse");
case models_1.MessageTag.Close:
return PushClientParser.proto.lookupType("mcs_proto.Close");
case models_1.MessageTag.IqStanza:
return PushClientParser.proto.lookupType("mcs_proto.IqStanza");
case models_1.MessageTag.DataMessageStanza:
return PushClientParser.proto.lookupType("mcs_proto.DataMessageStanza");
case models_1.MessageTag.StreamErrorStanza:
return PushClientParser.proto.lookupType("mcs_proto.StreamErrorStanza");
default:
throw new error_2.MCSProtocolMessageTagError("Unknown protocol message tag", { context: { messageTag: this.messageTag } });
}
}
}
exports.PushClientParser = PushClientParser;
//# sourceMappingURL=parser.js.map