UNPKG

hap-nodejs

Version:

HAP-NodeJS is a Node.js implementation of HomeKit Accessory Server.

726 lines 35.4 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.ResolvedAdvertiser = exports.AvahiAdvertiser = exports.DBusInvokeError = exports.BonjourHAPAdvertiser = exports.CiaoAdvertiser = exports.AdvertiserEvent = exports.PairingFeatureFlag = exports.StatusFlag = void 0; var tslib_1 = require("tslib"); // eslint-disable-next-line @typescript-eslint/triple-slash-reference /// <reference path="../../@types/bonjour-hap.d.ts" /> var ciao_1 = tslib_1.__importDefault(require("@homebridge/ciao")); var dbus_native_1 = tslib_1.__importDefault(require("@homebridge/dbus-native")); var assert_1 = tslib_1.__importDefault(require("assert")); var bonjour_hap_1 = tslib_1.__importDefault(require("bonjour-hap")); var crypto_1 = tslib_1.__importDefault(require("crypto")); var debug_1 = tslib_1.__importDefault(require("debug")); var events_1 = require("events"); var promise_utils_1 = require("./util/promise-utils"); var debug = (0, debug_1.default)("HAP-NodeJS:Advertiser"); /** * This enum lists all bitmasks for all known status flags. * When the bit for the given bitmask is set, it represents the state described by the name. * * @group Advertiser */ var StatusFlag; (function (StatusFlag) { StatusFlag[StatusFlag["NOT_PAIRED"] = 1] = "NOT_PAIRED"; StatusFlag[StatusFlag["NOT_JOINED_WIFI"] = 2] = "NOT_JOINED_WIFI"; StatusFlag[StatusFlag["PROBLEM_DETECTED"] = 4] = "PROBLEM_DETECTED"; })(StatusFlag || (exports.StatusFlag = StatusFlag = {})); /** * This enum lists all bitmasks for all known pairing feature flags. * When the bit for the given bitmask is set, it represents the state described by the name. * * @group Advertiser */ var PairingFeatureFlag; (function (PairingFeatureFlag) { PairingFeatureFlag[PairingFeatureFlag["SUPPORTS_HARDWARE_AUTHENTICATION"] = 1] = "SUPPORTS_HARDWARE_AUTHENTICATION"; PairingFeatureFlag[PairingFeatureFlag["SUPPORTS_SOFTWARE_AUTHENTICATION"] = 2] = "SUPPORTS_SOFTWARE_AUTHENTICATION"; })(PairingFeatureFlag || (exports.PairingFeatureFlag = PairingFeatureFlag = {})); /** * @group Advertiser */ var AdvertiserEvent; (function (AdvertiserEvent) { /** * Emitted if the underlying mDNS advertisers signals, that the service name * was automatically changed due to some naming conflicts on the network. */ AdvertiserEvent["UPDATED_NAME"] = "updated-name"; })(AdvertiserEvent || (exports.AdvertiserEvent = AdvertiserEvent = {})); /** * Advertiser uses mdns to broadcast the presence of an Accessory to the local network. * * Note that as of iOS 9, an accessory can only pair with a single client. Instead of pairing your * accessories with multiple iOS devices in your home, Apple intends for you to use Home Sharing. * To support this requirement, we provide the ability to be "discoverable" or not (via a "service flag" on the * mdns payload). * * @group Advertiser */ var CiaoAdvertiser = /** @class */ (function (_super) { tslib_1.__extends(CiaoAdvertiser, _super); function CiaoAdvertiser(accessoryInfo, responderOptions, serviceOptions) { var _this = _super.call(this) || this; _this.accessoryInfo = accessoryInfo; _this.setupHash = CiaoAdvertiser.computeSetupHash(accessoryInfo); _this.responder = ciao_1.default.getResponder(tslib_1.__assign({}, responderOptions)); _this.advertisedService = _this.responder.createService(tslib_1.__assign({ name: _this.accessoryInfo.displayName, type: "hap" /* ServiceType.HAP */, txt: CiaoAdvertiser.createTxt(accessoryInfo, _this.setupHash) }, serviceOptions)); _this.advertisedService.on("name-change" /* ServiceEvent.NAME_CHANGED */, _this.emit.bind(_this, "updated-name" /* AdvertiserEvent.UPDATED_NAME */)); debug("Preparing Advertiser for '".concat(_this.accessoryInfo.displayName, "' using ciao backend!")); return _this; } CiaoAdvertiser.prototype.initPort = function (port) { this.advertisedService.updatePort(port); }; CiaoAdvertiser.prototype.startAdvertising = function () { debug("Starting to advertise '".concat(this.accessoryInfo.displayName, "' using ciao backend!")); return this.advertisedService.advertise(); }; CiaoAdvertiser.prototype.updateAdvertisement = function (silent) { var txt = CiaoAdvertiser.createTxt(this.accessoryInfo, this.setupHash); debug("Updating txt record (txt: %o, silent: %d)", txt, silent); this.advertisedService.updateTxt(txt, silent); }; CiaoAdvertiser.prototype.destroy = function () { return tslib_1.__awaiter(this, void 0, void 0, function () { return tslib_1.__generator(this, function (_a) { switch (_a.label) { case 0: // advertisedService.destroy(); is called implicitly via the shutdown call return [4 /*yield*/, this.responder.shutdown()]; case 1: // advertisedService.destroy(); is called implicitly via the shutdown call _a.sent(); this.removeAllListeners(); return [2 /*return*/]; } }); }); }; CiaoAdvertiser.createTxt = function (accessoryInfo, setupHash) { var statusFlags = []; if (!accessoryInfo.paired()) { statusFlags.push(1 /* StatusFlag.NOT_PAIRED */); } return { "c#": accessoryInfo.getConfigVersion(), // current configuration number ff: CiaoAdvertiser.ff(), // pairing feature flags id: accessoryInfo.username, // device id md: accessoryInfo.model, // model name pv: CiaoAdvertiser.protocolVersion, // protocol version "s#": 1, // current state number (must be 1) sf: CiaoAdvertiser.sf.apply(CiaoAdvertiser, tslib_1.__spreadArray([], tslib_1.__read(statusFlags), false)), // status flags ci: accessoryInfo.category, sh: setupHash, }; }; CiaoAdvertiser.computeSetupHash = function (accessoryInfo) { var hash = crypto_1.default.createHash("sha512"); hash.update(accessoryInfo.setupID + accessoryInfo.username.toUpperCase()); return hash.digest().slice(0, 4).toString("base64"); }; CiaoAdvertiser.ff = function () { var flags = []; for (var _i = 0; _i < arguments.length; _i++) { flags[_i] = arguments[_i]; } var value = 0; flags.forEach(function (flag) { return value |= flag; }); return value; }; CiaoAdvertiser.sf = function () { var flags = []; for (var _i = 0; _i < arguments.length; _i++) { flags[_i] = arguments[_i]; } var value = 0; flags.forEach(function (flag) { return value |= flag; }); return value; }; CiaoAdvertiser.protocolVersion = "1.1"; CiaoAdvertiser.protocolVersionService = "1.1.0"; return CiaoAdvertiser; }(events_1.EventEmitter)); exports.CiaoAdvertiser = CiaoAdvertiser; /** * Advertiser base on the legacy "bonjour-hap" library. * * @group Advertiser */ var BonjourHAPAdvertiser = /** @class */ (function (_super) { tslib_1.__extends(BonjourHAPAdvertiser, _super); function BonjourHAPAdvertiser(accessoryInfo, responderOptions, serviceOptions) { var _this = _super.call(this) || this; _this.destroyed = false; _this.accessoryInfo = accessoryInfo; _this.setupHash = CiaoAdvertiser.computeSetupHash(accessoryInfo); _this.serviceOptions = serviceOptions; _this.bonjour = (0, bonjour_hap_1.default)(responderOptions); debug("Preparing Advertiser for '".concat(_this.accessoryInfo.displayName, "' using bonjour-hap backend!")); return _this; } BonjourHAPAdvertiser.prototype.initPort = function (port) { this.port = port; }; BonjourHAPAdvertiser.prototype.startAdvertising = function () { (0, assert_1.default)(!this.destroyed, "Can't advertise on a destroyed bonjour instance!"); if (this.port == null) { throw new Error("Tried starting bonjour-hap advertisement without initializing port!"); } debug("Starting to advertise '".concat(this.accessoryInfo.displayName, "' using bonjour-hap backend!")); if (this.advertisement) { this.destroy(); } var hostname = this.accessoryInfo.username.replace(/:/ig, "_") + ".local"; this.advertisement = this.bonjour.publish(tslib_1.__assign({ name: this.accessoryInfo.displayName, type: "hap", port: this.port, txt: CiaoAdvertiser.createTxt(this.accessoryInfo, this.setupHash), host: hostname, addUnsafeServiceEnumerationRecord: true }, this.serviceOptions)); return (0, promise_utils_1.PromiseTimeout)(1); }; BonjourHAPAdvertiser.prototype.updateAdvertisement = function (silent) { var txt = CiaoAdvertiser.createTxt(this.accessoryInfo, this.setupHash); debug("Updating txt record (txt: %o, silent: %d)", txt, silent); if (this.advertisement) { this.advertisement.updateTxt(txt, silent); } }; BonjourHAPAdvertiser.prototype.destroy = function () { var _this = this; if (this.advertisement) { this.advertisement.stop(function () { _this.advertisement.destroy(); _this.advertisement = undefined; _this.bonjour.destroy(); }); } else { this.bonjour.destroy(); } }; return BonjourHAPAdvertiser; }(events_1.EventEmitter)); exports.BonjourHAPAdvertiser = BonjourHAPAdvertiser; function messageBusConnectionResult(bus) { return new Promise(function (resolve, reject) { var errorHandler = function (error) { // eslint-disable-next-line @typescript-eslint/no-use-before-define bus.connection.removeListener("connect", connectHandler); reject(error); }; var connectHandler = function () { bus.connection.removeListener("error", errorHandler); resolve(); }; bus.connection.once("connect", connectHandler); bus.connection.once("error", errorHandler); }); } /** * @group Advertiser */ var DBusInvokeError = /** @class */ (function (_super) { tslib_1.__extends(DBusInvokeError, _super); // eslint-disable-next-line @typescript-eslint/no-explicit-any function DBusInvokeError(errorObject) { var _this = _super.call(this) || this; Object.setPrototypeOf(_this, DBusInvokeError.prototype); _this.name = "DBusInvokeError"; _this.errorName = errorObject.name; if (Array.isArray(errorObject.message) && errorObject.message.length === 1) { _this.message = errorObject.message[0]; } else { _this.message = errorObject.message.toString(); } return _this; } return DBusInvokeError; }(Error)); exports.DBusInvokeError = DBusInvokeError; // eslint-disable-next-line @typescript-eslint/no-explicit-any function dbusInvoke(bus, destination, path, dbusInterface, member, others) { return new Promise(function (resolve, reject) { var command = tslib_1.__assign({ destination: destination, path: path, interface: dbusInterface, member: member }, (others || {})); bus.invoke(command, function (err, result) { if (err) { reject(new DBusInvokeError(err)); } else { resolve(result); } }); }); } /** * AvahiServerState. * * Refer to https://github.com/lathiat/avahi/blob/fd482a74625b8db8547b8cfca3ee3d3c6c721423/avahi-common/defs.h#L220-L227. * * @group Advertiser */ var AvahiServerState; (function (AvahiServerState) { // noinspection JSUnusedGlobalSymbols AvahiServerState[AvahiServerState["INVALID"] = 0] = "INVALID"; AvahiServerState[AvahiServerState["REGISTERING"] = 1] = "REGISTERING"; AvahiServerState[AvahiServerState["RUNNING"] = 2] = "RUNNING"; AvahiServerState[AvahiServerState["COLLISION"] = 3] = "COLLISION"; AvahiServerState[AvahiServerState["FAILURE"] = 4] = "FAILURE"; })(AvahiServerState || (AvahiServerState = {})); /** * Advertiser based on the Avahi D-Bus library. * For (very crappy) docs on the interface, see the XML files at: https://github.com/lathiat/avahi/tree/master/avahi-daemon. * * Refer to https://github.com/lathiat/avahi/blob/fd482a74625b8db8547b8cfca3ee3d3c6c721423/avahi-common/defs.h#L120-L155 for a * rough API usage guide of Avahi. * * @group Advertiser */ var AvahiAdvertiser = /** @class */ (function (_super) { tslib_1.__extends(AvahiAdvertiser, _super); function AvahiAdvertiser(accessoryInfo) { var _this = _super.call(this) || this; _this.accessoryInfo = accessoryInfo; _this.setupHash = CiaoAdvertiser.computeSetupHash(accessoryInfo); debug("Preparing Advertiser for '".concat(_this.accessoryInfo.displayName, "' using Avahi backend!")); _this.bus = dbus_native_1.default.systemBus(); _this.stateChangeHandler = _this.handleStateChangedEvent.bind(_this); return _this; } AvahiAdvertiser.prototype.createTxt = function () { return Object .entries(CiaoAdvertiser.createTxt(this.accessoryInfo, this.setupHash)) .map(function (el) { return Buffer.from(el[0] + "=" + el[1]); }); }; AvahiAdvertiser.prototype.initPort = function (port) { this.port = port; }; AvahiAdvertiser.prototype.startAdvertising = function () { return tslib_1.__awaiter(this, void 0, void 0, function () { var _a, _b, error_1; return tslib_1.__generator(this, function (_c) { switch (_c.label) { case 0: if (this.port == null) { throw new Error("Tried starting Avahi advertisement without initializing port!"); } if (!this.bus) { throw new Error("Tried to start Avahi advertisement on a destroyed advertiser!"); } debug("Starting to advertise '".concat(this.accessoryInfo.displayName, "' using Avahi backend!")); _a = this; return [4 /*yield*/, AvahiAdvertiser.avahiInvoke(this.bus, "/", "Server", "EntryGroupNew")]; case 1: _a.path = (_c.sent()); return [4 /*yield*/, AvahiAdvertiser.avahiInvoke(this.bus, this.path, "EntryGroup", "AddService", { body: [ -1, // interface -1, // protocol 0, // flags this.accessoryInfo.displayName, // name "_hap._tcp", // type "", // domain "", // host this.port, // port this.createTxt(), // txt ], signature: "iiussssqaay", })]; case 2: _c.sent(); return [4 /*yield*/, AvahiAdvertiser.avahiInvoke(this.bus, this.path, "EntryGroup", "Commit")]; case 3: _c.sent(); _c.label = 4; case 4: _c.trys.push([4, 7, , 8]); if (!!this.avahiServerInterface) return [3 /*break*/, 6]; _b = this; return [4 /*yield*/, AvahiAdvertiser.avahiInterface(this.bus, "Server")]; case 5: _b.avahiServerInterface = _c.sent(); this.avahiServerInterface.on("StateChanged", this.stateChangeHandler); _c.label = 6; case 6: return [3 /*break*/, 8]; case 7: error_1 = _c.sent(); // We have some problem on Synology https://github.com/homebridge/HAP-NodeJS/issues/993 console.warn("Failed to create listener for avahi-daemon server state. The system will not be notified about restarts of avahi-daemon " + "and will therefore stay undiscoverable in those instances. Error message: " + error_1); if (error_1.stack) { debug("Detailed error: " + error_1.stack); } return [3 /*break*/, 8]; case 8: return [2 /*return*/]; } }); }); }; /** * Event handler for the `StateChanged` event of the `org.freedesktop.Avahi.Server` DBus interface. * * This is called once the state of the running avahi-daemon changes its running state. * @param state - The state the server changed into {@see AvahiServerState}. */ AvahiAdvertiser.prototype.handleStateChangedEvent = function (state) { if (state === 2 /* AvahiServerState.RUNNING */ && this.path) { debug("Found Avahi daemon to have restarted!"); this.startAdvertising() .catch(function (reason) { return console.error("Could not (re-)create mDNS advertisement. The HAP-Server won't be discoverable: " + reason); }); } }; AvahiAdvertiser.prototype.updateAdvertisement = function (silent) { return tslib_1.__awaiter(this, void 0, void 0, function () { var error_2; return tslib_1.__generator(this, function (_a) { switch (_a.label) { case 0: if (!this.bus) { throw new Error("Tried to update Avahi advertisement on a destroyed advertiser!"); } if (!this.path) { debug("Tried to update advertisement without a valid `path`!"); return [2 /*return*/]; } debug("Updating txt record (txt: %o, silent: %d)", CiaoAdvertiser.createTxt(this.accessoryInfo, this.setupHash), silent); _a.label = 1; case 1: _a.trys.push([1, 3, , 4]); return [4 /*yield*/, AvahiAdvertiser.avahiInvoke(this.bus, this.path, "EntryGroup", "UpdateServiceTxt", { body: [-1, -1, 0, this.accessoryInfo.displayName, "_hap._tcp", "", this.createTxt()], signature: "iiusssaay", })]; case 2: _a.sent(); return [3 /*break*/, 4]; case 3: error_2 = _a.sent(); console.error("Failed to update avahi advertisement: " + error_2); return [3 /*break*/, 4]; case 4: return [2 /*return*/]; } }); }); }; AvahiAdvertiser.prototype.destroy = function () { return tslib_1.__awaiter(this, void 0, void 0, function () { var error_3; return tslib_1.__generator(this, function (_a) { switch (_a.label) { case 0: if (!this.bus) { throw new Error("Tried to destroy Avahi advertisement on a destroyed advertiser!"); } if (!this.path) return [3 /*break*/, 5]; _a.label = 1; case 1: _a.trys.push([1, 3, , 4]); return [4 /*yield*/, AvahiAdvertiser.avahiInvoke(this.bus, this.path, "EntryGroup", "Free")]; case 2: _a.sent(); return [3 /*break*/, 4]; case 3: error_3 = _a.sent(); // Typically, this fails if e.g. avahi service was stopped in the meantime. debug("Destroying Avahi advertisement failed: " + error_3); return [3 /*break*/, 4]; case 4: this.path = undefined; _a.label = 5; case 5: if (this.avahiServerInterface) { this.avahiServerInterface.removeListener("StateChanged", this.stateChangeHandler); this.avahiServerInterface = undefined; } this.bus.connection.stream.destroy(); this.bus = undefined; return [2 /*return*/]; } }); }); }; AvahiAdvertiser.isAvailable = function () { return tslib_1.__awaiter(this, void 0, void 0, function () { var bus, error_4, version, error_5; return tslib_1.__generator(this, function (_a) { switch (_a.label) { case 0: bus = dbus_native_1.default.systemBus(); _a.label = 1; case 1: _a.trys.push([1, , 9, 10]); _a.label = 2; case 2: _a.trys.push([2, 4, , 5]); return [4 /*yield*/, messageBusConnectionResult(bus)]; case 3: _a.sent(); return [3 /*break*/, 5]; case 4: error_4 = _a.sent(); debug("Avahi/DBus classified unavailable due to missing dbus interface!"); return [2 /*return*/, false]; case 5: _a.trys.push([5, 7, , 8]); return [4 /*yield*/, this.avahiInvoke(bus, "/", "Server", "GetVersionString")]; case 6: version = _a.sent(); debug("Detected Avahi over DBus interface running version '%s'.", version); return [3 /*break*/, 8]; case 7: error_5 = _a.sent(); debug("Avahi/DBus classified unavailable due to missing avahi interface!"); return [2 /*return*/, false]; case 8: return [2 /*return*/, true]; case 9: bus.connection.stream.destroy(); return [7 /*endfinally*/]; case 10: return [2 /*return*/]; } }); }); }; // eslint-disable-next-line @typescript-eslint/no-explicit-any AvahiAdvertiser.avahiInvoke = function (bus, path, dbusInterface, member, others) { return dbusInvoke(bus, "org.freedesktop.Avahi", path, "org.freedesktop.Avahi.".concat(dbusInterface), member, others); }; AvahiAdvertiser.avahiInterface = function (bus, dbusInterface) { return new Promise(function (resolve, reject) { bus .getService("org.freedesktop.Avahi") .getInterface("/", "org.freedesktop.Avahi." + dbusInterface, function (error, iface) { if (error || !iface) { reject(error !== null && error !== void 0 ? error : new Error("Interface not present!")); } else { resolve(iface); } }); }); }; return AvahiAdvertiser; }(events_1.EventEmitter)); exports.AvahiAdvertiser = AvahiAdvertiser; var RESOLVED_PERMISSIONS_ERRORS = [ "org.freedesktop.DBus.Error.AccessDenied", "org.freedesktop.DBus.Error.AuthFailed", "org.freedesktop.DBus.Error.InteractiveAuthorizationRequired", ]; /** * Advertiser based on the systemd-resolved D-Bus library. * For docs on the interface, see: https://www.freedesktop.org/software/systemd/man/org.freedesktop.resolve1.html * * @group Advertiser */ var ResolvedAdvertiser = /** @class */ (function (_super) { tslib_1.__extends(ResolvedAdvertiser, _super); function ResolvedAdvertiser(accessoryInfo) { var _this = _super.call(this) || this; _this.accessoryInfo = accessoryInfo; _this.setupHash = CiaoAdvertiser.computeSetupHash(accessoryInfo); _this.bus = dbus_native_1.default.systemBus(); debug("Preparing Advertiser for '".concat(_this.accessoryInfo.displayName, "' using systemd-resolved backend!")); return _this; } ResolvedAdvertiser.prototype.createTxt = function () { return Object .entries(CiaoAdvertiser.createTxt(this.accessoryInfo, this.setupHash)) .map(function (el) { return [el[0].toString(), Buffer.from(el[1].toString())]; }); }; ResolvedAdvertiser.prototype.initPort = function (port) { this.port = port; }; ResolvedAdvertiser.prototype.startAdvertising = function () { return tslib_1.__awaiter(this, void 0, void 0, function () { var _a, error_6; return tslib_1.__generator(this, function (_b) { switch (_b.label) { case 0: if (this.port == null) { throw new Error("Tried starting systemd-resolved advertisement without initializing port!"); } if (!this.bus) { throw new Error("Tried to start systemd-resolved advertisement on a destroyed advertiser!"); } debug("Starting to advertise '".concat(this.accessoryInfo.displayName, "' using systemd-resolved backend!")); _b.label = 1; case 1: _b.trys.push([1, 3, , 4]); _a = this; return [4 /*yield*/, ResolvedAdvertiser.managerInvoke(this.bus, "RegisterService", { body: [ this.accessoryInfo.displayName, // name this.accessoryInfo.displayName, // name_template "_hap._tcp", // type this.port, // service_port 0, // service_priority 0, // service_weight [this.createTxt()], // txt_datas ], signature: "sssqqqaa{say}", })]; case 2: _a.path = _b.sent(); return [3 /*break*/, 4]; case 3: error_6 = _b.sent(); if (error_6 instanceof DBusInvokeError) { if (RESOLVED_PERMISSIONS_ERRORS.includes(error_6.errorName)) { error_6.message = "Permissions issue. See https://homebridge.io/w/mDNS-Options for more info. ".concat(error_6.message); } } throw error_6; case 4: return [2 /*return*/]; } }); }); }; ResolvedAdvertiser.prototype.updateAdvertisement = function (silent) { return tslib_1.__awaiter(this, void 0, void 0, function () { return tslib_1.__generator(this, function (_a) { switch (_a.label) { case 0: if (!this.bus) { throw new Error("Tried to update systemd-resolved advertisement on a destroyed advertiser!"); } debug("Updating txt record (txt: %o, silent: %d)", CiaoAdvertiser.createTxt(this.accessoryInfo, this.setupHash), silent); // Currently, systemd-resolved has no way to update an existing record. return [4 /*yield*/, this.stopAdvertising()]; case 1: // Currently, systemd-resolved has no way to update an existing record. _a.sent(); return [4 /*yield*/, this.startAdvertising()]; case 2: _a.sent(); return [2 /*return*/]; } }); }); }; ResolvedAdvertiser.prototype.stopAdvertising = function () { return tslib_1.__awaiter(this, void 0, void 0, function () { var error_7; return tslib_1.__generator(this, function (_a) { switch (_a.label) { case 0: if (!this.bus) { throw new Error("Tried to destroy systemd-resolved advertisement on a destroyed advertiser!"); } if (!this.path) return [3 /*break*/, 5]; _a.label = 1; case 1: _a.trys.push([1, 3, , 4]); return [4 /*yield*/, ResolvedAdvertiser.managerInvoke(this.bus, "UnregisterService", { body: [this.path], signature: "o", })]; case 2: _a.sent(); return [3 /*break*/, 4]; case 3: error_7 = _a.sent(); // Typically, this fails if e.g. systemd-resolved service was stopped in the meantime. debug("Destroying systemd-resolved advertisement failed: " + error_7); return [3 /*break*/, 4]; case 4: this.path = undefined; _a.label = 5; case 5: return [2 /*return*/]; } }); }); }; ResolvedAdvertiser.prototype.destroy = function () { return tslib_1.__awaiter(this, void 0, void 0, function () { return tslib_1.__generator(this, function (_a) { switch (_a.label) { case 0: if (!this.bus) { throw new Error("Tried to destroy systemd-resolved advertisement on a destroyed advertiser!"); } return [4 /*yield*/, this.stopAdvertising()]; case 1: _a.sent(); this.bus.connection.stream.destroy(); this.bus = undefined; return [2 /*return*/]; } }); }); }; ResolvedAdvertiser.isAvailable = function () { return tslib_1.__awaiter(this, void 0, void 0, function () { var bus, error_8, error_9, mdnsStatus, error_10; return tslib_1.__generator(this, function (_a) { switch (_a.label) { case 0: bus = dbus_native_1.default.systemBus(); _a.label = 1; case 1: _a.trys.push([1, , 12, 13]); _a.label = 2; case 2: _a.trys.push([2, 4, , 5]); return [4 /*yield*/, messageBusConnectionResult(bus)]; case 3: _a.sent(); return [3 /*break*/, 5]; case 4: error_8 = _a.sent(); debug("systemd-resolved/DBus classified unavailable due to missing dbus interface!"); return [2 /*return*/, false]; case 5: _a.trys.push([5, 7, , 8]); // Ensure that systemd-resolved is accessible. return [4 /*yield*/, this.managerInvoke(bus, "ResolveHostname", { body: [0, "127.0.0.1", 0, 0], signature: "isit", })]; case 6: // Ensure that systemd-resolved is accessible. _a.sent(); debug("Detected systemd-resolved over DBus interface running version."); return [3 /*break*/, 8]; case 7: error_9 = _a.sent(); debug("systemd-resolved/DBus classified unavailable due to missing systemd-resolved interface!"); return [2 /*return*/, false]; case 8: _a.trys.push([8, 10, , 11]); return [4 /*yield*/, this.resolvedInvoke(bus, "org.freedesktop.DBus.Properties", "Get", { body: ["org.freedesktop.resolve1.Manager", "MulticastDNS"], signature: "ss", })]; case 9: mdnsStatus = _a.sent(); if (mdnsStatus[0][0].type !== "s") { throw new Error("Invalid type for MulticastDNS"); } if (mdnsStatus[1][0] !== "yes") { debug("systemd-resolved/DBus classified unavailable because MulticastDNS is not enabled!"); return [2 /*return*/, false]; } return [3 /*break*/, 11]; case 10: error_10 = _a.sent(); debug("systemd-resolved/DBus classified unavailable due to failure checking system status: " + error_10); return [2 /*return*/, false]; case 11: return [2 /*return*/, true]; case 12: bus.connection.stream.destroy(); return [7 /*endfinally*/]; case 13: return [2 /*return*/]; } }); }); }; // eslint-disable-next-line @typescript-eslint/no-explicit-any ResolvedAdvertiser.resolvedInvoke = function (bus, dbusInterface, member, others) { return dbusInvoke(bus, "org.freedesktop.resolve1", "/org/freedesktop/resolve1", dbusInterface, member, others); }; // eslint-disable-next-line @typescript-eslint/no-explicit-any ResolvedAdvertiser.managerInvoke = function (bus, member, others) { return this.resolvedInvoke(bus, "org.freedesktop.resolve1.Manager", member, others); }; return ResolvedAdvertiser; }(events_1.EventEmitter)); exports.ResolvedAdvertiser = ResolvedAdvertiser; //# sourceMappingURL=Advertiser.js.map