@homebridge/dbus-native 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +18 -0
- package/README.md +124 -0
- package/bin/dbus2js.js +118 -0
- package/index.js +155 -0
- package/lib/address-x11.js +38 -0
- package/lib/align.js +12 -0
- package/lib/bus.js +384 -0
- package/lib/constants.js +54 -0
- package/lib/dbus-buffer.js +182 -0
- package/lib/handshake.js +137 -0
- package/lib/header-signature.json +20 -0
- package/lib/hello-message.js +16 -0
- package/lib/introspect.js +201 -0
- package/lib/marshall.js +108 -0
- package/lib/marshallers.js +336 -0
- package/lib/message.js +118 -0
- package/lib/portforward.js +38 -0
- package/lib/readline.js +23 -0
- package/lib/server-handshake.js +37 -0
- package/lib/server.js +18 -0
- package/lib/signature.js +61 -0
- package/lib/stdifaces.js +203 -0
- package/lib/unmarshall.js +9 -0
- package/package.json +90 -0
package/lib/stdifaces.js
ADDED
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
const constants = require('./constants');
|
|
2
|
+
const parseSignature = require('./signature');
|
|
3
|
+
|
|
4
|
+
// TODO: use xmlbuilder
|
|
5
|
+
|
|
6
|
+
var xmlHeader =
|
|
7
|
+
'<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"\n' +
|
|
8
|
+
' "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">';
|
|
9
|
+
var stdIfaces;
|
|
10
|
+
|
|
11
|
+
module.exports = function(msg, bus) {
|
|
12
|
+
if (
|
|
13
|
+
msg['interface'] === 'org.freedesktop.DBus.Introspectable' &&
|
|
14
|
+
msg.member === 'Introspect'
|
|
15
|
+
) {
|
|
16
|
+
if (msg.path === '/') msg.path = '';
|
|
17
|
+
|
|
18
|
+
var resultXml = [xmlHeader];
|
|
19
|
+
var nodes = {};
|
|
20
|
+
// TODO: this is not very efficiant for large number of exported objects
|
|
21
|
+
// need to build objects tree as they are exported and walk this tree on introspect request
|
|
22
|
+
for (var path in bus.exportedObjects) {
|
|
23
|
+
if (path.indexOf(msg.path) === 0) {
|
|
24
|
+
// objects path starts with requested
|
|
25
|
+
var introspectableObj = bus.exportedObjects[msg.path];
|
|
26
|
+
if (introspectableObj) {
|
|
27
|
+
nodes[msg.path] = introspectableObj;
|
|
28
|
+
} else {
|
|
29
|
+
if (path[msg.path.length] !== '/') continue;
|
|
30
|
+
var localPath = path.substr(msg.path.length);
|
|
31
|
+
var pathParts = localPath.split('/');
|
|
32
|
+
var localName = pathParts[1];
|
|
33
|
+
nodes[localName] = null;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
var length = Object.keys(nodes).length;
|
|
39
|
+
if (length === 0) {
|
|
40
|
+
resultXml.push('<node/>');
|
|
41
|
+
} else if (length === 1) {
|
|
42
|
+
var obj = nodes[Object.keys(nodes)[0]];
|
|
43
|
+
if (obj) {
|
|
44
|
+
resultXml.push('<node>');
|
|
45
|
+
for (var ifaceNode in obj) {
|
|
46
|
+
resultXml.push(interfaceToXML(obj[ifaceNode][0]));
|
|
47
|
+
}
|
|
48
|
+
resultXml.push(stdIfaces);
|
|
49
|
+
resultXml.push('</node>');
|
|
50
|
+
} else {
|
|
51
|
+
resultXml.push(
|
|
52
|
+
`<node>\n <node name="${Object.keys(nodes)[0]}"/>\n </node>`
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
} else {
|
|
56
|
+
resultXml.push('<node>');
|
|
57
|
+
for (var name in nodes) {
|
|
58
|
+
if (nodes[name] === null) {
|
|
59
|
+
resultXml.push(` <node name="${name}" />`);
|
|
60
|
+
} else {
|
|
61
|
+
obj = nodes[name];
|
|
62
|
+
resultXml.push(` <node name="${name}" >`);
|
|
63
|
+
for (var ifaceName in obj) {
|
|
64
|
+
resultXml.push(interfaceToXML(obj[ifaceName][0]));
|
|
65
|
+
}
|
|
66
|
+
resultXml.push(stdIfaces);
|
|
67
|
+
resultXml.push(' </node>');
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
resultXml.push('</node>');
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const introspectableReply = {
|
|
74
|
+
type: constants.messageType.methodReturn,
|
|
75
|
+
serial: bus.serial++,
|
|
76
|
+
replySerial: msg.serial,
|
|
77
|
+
destination: msg.sender,
|
|
78
|
+
signature: 's',
|
|
79
|
+
body: [resultXml.join('\n')]
|
|
80
|
+
};
|
|
81
|
+
bus.connection.message(introspectableReply);
|
|
82
|
+
return 1;
|
|
83
|
+
} else if (msg['interface'] === 'org.freedesktop.DBus.Properties') {
|
|
84
|
+
var interfaceName = msg.body[0];
|
|
85
|
+
var propertiesObj = bus.exportedObjects[msg.path];
|
|
86
|
+
// TODO: !propertiesObj -> UnknownObject http://www.freedesktop.org/wiki/Software/DBusBindingErrors
|
|
87
|
+
if (!propertiesObj || !propertiesObj[interfaceName]) {
|
|
88
|
+
// TODO:
|
|
89
|
+
bus.sendError(
|
|
90
|
+
msg,
|
|
91
|
+
'org.freedesktop.DBus.Error.UnknownMethod',
|
|
92
|
+
'Uh oh oh'
|
|
93
|
+
);
|
|
94
|
+
return 1;
|
|
95
|
+
}
|
|
96
|
+
var impl = propertiesObj[interfaceName][1];
|
|
97
|
+
|
|
98
|
+
const propertiesReply = {
|
|
99
|
+
type: constants.messageType.methodReturn,
|
|
100
|
+
serial: bus.serial++,
|
|
101
|
+
replySerial: msg.serial,
|
|
102
|
+
destination: msg.sender
|
|
103
|
+
};
|
|
104
|
+
if (msg.member === 'Get' || msg.member === 'Set') {
|
|
105
|
+
var propertyName = msg.body[1];
|
|
106
|
+
var propType = propertiesObj[interfaceName][0].properties[propertyName];
|
|
107
|
+
if (msg.member === 'Get') {
|
|
108
|
+
var propValue = impl[propertyName];
|
|
109
|
+
propertiesReply.signature = 'v';
|
|
110
|
+
propertiesReply.body = [[propType, propValue]];
|
|
111
|
+
} else {
|
|
112
|
+
impl[propertyName] = 1234; // TODO: read variant and set property value
|
|
113
|
+
}
|
|
114
|
+
} else if (msg.member === 'GetAll') {
|
|
115
|
+
propertiesReply.signature = 'a{sv}';
|
|
116
|
+
var props = [];
|
|
117
|
+
for (var p in propertiesObj[interfaceName][0].properties) {
|
|
118
|
+
var propertySignature = propertiesObj[interfaceName][0].properties[p];
|
|
119
|
+
props.push([p, [propertySignature, impl[p]]]);
|
|
120
|
+
}
|
|
121
|
+
propertiesReply.body = [props];
|
|
122
|
+
}
|
|
123
|
+
bus.connection.message(propertiesReply);
|
|
124
|
+
return 1;
|
|
125
|
+
} else if (msg['interface'] === 'org.freedesktop.DBus.Peer') {
|
|
126
|
+
// TODO: implement bus.replyTo(srcMsg, signature, body) method
|
|
127
|
+
const peerReply = {
|
|
128
|
+
type: constants.messageType.methodReturn,
|
|
129
|
+
serial: bus.serial++,
|
|
130
|
+
replySerial: msg.serial,
|
|
131
|
+
destination: msg.sender
|
|
132
|
+
};
|
|
133
|
+
if (msg.member === 'Ping') {
|
|
134
|
+
// empty body
|
|
135
|
+
} else if (msg.member === 'GetMachineId') {
|
|
136
|
+
peerReply.signature = 's';
|
|
137
|
+
peerReply.body = ['This is a machine id. TODO: implement'];
|
|
138
|
+
}
|
|
139
|
+
bus.connection.message(peerReply);
|
|
140
|
+
return 1;
|
|
141
|
+
}
|
|
142
|
+
return 0;
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
// TODO: move to introspect.js
|
|
146
|
+
function interfaceToXML(iface) {
|
|
147
|
+
var result = [];
|
|
148
|
+
var dumpArgs = function(argsSignature, argsNames, direction) {
|
|
149
|
+
if (!argsSignature) return;
|
|
150
|
+
var args = parseSignature(argsSignature);
|
|
151
|
+
args.forEach(function(arg, num) {
|
|
152
|
+
var argName = argsNames ? argsNames[num] : direction + num;
|
|
153
|
+
var dirStr = direction === 'signal' ? '' : `" direction="${direction}`;
|
|
154
|
+
result.push(
|
|
155
|
+
` <arg type="${dumpSignature([arg])}" name="${argName}${
|
|
156
|
+
dirStr
|
|
157
|
+
}" />`
|
|
158
|
+
);
|
|
159
|
+
});
|
|
160
|
+
};
|
|
161
|
+
result.push(` <interface name="${iface.name}">`);
|
|
162
|
+
if (iface.methods) {
|
|
163
|
+
for (var methodName in iface.methods) {
|
|
164
|
+
var method = iface.methods[methodName];
|
|
165
|
+
result.push(` <method name="${methodName}">`);
|
|
166
|
+
dumpArgs(method[0], method[2], 'in');
|
|
167
|
+
dumpArgs(method[1], method[3], 'out');
|
|
168
|
+
result.push(' </method>');
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
if (iface.signals) {
|
|
172
|
+
for (var signalName in iface.signals) {
|
|
173
|
+
var signal = iface.signals[signalName];
|
|
174
|
+
result.push(` <signal name="${signalName}">`);
|
|
175
|
+
dumpArgs(signal[0], signal.slice(1), 'signal');
|
|
176
|
+
result.push(' </signal>');
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
if (iface.properties) {
|
|
180
|
+
for (const propertyName in iface.properties) {
|
|
181
|
+
// TODO: decide how to encode access
|
|
182
|
+
result.push(
|
|
183
|
+
` <property name="${propertyName}" type="${
|
|
184
|
+
iface.properties[propertyName]
|
|
185
|
+
}" access="readwrite"/>`
|
|
186
|
+
);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
result.push(' </interface>');
|
|
190
|
+
return result.join('\n');
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
function dumpSignature(s) {
|
|
194
|
+
var result = [];
|
|
195
|
+
s.forEach(function(sig) {
|
|
196
|
+
result.push(sig.type + dumpSignature(sig.child));
|
|
197
|
+
if (sig.type === '{') result.push('}');
|
|
198
|
+
if (sig.type === '(') result.push(')');
|
|
199
|
+
});
|
|
200
|
+
return result.join('');
|
|
201
|
+
}
|
|
202
|
+
stdIfaces =
|
|
203
|
+
' <interface name="org.freedesktop.DBus.Properties">\n <method name="Get">\n <arg type="s" name="interface_name" direction="in"/>\n <arg type="s" name="property_name" direction="in"/>\n <arg type="v" name="value" direction="out"/>\n </method>\n <method name="GetAll">\n <arg type="s" name="interface_name" direction="in"/>\n <arg type="a{sv}" name="properties" direction="out"/>\n </method>\n <method name="Set">\n <arg type="s" name="interface_name" direction="in"/>\n <arg type="s" name="property_name" direction="in"/>\n <arg type="v" name="value" direction="in"/>\n </method>\n <signal name="PropertiesChanged">\n <arg type="s" name="interface_name"/>\n <arg type="a{sv}" name="changed_properties"/>\n <arg type="as" name="invalidated_properties"/>\n </signal>\n </interface>\n <interface name="org.freedesktop.DBus.Introspectable">\n <method name="Introspect">\n <arg type="s" name="xml_data" direction="out"/>\n </method>\n </interface>\n <interface name="org.freedesktop.DBus.Peer">\n <method name="Ping"/>\n <method name="GetMachineId">\n <arg type="s" name="machine_uuid" direction="out"/>\n </method>\n </interface>';
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
const Buffer = require('safe-buffer').Buffer;
|
|
2
|
+
const DBusBuffer = require('./dbus-buffer');
|
|
3
|
+
|
|
4
|
+
module.exports = function unmarshall(buffer, signature, startPos, options) {
|
|
5
|
+
if (!startPos) startPos = 0;
|
|
6
|
+
if (signature === '') return Buffer.from('');
|
|
7
|
+
var dbuff = new DBusBuffer(buffer, startPos, options);
|
|
8
|
+
return dbuff.read(signature);
|
|
9
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@homebridge/dbus-native",
|
|
3
|
+
"author": "Andrey Sidorov <sidorares@yandex.com>",
|
|
4
|
+
"version": "0.4.0",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"dbus",
|
|
7
|
+
"dcop",
|
|
8
|
+
"d-bus",
|
|
9
|
+
"rpc",
|
|
10
|
+
"gnome",
|
|
11
|
+
"kde"
|
|
12
|
+
],
|
|
13
|
+
"description": "D-bus protocol implementation in native javascript",
|
|
14
|
+
"files": [
|
|
15
|
+
"bin/dbus2js.js",
|
|
16
|
+
"lib/*",
|
|
17
|
+
"index.js",
|
|
18
|
+
"package.json"
|
|
19
|
+
],
|
|
20
|
+
"directories": {
|
|
21
|
+
"lib": "lib",
|
|
22
|
+
"test": "test",
|
|
23
|
+
"examples": "examples"
|
|
24
|
+
},
|
|
25
|
+
"main": "index.js",
|
|
26
|
+
"maintainers": [
|
|
27
|
+
{
|
|
28
|
+
"name": "Andrey Sidorov",
|
|
29
|
+
"email": "sidoares@yandex.ru"
|
|
30
|
+
}
|
|
31
|
+
],
|
|
32
|
+
"license": "MIT",
|
|
33
|
+
"repository": {
|
|
34
|
+
"type": "git",
|
|
35
|
+
"url": "https://github.com/homebridge/dbus-native.git"
|
|
36
|
+
},
|
|
37
|
+
"bin": {
|
|
38
|
+
"dbus2js": "./bin/dbus2js.js"
|
|
39
|
+
},
|
|
40
|
+
"dependencies": {
|
|
41
|
+
"event-stream": "^4.0.0",
|
|
42
|
+
"hexy": "^0.2.10",
|
|
43
|
+
"long": "^4.0.0",
|
|
44
|
+
"optimist": "^0.6.1",
|
|
45
|
+
"put": "0.0.6",
|
|
46
|
+
"safe-buffer": "^5.1.1",
|
|
47
|
+
"xml2js": "^0.4.17"
|
|
48
|
+
},
|
|
49
|
+
"uncomment": {
|
|
50
|
+
"optionalDependencies": {
|
|
51
|
+
"abstract-socket": "^2.0.0"
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
"devDependencies": {
|
|
55
|
+
"eslint": "^5.0.0",
|
|
56
|
+
"eslint-config-prettier": "^3.0.0",
|
|
57
|
+
"eslint-plugin-markdown": "^1.0.0-beta.6",
|
|
58
|
+
"eslint-plugin-prettier": "^3.0.0",
|
|
59
|
+
"husky": "^1.0.0",
|
|
60
|
+
"lint-staged": "^8.0.0",
|
|
61
|
+
"mocha": "*",
|
|
62
|
+
"prettier": "^1.7.4"
|
|
63
|
+
},
|
|
64
|
+
"scripts": {
|
|
65
|
+
"lint": "npm run lint:docs && npm run lint:code",
|
|
66
|
+
"lint:code": "eslint index.js 'bin/*.js' 'lib/**/*.js' 'test/**/*.js'",
|
|
67
|
+
"lint:docs": "eslint 'examples/**/*.js'",
|
|
68
|
+
"test": "npm run lint && npm run test:raw",
|
|
69
|
+
"test:raw": "mocha",
|
|
70
|
+
"prettier": "prettier --write index.js '{bin,lib,examples,test}/**/*.js'",
|
|
71
|
+
"prettier:docs": "prettier-markdown README.md",
|
|
72
|
+
"eslint-check": "eslint --print-config .eslintrc | eslint-config-prettier-check",
|
|
73
|
+
"precommit": "lint-staged",
|
|
74
|
+
"prepublish": "npm prune"
|
|
75
|
+
},
|
|
76
|
+
"lint-staged": {
|
|
77
|
+
"*.js": [
|
|
78
|
+
"prettier --write",
|
|
79
|
+
"git add"
|
|
80
|
+
]
|
|
81
|
+
},
|
|
82
|
+
"prettier": {
|
|
83
|
+
"semi": true,
|
|
84
|
+
"singleQuote": true,
|
|
85
|
+
"trailingComma": "none"
|
|
86
|
+
},
|
|
87
|
+
"engine": {
|
|
88
|
+
"node": ">=8.0"
|
|
89
|
+
}
|
|
90
|
+
}
|