Comparing version 0.3.7 to 0.3.8
@@ -19,2 +19,4 @@ "use strict"; | ||
console.log(listDevicesWithPaths); | ||
let Properties = yield client.getProperties(listDevicesWithPaths[0].id); | ||
console.log(Properties); | ||
}); | ||
@@ -21,0 +23,0 @@ } |
@@ -13,2 +13,4 @@ //测试 | ||
console.log(listDevicesWithPaths); | ||
let Properties = await client.getProperties(listDevicesWithPaths[0].id); | ||
console.log(Properties); | ||
// return client | ||
@@ -15,0 +17,0 @@ // .listDevicesWithPaths() |
{ | ||
"name": "xadb", | ||
"version": "0.3.7", | ||
"version": "0.3.8", | ||
"main": "./index", | ||
@@ -5,0 +5,0 @@ "scripts": { |
@@ -6,5 +6,5 @@ /* eslint-disable | ||
// Fix any style issues and re-enable lint. | ||
const Promise = require('bluebird') | ||
const forge = require('node-forge') | ||
const { BigInteger } = forge.jsbn | ||
const Promise = require('bluebird'); | ||
const forge = require('node-forge'); | ||
const {BigInteger} = forge.jsbn; | ||
@@ -26,80 +26,82 @@ /* | ||
*/ | ||
var Auth = (function() { | ||
let RE = undefined | ||
let readPublicKeyFromStruct = undefined | ||
Auth = class Auth { | ||
static initClass() { | ||
// coffeelint: disable=max_line_length | ||
RE = /^((?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?)\0? (.*)\s*$/ | ||
// coffeelint: enable=max_line_length | ||
readPublicKeyFromStruct = function(struct, comment) { | ||
if (!struct.length) { throw new Error('Invalid public key') } | ||
// Keep track of what we've read already | ||
let offset = 0 | ||
// Get len | ||
const len = struct.readUInt32LE(offset) * 4 | ||
offset += 4 | ||
if (struct.length !== (4 + 4 + len + len + 4)) { | ||
throw new Error('Invalid public key') | ||
var Auth = (function () { | ||
let RE = undefined; | ||
let readPublicKeyFromStruct = undefined; | ||
Auth = class Auth { | ||
static initClass() { | ||
// coffeelint: disable=max_line_length | ||
RE = /^((?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?)\0? (.*)\s*$/; | ||
// coffeelint: enable=max_line_length | ||
readPublicKeyFromStruct = function (struct, comment) { | ||
if (!struct.length) { | ||
throw new Error('Invalid public key') | ||
} | ||
// Keep track of what we've read already | ||
let offset = 0; | ||
// Get len | ||
const len = struct.readUInt32LE(offset) * 4; | ||
offset += 4; | ||
if (struct.length !== (4 + 4 + len + len + 4)) { | ||
throw new Error('Invalid public key') | ||
} | ||
// Skip n0inv, we don't need it | ||
offset += 4; | ||
// Get n | ||
const n = Buffer.alloc(len); | ||
struct.copy(n, 0, offset, offset + len); | ||
[].reverse.call(n); | ||
offset += len; | ||
// Skip rr, we don't need it | ||
offset += len; | ||
// Get e | ||
const e = struct.readUInt32LE(offset); | ||
if ((e !== 3) && (e !== 65537)) { | ||
throw new Error(`Invalid exponent ${e}, only 3 and 65537 are supported`) | ||
} | ||
// Restore the public key | ||
const key = forge.pki.setRsaPublicKey( | ||
new BigInteger(n.toString('hex'), 16), | ||
new BigInteger(e.toString(), 10) | ||
); | ||
// It will be difficult to retrieve the fingerprint later as it's based | ||
// on the complete struct data, so let's just extend the key with it. | ||
const md = forge.md.md5.create(); | ||
md.update(struct.toString('binary')); | ||
key.fingerprint = md.digest().toHex().match(/../g).join(':'); | ||
// Expose comment for the same reason | ||
key.comment = comment; | ||
return key | ||
} | ||
} | ||
// Skip n0inv, we don't need it | ||
offset += 4 | ||
// Get n | ||
const n = Buffer.alloc(len) | ||
struct.copy(n, 0, offset, offset + len); | ||
[].reverse.call(n) | ||
offset += len | ||
// Skip rr, we don't need it | ||
offset += len | ||
// Get e | ||
const e = struct.readUInt32LE(offset) | ||
if ((e !== 3) && (e !== 65537)) { | ||
throw new Error(`Invalid exponent ${e}, only 3 and 65537 are supported`) | ||
} | ||
// Restore the public key | ||
const key = forge.pki.setRsaPublicKey( | ||
new BigInteger(n.toString('hex'), 16), | ||
new BigInteger(e.toString(), 10) | ||
) | ||
// It will be difficult to retrieve the fingerprint later as it's based | ||
// on the complete struct data, so let's just extend the key with it. | ||
const md = forge.md.md5.create() | ||
md.update(struct.toString('binary')) | ||
key.fingerprint = md.digest().toHex().match(/../g).join(':') | ||
// Expose comment for the same reason | ||
key.comment = comment | ||
return key | ||
} | ||
} | ||
static parsePublicKey(buffer) { | ||
return new Promise(function(resolve, reject) { | ||
let match | ||
if (match = RE.exec(buffer)) { | ||
const struct = Buffer.from(match[1], 'base64') | ||
const comment = match[2] | ||
return resolve(readPublicKeyFromStruct(struct, comment)) | ||
} else { | ||
return reject(new Error('Unrecognizable public key format')) | ||
static parsePublicKey(buffer) { | ||
return new Promise(function (resolve, reject) { | ||
let match; | ||
if (match = RE.exec(buffer)) { | ||
const struct = Buffer.from(match[1], 'base64'); | ||
const comment = match[2]; | ||
return resolve(readPublicKeyFromStruct(struct, comment)) | ||
} else { | ||
return reject(new Error('Unrecognizable public key format')) | ||
} | ||
}) | ||
} | ||
}) | ||
} | ||
} | ||
Auth.initClass() | ||
return Auth | ||
})() | ||
}; | ||
Auth.initClass(); | ||
return Auth | ||
})(); | ||
module.exports = Auth | ||
module.exports = Auth; |
@@ -11,4 +11,4 @@ "use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const connection_1 = require("./connection"); | ||
const debug = require('debug')('adb:client'); | ||
const Connection = require('./connection'); | ||
const Sync = require('./sync'); | ||
@@ -76,3 +76,3 @@ const Parser = require('./parser'); | ||
let conn = yield new Promise((resolve, reject) => { | ||
let my_conn = new connection_1.Connection(this.options) | ||
let my_conn = new Connection(this.options) | ||
.on('error', (err => reject(err))).on('connect', (() => resolve(my_conn))).connect(); | ||
@@ -79,0 +79,0 @@ }); |
@@ -1,6 +0,5 @@ | ||
import {Connection} from "./connection"; | ||
const debug = require('debug')('adb:client'); | ||
const Connection = require('./connection'); | ||
const Sync = require('./sync'); | ||
@@ -7,0 +6,0 @@ const Parser = require('./parser'); |
// TODO: This file was created by bulk-decaffeinate. | ||
// Sanity-check the conversion and remove this comment. | ||
const Command = require('../../command') | ||
const Protocol = require('../../protocol') | ||
const Command = require('../../command'); | ||
const Protocol = require('../../protocol'); | ||
class HostDevicesWithPathsCommand extends Command { | ||
execute() { | ||
this._send('host:devices-l') | ||
return this.parser.readAscii(4) | ||
.then(reply => { | ||
switch (reply) { | ||
case Protocol.OKAY: | ||
return this._readDevices() | ||
case Protocol.FAIL: | ||
return this.parser.readError() | ||
default: | ||
return this.parser.unexpected(reply, 'OKAY or FAIL') | ||
} | ||
}) | ||
} | ||
execute() { | ||
this._send('host:devices-l'); | ||
return this.parser.readAscii(4) | ||
.then(reply => { | ||
switch (reply) { | ||
case Protocol.OKAY: | ||
return this._readDevices(); | ||
case Protocol.FAIL: | ||
return this.parser.readError(); | ||
default: | ||
return this.parser.unexpected(reply, 'OKAY or FAIL') | ||
} | ||
}) | ||
} | ||
_readDevices() { | ||
return this.parser.readValue() | ||
.then(value => { | ||
return this._parseDevices(value) | ||
}) | ||
} | ||
_readDevices() { | ||
return this.parser.readValue() | ||
.then(value => { | ||
return this._parseDevices(value) | ||
}) | ||
} | ||
_parseDevices(value) { | ||
const devices = [] | ||
if (!value.length) { return devices } | ||
for (let line of value.toString('ascii').split('\n')) { | ||
if (line) { | ||
// For some reason, the columns are separated by spaces instead of tabs | ||
const [id, type, path] = Array.from(line.split(/\s+/)) | ||
devices.push({id, type, path}) | ||
} | ||
_parseDevices(value) { | ||
const devices = []; | ||
if (!value.length) { | ||
return devices | ||
} | ||
for (let line of value.toString('ascii').split('\n')) { | ||
if (line) { | ||
let array = Array.from(line.split(/\s+/)); | ||
let [id, type, path, transport_id] = Array.from(line.split(/\s+/)); | ||
for (let i = 0; i < array.length; i++) { | ||
let strs = array[i].split(':'); | ||
if (strs[0] == 'transport_id') { | ||
transport_id = strs[1]; | ||
} | ||
} | ||
devices.push({id, type, path, transport_id}) | ||
} | ||
} | ||
return devices | ||
} | ||
return devices | ||
} | ||
} | ||
module.exports = HostDevicesWithPathsCommand | ||
module.exports = HostDevicesWithPathsCommand; |
@@ -1,77 +0,82 @@ | ||
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const Net = require("net"); | ||
const events_1 = require("events"); | ||
const child_process_1 = require("child_process"); | ||
const debug = require("debug"); | ||
const parser_1 = require("./parser"); | ||
const dump = require('./dump'); | ||
class Connection extends events_1.EventEmitter { | ||
constructor(options) { | ||
super(); | ||
this.options = options; | ||
this.socket = null; | ||
this.parser = null; | ||
this.triedStarting = false; | ||
// TODO: This file was created by bulk-decaffeinate. | ||
// Sanity-check the conversion and remove this comment. | ||
const Net = require('net') | ||
const debug = require('debug')('adb:connection') | ||
const {EventEmitter} = require('events') | ||
const {execFile} = require('child_process') | ||
const Parser = require('./parser') | ||
const dump = require('./dump') | ||
class Connection extends EventEmitter { | ||
constructor(options) { | ||
super() | ||
this.options = options | ||
this.socket = null | ||
this.parser = null | ||
this.triedStarting = false | ||
} | ||
connect() { | ||
this.socket = Net.connect(this.options) | ||
this.socket.setNoDelay(true) | ||
this.parser = new Parser(this.socket) | ||
this.socket.on('connect', () => { | ||
return this.emit('connect') | ||
}) | ||
this.socket.on('end', () => { | ||
return this.emit('end') | ||
}) | ||
this.socket.on('drain', () => { | ||
return this.emit('drain') | ||
}) | ||
this.socket.on('timeout', () => { | ||
return this.emit('timeout') | ||
}) | ||
this.socket.on('error', err => { | ||
return this._handleError(err) | ||
}) | ||
this.socket.on('close', hadError => { | ||
return this.emit('close', hadError) | ||
}) | ||
return this | ||
} | ||
end() { | ||
this.socket.end() | ||
return this | ||
} | ||
write(data, callback) { | ||
this.socket.write(dump(data), callback) | ||
return this | ||
} | ||
startServer(callback) { | ||
debug(`Starting ADB server via '${this.options.bin} start-server'`) | ||
return this._exec(['start-server'], {}, callback) | ||
} | ||
_exec(args, options, callback) { | ||
debug(`CLI: ${this.options.bin} ${args.join(' ')}`) | ||
execFile(this.options.bin, args, options, callback) | ||
return this | ||
} | ||
_handleError(err) { | ||
if ((err.code === 'ECONNREFUSED') && !this.triedStarting) { | ||
debug('Connection was refused, let\'s try starting the server once') | ||
this.triedStarting = true | ||
this.startServer(err => { | ||
if (err) { return this._handleError(err) } | ||
return this.connect() | ||
}) | ||
} else { | ||
debug(`Connection had an error: ${err.message}`) | ||
this.emit('error', err) | ||
this.end() | ||
} | ||
connect() { | ||
this.socket = Net.connect(this.options); | ||
this.socket.setNoDelay(true); | ||
this.parser = new parser_1.Parser(this.socket); | ||
this.socket.on('connect', () => { | ||
return this.emit('connect'); | ||
}); | ||
this.socket.on('end', () => { | ||
return this.emit('end'); | ||
}); | ||
this.socket.on('drain', () => { | ||
return this.emit('drain'); | ||
}); | ||
this.socket.on('timeout', () => { | ||
return this.emit('timeout'); | ||
}); | ||
this.socket.on('error', err => { | ||
return this._handleError(err); | ||
}); | ||
this.socket.on('close', hadError => { | ||
return this.emit('close', hadError); | ||
}); | ||
return this; | ||
} | ||
end() { | ||
this.socket.end(); | ||
return this; | ||
} | ||
write(data, callback) { | ||
this.socket.write(dump(data), callback); | ||
return this; | ||
} | ||
startServer(callback) { | ||
debug(`Starting ADB server via '${this.options.bin} start-server'`); | ||
return this._exec(['start-server'], {}, callback); | ||
} | ||
_exec(args, options, callback) { | ||
debug(`CLI: ${this.options.bin} ${args.join(' ')}`); | ||
child_process_1.execFile(this.options.bin, args, options, callback); | ||
return this; | ||
} | ||
_handleError(err) { | ||
if ((err.code === 'ECONNREFUSED') && !this.triedStarting) { | ||
debug('Connection was refused, let\'s try starting the server once'); | ||
this.triedStarting = true; | ||
this.startServer(err => { | ||
if (err) { | ||
return this._handleError(err); | ||
} | ||
return this.connect(); | ||
}); | ||
} | ||
else { | ||
debug(`Connection had an error: ${err.message}`); | ||
this.emit('error', err); | ||
this.end(); | ||
} | ||
} | ||
} | ||
} | ||
exports.Connection = Connection; | ||
//# sourceMappingURL=connection.js.map | ||
module.exports = Connection |
// TODO: This file was created by bulk-decaffeinate. | ||
// Sanity-check the conversion and remove this comment. | ||
const Assert = require('assert') | ||
const Stream = require('stream') | ||
const Assert = require('assert'); | ||
const Stream = require('stream'); | ||
class RgbTransform extends Stream.Transform { | ||
constructor(meta, options) { | ||
super(options) | ||
this.meta = meta | ||
this._buffer = Buffer.from('') | ||
Assert.ok(((this.meta.bpp === 24) || (this.meta.bpp === 32)), | ||
'Only 24-bit and 32-bit raw images with 8-bits per color are supported') | ||
this._r_pos = this.meta.red_offset / 8 | ||
this._g_pos = this.meta.green_offset / 8 | ||
this._b_pos = this.meta.blue_offset / 8 | ||
this._a_pos = this.meta.alpha_offset / 8 | ||
this._pixel_bytes = this.meta.bpp / 8 | ||
} | ||
constructor(meta, options) { | ||
super(options); | ||
this.meta = meta; | ||
this._buffer = Buffer.from(''); | ||
Assert.ok(((this.meta.bpp === 24) || (this.meta.bpp === 32)), | ||
'Only 24-bit and 32-bit raw images with 8-bits per color are supported'); | ||
this._r_pos = this.meta.red_offset / 8; | ||
this._g_pos = this.meta.green_offset / 8; | ||
this._b_pos = this.meta.blue_offset / 8; | ||
this._a_pos = this.meta.alpha_offset / 8; | ||
this._pixel_bytes = this.meta.bpp / 8 | ||
} | ||
_transform(chunk, encoding, done) { | ||
if (this._buffer.length) { | ||
this._buffer = Buffer.concat([this._buffer, chunk], this._buffer.length + chunk.length) | ||
} else { | ||
this._buffer = chunk | ||
_transform(chunk, encoding, done) { | ||
if (this._buffer.length) { | ||
this._buffer = Buffer.concat([this._buffer, chunk], this._buffer.length + chunk.length) | ||
} else { | ||
this._buffer = chunk | ||
} | ||
let sourceCursor = 0; | ||
let targetCursor = 0; | ||
const target = this._pixel_bytes === 3 | ||
? this._buffer | ||
: Buffer.alloc(Math.max(4, (chunk.length / this._pixel_bytes) * 3)); | ||
while ((this._buffer.length - sourceCursor) >= this._pixel_bytes) { | ||
const r = this._buffer[sourceCursor + this._r_pos]; | ||
const g = this._buffer[sourceCursor + this._g_pos]; | ||
const b = this._buffer[sourceCursor + this._b_pos]; | ||
target[targetCursor + 0] = r; | ||
target[targetCursor + 1] = g; | ||
target[targetCursor + 2] = b; | ||
sourceCursor += this._pixel_bytes; | ||
targetCursor += 3 | ||
} | ||
if (targetCursor) { | ||
this.push(target.slice(0, targetCursor)); | ||
this._buffer = this._buffer.slice(sourceCursor) | ||
} | ||
done() | ||
} | ||
let sourceCursor = 0 | ||
let targetCursor = 0 | ||
const target = this._pixel_bytes === 3 | ||
? this._buffer | ||
: Buffer.alloc(Math.max(4, (chunk.length / this._pixel_bytes) * 3)) | ||
while ((this._buffer.length - sourceCursor) >= this._pixel_bytes) { | ||
const r = this._buffer[sourceCursor + this._r_pos] | ||
const g = this._buffer[sourceCursor + this._g_pos] | ||
const b = this._buffer[sourceCursor + this._b_pos] | ||
target[targetCursor + 0] = r | ||
target[targetCursor + 1] = g | ||
target[targetCursor + 2] = b | ||
sourceCursor += this._pixel_bytes | ||
targetCursor += 3 | ||
} | ||
if (targetCursor) { | ||
this.push(target.slice(0, targetCursor)) | ||
this._buffer = this._buffer.slice(sourceCursor) | ||
} | ||
done() | ||
} | ||
} | ||
module.exports = RgbTransform | ||
module.exports = RgbTransform; |
@@ -1,152 +0,193 @@ | ||
"use strict"; | ||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { | ||
return new (P || (P = Promise))(function (resolve, reject) { | ||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } | ||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } | ||
function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } | ||
step((generator = generator.apply(thisArg, _arguments || [])).next()); | ||
}); | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const Protocol = require("./protocol"); | ||
/* eslint-disable | ||
no-cond-assign, | ||
*/ | ||
// TODO: This file was created by bulk-decaffeinate. | ||
// Fix any style issues and re-enable lint. | ||
const Promise = require('bluebird'); | ||
const Protocol = require('./protocol'); | ||
class Parser { | ||
constructor(stream) { | ||
this.stream = stream; | ||
this.ended = false; | ||
this.ended = false | ||
} | ||
end() { | ||
return __awaiter(this, void 0, void 0, function* () { | ||
let work = yield new Promise((resolve, reject) => { | ||
if (this.ended) { | ||
return resolve(true); | ||
} | ||
const tryRead = () => { | ||
while (this.stream.read()) { | ||
} | ||
}; | ||
this.stream.on('readable', tryRead); | ||
this.stream.on('error', (err => reject(err))); | ||
this.stream.on('end', (() => { | ||
this.ended = true; | ||
return resolve(true); | ||
})); | ||
this.stream.read(0); | ||
this.stream.end(); | ||
}); | ||
this.stream.removeAllListeners(); | ||
return work; | ||
}); | ||
let endListener, errorListener; | ||
if (this.ended) { | ||
return Promise.resolve(true) | ||
} | ||
const resolver = Promise.defer(); | ||
const tryRead = () => { | ||
while (this.stream.read()) { | ||
} | ||
}; | ||
this.stream.on('readable', tryRead); | ||
this.stream.on('error', (errorListener = err => resolver.reject(err)) | ||
); | ||
this.stream.on('end', (endListener = () => { | ||
this.ended = true; | ||
return resolver.resolve(true) | ||
}) | ||
); | ||
this.stream.read(0); | ||
this.stream.end(); | ||
return resolver.promise.cancellable().finally(() => { | ||
this.stream.removeListener('readable', tryRead); | ||
this.stream.removeListener('error', errorListener); | ||
return this.stream.removeListener('end', endListener) | ||
}) | ||
} | ||
raw() { | ||
return this.stream; | ||
return this.stream | ||
} | ||
readAll() { | ||
return __awaiter(this, void 0, void 0, function* () { | ||
let work = yield new Promise((resolve, reject) => { | ||
let all = Buffer.alloc(0); | ||
const tryRead = () => { | ||
let chunk; | ||
while ((chunk = this.stream.read())) { | ||
all = Buffer.concat([all, chunk]); | ||
} | ||
if (this.ended) { | ||
return resolve(all); | ||
} | ||
}; | ||
this.stream.on('readable', tryRead); | ||
this.stream.on('error', (err => reject(err))); | ||
this.stream.on('end', (() => { | ||
this.ended = true; | ||
return resolve(all); | ||
})); | ||
tryRead(); | ||
}); | ||
this.stream.removeAllListeners(); | ||
return work; | ||
}); | ||
let endListener, errorListener; | ||
let all = Buffer.alloc(0); | ||
const resolver = Promise.defer(); | ||
const tryRead = () => { | ||
let chunk; | ||
while ((chunk = this.stream.read())) { | ||
all = Buffer.concat([all, chunk]) | ||
} | ||
if (this.ended) { | ||
return resolver.resolve(all) | ||
} | ||
}; | ||
this.stream.on('readable', tryRead); | ||
this.stream.on('error', (errorListener = err => resolver.reject(err)) | ||
); | ||
this.stream.on('end', (endListener = () => { | ||
this.ended = true; | ||
return resolver.resolve(all) | ||
}) | ||
); | ||
tryRead(); | ||
return resolver.promise.cancellable().finally(() => { | ||
this.stream.removeListener('readable', tryRead); | ||
this.stream.removeListener('error', errorListener); | ||
return this.stream.removeListener('end', endListener) | ||
}) | ||
} | ||
readAscii(howMany) { | ||
return this.readBytes(howMany) | ||
.then(chunk => chunk.toString('ascii')); | ||
.then(chunk => chunk.toString('ascii')) | ||
} | ||
readBytes(howMany) { | ||
return __awaiter(this, void 0, void 0, function* () { | ||
let work = yield new Promise((resolve, reject) => { | ||
const tryRead = () => { | ||
if (howMany) { | ||
let chunk; | ||
if (chunk = this.stream.read(howMany)) { | ||
howMany -= chunk.length; | ||
if (howMany === 0) { | ||
return resolve(chunk); | ||
} | ||
} | ||
if (this.ended) { | ||
return reject(new Parser.PrematureEOFError(howMany)); | ||
} | ||
const resolver = Promise.defer(); | ||
const tryRead = () => { | ||
if (howMany) { | ||
let chunk; | ||
if (chunk = this.stream.read(howMany)) { | ||
// If the stream ends while still having unread bytes, the read call | ||
// will ignore the limit and just return what it's got. | ||
howMany -= chunk.length; | ||
if (howMany === 0) { | ||
return resolver.resolve(chunk) | ||
} | ||
else { | ||
return resolve(Buffer.alloc(0)); | ||
} | ||
}; | ||
const endListener = () => { | ||
this.ended = true; | ||
return reject(new Parser.PrematureEOFError(howMany)); | ||
}; | ||
const errorListener = err => reject(err); | ||
this.stream.on('readable', tryRead); | ||
this.stream.on('error', errorListener); | ||
this.stream.on('end', endListener); | ||
tryRead(); | ||
}); | ||
this.stream.removeAllListeners(); | ||
return work; | ||
}); | ||
} | ||
if (this.ended) { | ||
return resolver.reject(new Parser.PrematureEOFError(howMany)) | ||
} | ||
} else { | ||
return resolver.resolve(Buffer.alloc(0)) | ||
} | ||
}; | ||
const endListener = () => { | ||
this.ended = true; | ||
return resolver.reject(new Parser.PrematureEOFError(howMany)) | ||
}; | ||
const errorListener = err => resolver.reject(err); | ||
this.stream.on('readable', tryRead); | ||
this.stream.on('error', errorListener); | ||
this.stream.on('end', endListener); | ||
tryRead(); | ||
return resolver.promise.cancellable().finally(() => { | ||
this.stream.removeListener('readable', tryRead); | ||
this.stream.removeListener('error', errorListener); | ||
return this.stream.removeListener('end', endListener) | ||
}) | ||
} | ||
readByteFlow(howMany, targetStream) { | ||
return __awaiter(this, void 0, void 0, function* () { | ||
let work = yield new Promise((resolve, reject) => { | ||
const tryRead = () => { | ||
if (howMany) { | ||
let chunk; | ||
while ((chunk = this.stream.read(howMany) || this.stream.read())) { | ||
howMany -= chunk.length; | ||
targetStream.write(chunk); | ||
if (howMany === 0) { | ||
return resolve(); | ||
} | ||
} | ||
if (this.ended) { | ||
return reject(new Parser.PrematureEOFError(howMany)); | ||
} | ||
const resolver = Promise.defer(); | ||
const tryRead = () => { | ||
if (howMany) { | ||
// Try to get the exact amount we need first. If unsuccessful, take | ||
// whatever is available, which will be less than the needed amount. | ||
let chunk; | ||
while ((chunk = this.stream.read(howMany) || this.stream.read())) { | ||
howMany -= chunk.length; | ||
targetStream.write(chunk); | ||
if (howMany === 0) { | ||
return resolver.resolve() | ||
} | ||
else { | ||
return resolve(); | ||
} | ||
}; | ||
const endListener = () => { | ||
this.ended = true; | ||
return reject(new Parser.PrematureEOFError(howMany)); | ||
}; | ||
const errorListener = err => reject(err); | ||
this.stream.on('readable', tryRead); | ||
this.stream.on('error', errorListener); | ||
this.stream.on('end', endListener); | ||
tryRead(); | ||
}); | ||
this.stream.removeAllListeners(); | ||
return work; | ||
}); | ||
} | ||
if (this.ended) { | ||
return resolver.reject(new Parser.PrematureEOFError(howMany)) | ||
} | ||
} else { | ||
return resolver.resolve() | ||
} | ||
}; | ||
const endListener = () => { | ||
this.ended = true; | ||
return resolver.reject(new Parser.PrematureEOFError(howMany)) | ||
}; | ||
const errorListener = err => resolver.reject(err); | ||
this.stream.on('readable', tryRead); | ||
this.stream.on('error', errorListener); | ||
this.stream.on('end', endListener); | ||
tryRead(); | ||
return resolver.promise.cancellable().finally(() => { | ||
this.stream.removeListener('readable', tryRead); | ||
this.stream.removeListener('error', errorListener); | ||
return this.stream.removeListener('end', endListener) | ||
}) | ||
} | ||
readError() { | ||
return this.readValue() | ||
.then(value => Promise.reject(new Parser.FailError(value.toString()))); | ||
.then(value => Promise.reject(new Parser.FailError(value.toString()))) | ||
} | ||
readValue() { | ||
return this.readAscii(4) | ||
.then(value => { | ||
const length = Protocol.decodeLength(value); | ||
return this.readBytes(length); | ||
}); | ||
const length = Protocol.decodeLength(value); | ||
return this.readBytes(length) | ||
}) | ||
} | ||
readUntil(code) { | ||
@@ -157,52 +198,54 @@ let skipped = Buffer.alloc(0); | ||
.then(function (chunk) { | ||
if (chunk[0] === code) { | ||
return skipped; | ||
} | ||
else { | ||
skipped = Buffer.concat([skipped, chunk]); | ||
return read(); | ||
} | ||
}); | ||
if (chunk[0] === code) { | ||
return skipped | ||
} else { | ||
skipped = Buffer.concat([skipped, chunk]); | ||
return read() | ||
} | ||
}) | ||
}; | ||
return read(); | ||
return read() | ||
} | ||
searchLine(re) { | ||
return this.readLine() | ||
.then(line => { | ||
let match; | ||
if ((match = re.exec(line))) { | ||
return match; | ||
} | ||
else { | ||
return this.searchLine(re); | ||
} | ||
}); | ||
let match; | ||
if ((match = re.exec(line))) { | ||
return match | ||
} else { | ||
return this.searchLine(re) | ||
} | ||
}) | ||
} | ||
readLine() { | ||
return this.readUntil(0x0a) | ||
return this.readUntil(0x0a) // '\n' | ||
.then(function (line) { | ||
if (line[line.length - 1] === 0x0d) { | ||
return line.slice(0, -1); | ||
} | ||
else { | ||
return line; | ||
} | ||
}); | ||
if (line[line.length - 1] === 0x0d) { // '\r' | ||
return line.slice(0, -1) | ||
} else { | ||
return line | ||
} | ||
}) | ||
} | ||
unexpected(data, expected) { | ||
return Promise.reject(new Parser.UnexpectedDataError(data, expected)); | ||
return Promise.reject(new Parser.UnexpectedDataError(data, expected)) | ||
} | ||
} | ||
Parser.FailError = class FailError extends Error { | ||
constructor(message) { | ||
super(); | ||
super(); // TODO check sanity | ||
Error.call(this); | ||
this.name = 'FailError'; | ||
this.message = `Failure: '${message}'`; | ||
Error.captureStackTrace(this, Parser.FailError); | ||
Error.captureStackTrace(this, Parser.FailError) | ||
} | ||
}; | ||
Parser.PrematureEOFError = class PrematureEOFError extends Error { | ||
constructor(howManyMissing) { | ||
super(); | ||
super(); // TODO check sanity | ||
Error.call(this); | ||
@@ -213,8 +256,9 @@ this.name = 'PrematureEOFError'; | ||
this.missingBytes = howManyMissing; | ||
Error.captureStackTrace(this, Parser.PrematureEOFError); | ||
Error.captureStackTrace(this, Parser.PrematureEOFError) | ||
} | ||
}; | ||
Parser.UnexpectedDataError = class UnexpectedDataError extends Error { | ||
constructor(unexpected, expected) { | ||
super(); | ||
super(); // TODO check sanity | ||
Error.call(this); | ||
@@ -225,6 +269,6 @@ this.name = 'UnexpectedDataError'; | ||
this.expected = expected; | ||
Error.captureStackTrace(this, Parser.UnexpectedDataError); | ||
Error.captureStackTrace(this, Parser.UnexpectedDataError) | ||
} | ||
}; | ||
exports.Parser = Parser; | ||
//# sourceMappingURL=parser.js.map | ||
module.exports = Parser; |
@@ -23,3 +23,3 @@ class Protocol { | ||
if (!Buffer.isBuffer(data)) { | ||
data = Buffer.from(data); | ||
data = Buffer.from(data) | ||
} | ||
@@ -26,0 +26,0 @@ return Buffer.concat([Buffer.from(Protocol.encodeLength(data.length)), data]) |
@@ -1,273 +0,328 @@ | ||
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const Fs = require('fs'); | ||
const Path = require('path'); | ||
const Promise = require('bluebird'); | ||
const { EventEmitter } = require('events'); | ||
const debug = require('debug')('adb:sync'); | ||
const Parser = require('./parser'); | ||
const Protocol = require('./protocol'); | ||
const Stats = require('./sync/stats'); | ||
const Entry = require('./sync/entry'); | ||
const PushTransfer = require('./sync/pushtransfer'); | ||
const PullTransfer = require('./sync/pulltransfer'); | ||
class Sync extends EventEmitter { | ||
constructor(connection) { | ||
super(); | ||
this.DEFAULT_CHMOD = 0o644; | ||
this.DATA_MAX_LENGTH = 65536; | ||
this.connection = connection; | ||
this.parser = this.connection.parser; | ||
/* eslint-disable | ||
no-cond-assign, | ||
no-unused-vars, | ||
*/ | ||
// TODO: This file was created by bulk-decaffeinate. | ||
// Fix any style issues and re-enable lint. | ||
const Fs = require('fs') | ||
const Path = require('path') | ||
const Promise = require('bluebird') | ||
const {EventEmitter} = require('events') | ||
const debug = require('debug')('adb:sync') | ||
const Parser = require('./parser') | ||
const Protocol = require('./protocol') | ||
const Stats = require('./sync/stats') | ||
const Entry = require('./sync/entry') | ||
const PushTransfer = require('./sync/pushtransfer') | ||
const PullTransfer = require('./sync/pulltransfer') | ||
var Sync = (function() { | ||
let TEMP_PATH = undefined | ||
let DEFAULT_CHMOD = undefined | ||
let DATA_MAX_LENGTH = undefined | ||
Sync = class Sync extends EventEmitter { | ||
static initClass() { | ||
TEMP_PATH = '/data/local/tmp' | ||
DEFAULT_CHMOD = 0o644 | ||
DATA_MAX_LENGTH = 65536 | ||
} | ||
static temp(path) { | ||
return `${Sync.TEMP_PATH}/${Path.basename(path)}`; | ||
return `${TEMP_PATH}/${Path.basename(path)}` | ||
} | ||
constructor(connection) { | ||
super() | ||
this.connection = connection | ||
this.parser = this.connection.parser | ||
} | ||
stat(path, callback) { | ||
this._sendCommandWithArg(Protocol.STAT, path); | ||
this._sendCommandWithArg(Protocol.STAT, path) | ||
return this.parser.readAscii(4) | ||
.then(reply => { | ||
switch (reply) { | ||
case Protocol.STAT: | ||
return this.parser.readBytes(12) | ||
.then(stat => { | ||
const mode = stat.readUInt32LE(0) | ||
const size = stat.readUInt32LE(4) | ||
const mtime = stat.readUInt32LE(8) | ||
if (mode === 0) { | ||
return this._enoent(path) | ||
} else { | ||
return new Stats(mode, size, mtime) | ||
} | ||
}) | ||
case Protocol.FAIL: | ||
return this._readError() | ||
default: | ||
return this.parser.unexpected(reply, 'STAT or FAIL') | ||
} | ||
}).nodeify(callback) | ||
} | ||
readdir(path, callback) { | ||
const files = [] | ||
var readNext = () => { | ||
return this.parser.readAscii(4) | ||
.then(reply => { | ||
.then(reply => { | ||
switch (reply) { | ||
case Protocol.STAT: | ||
return this.parser.readBytes(12) | ||
.then(stat => { | ||
const mode = stat.readUInt32LE(0); | ||
const size = stat.readUInt32LE(4); | ||
const mtime = stat.readUInt32LE(8); | ||
if (mode === 0) { | ||
return this._enoent(path); | ||
} | ||
else { | ||
return new Stats(mode, size, mtime); | ||
} | ||
}); | ||
case Protocol.FAIL: | ||
return this._readError(); | ||
default: | ||
return this.parser.unexpected(reply, 'STAT or FAIL'); | ||
case Protocol.DENT: | ||
return this.parser.readBytes(16) | ||
.then(stat => { | ||
const mode = stat.readUInt32LE(0) | ||
const size = stat.readUInt32LE(4) | ||
const mtime = stat.readUInt32LE(8) | ||
const namelen = stat.readUInt32LE(12) | ||
return this.parser.readBytes(namelen) | ||
.then(function(name) { | ||
name = name.toString() | ||
// Skip '.' and '..' to match Node's fs.readdir(). | ||
if ((name !== '.') && (name !== '..')) { | ||
files.push(new Entry(name, mode, size, mtime)) | ||
} | ||
return readNext() | ||
}) | ||
}) | ||
case Protocol.DONE: | ||
return this.parser.readBytes(16) | ||
.then(zero => files) | ||
case Protocol.FAIL: | ||
return this._readError() | ||
default: | ||
return this.parser.unexpected(reply, 'DENT, DONE or FAIL') | ||
} | ||
}).nodeify(callback); | ||
}) | ||
} | ||
this._sendCommandWithArg(Protocol.LIST, path) | ||
return readNext() | ||
.nodeify(callback) | ||
} | ||
readdir(path, callback) { | ||
const files = []; | ||
var readNext = () => { | ||
return this.parser.readAscii(4) | ||
.then(reply => { | ||
switch (reply) { | ||
case Protocol.DENT: | ||
return this.parser.readBytes(16) | ||
.then(stat => { | ||
const mode = stat.readUInt32LE(0); | ||
const size = stat.readUInt32LE(4); | ||
const mtime = stat.readUInt32LE(8); | ||
const namelen = stat.readUInt32LE(12); | ||
return this.parser.readBytes(namelen) | ||
.then(function (name) { | ||
name = name.toString(); | ||
if ((name !== '.') && (name !== '..')) { | ||
files.push(new Entry(name, mode, size, mtime)); | ||
} | ||
return readNext(); | ||
}); | ||
}); | ||
case Protocol.DONE: | ||
return this.parser.readBytes(16) | ||
.then(zero => files); | ||
case Protocol.FAIL: | ||
return this._readError(); | ||
default: | ||
return this.parser.unexpected(reply, 'DENT, DONE or FAIL'); | ||
} | ||
}); | ||
}; | ||
this._sendCommandWithArg(Protocol.LIST, path); | ||
return readNext() | ||
.nodeify(callback); | ||
} | ||
push(contents, path, mode) { | ||
if (typeof contents === 'string') { | ||
return this.pushFile(contents, path, mode); | ||
} | ||
else { | ||
return this.pushStream(contents, path, mode); | ||
} | ||
if (typeof contents === 'string') { | ||
return this.pushFile(contents, path, mode) | ||
} else { | ||
return this.pushStream(contents, path, mode) | ||
} | ||
} | ||
pushFile(file, path, mode = this.DEFAULT_CHMOD) { | ||
if (!mode) { | ||
mode = this.DEFAULT_CHMOD; | ||
} | ||
return this.pushStream(Fs.createReadStream(file), path, mode); | ||
pushFile(file, path, mode = DEFAULT_CHMOD) { | ||
if (!mode) { mode = DEFAULT_CHMOD } | ||
return this.pushStream(Fs.createReadStream(file), path, mode) | ||
} | ||
pushStream(stream, path, mode = this.DEFAULT_CHMOD) { | ||
mode |= Stats.S_IFREG; | ||
this._sendCommandWithArg(Protocol.SEND, `${path},${mode}`); | ||
return this._writeData(stream, Math.floor(Date.now() / 1000)); | ||
pushStream(stream, path, mode = DEFAULT_CHMOD) { | ||
mode |= Stats.S_IFREG | ||
this._sendCommandWithArg(Protocol.SEND, `${path},${mode}`) | ||
return this._writeData(stream, Math.floor(Date.now() / 1000)) | ||
} | ||
pull(path) { | ||
this._sendCommandWithArg(Protocol.RECV, `${path}`); | ||
return this._readData(); | ||
this._sendCommandWithArg(Protocol.RECV, `${path}`) | ||
return this._readData() | ||
} | ||
end() { | ||
this.connection.end(); | ||
return this; | ||
this.connection.end() | ||
return this | ||
} | ||
tempFile(path) { | ||
return Sync.temp(path); | ||
return Sync.temp(path) | ||
} | ||
_writeData(stream, timeStamp) { | ||
let writer; | ||
const transfer = new PushTransfer; | ||
const writeData = () => { | ||
let endListener, errorListener, readableListener; | ||
let resolver = Promise.defer(); | ||
writer = Promise.resolve() | ||
.cancellable(); | ||
stream.on('end', (endListener = () => { | ||
return writer.then(() => { | ||
this._sendCommandWithLength(Protocol.DONE, timeStamp); | ||
return resolver.resolve(); | ||
}); | ||
})); | ||
const waitForDrain = () => { | ||
let drainListener; | ||
resolver = Promise.defer(); | ||
this.connection.on('drain', (drainListener = () => resolver.resolve())); | ||
return resolver.promise.finally(() => { | ||
return this.connection.removeListener('drain', drainListener); | ||
}); | ||
}; | ||
const track = () => transfer.pop(); | ||
var writeNext = () => { | ||
let chunk; | ||
if (chunk = stream.read(this.DATA_MAX_LENGTH) || stream.read()) { | ||
this._sendCommandWithLength(Protocol.DATA, chunk.length); | ||
transfer.push(chunk.length); | ||
if (this.connection.write(chunk, track)) { | ||
return writeNext(); | ||
} | ||
else { | ||
return waitForDrain() | ||
.then(writeNext); | ||
} | ||
} | ||
else { | ||
return Promise.resolve(); | ||
} | ||
}; | ||
stream.on('readable', (readableListener = () => writer.then(writeNext))); | ||
stream.on('error', (errorListener = err => resolver.reject(err))); | ||
return resolver.promise.finally(function () { | ||
stream.removeListener('end', endListener); | ||
stream.removeListener('readable', readableListener); | ||
stream.removeListener('error', errorListener); | ||
return writer.cancel(); | ||
}); | ||
}; | ||
const readReply = () => { | ||
return this.parser.readAscii(4) | ||
.then(reply => { | ||
switch (reply) { | ||
case Protocol.OKAY: | ||
return this.parser.readBytes(4) | ||
.then(zero => true); | ||
case Protocol.FAIL: | ||
return this._readError(); | ||
default: | ||
return this.parser.unexpected(reply, 'OKAY or FAIL'); | ||
} | ||
}); | ||
}; | ||
writer = writeData() | ||
.cancellable() | ||
.catch(Promise.CancellationError, err => { | ||
return this.connection.end(); | ||
}).catch(function (err) { | ||
transfer.emit('error', err); | ||
return reader.cancel(); | ||
}); | ||
var reader = readReply() | ||
.cancellable() | ||
.catch(Promise.CancellationError, err => true).catch(function (err) { | ||
transfer.emit('error', err); | ||
return writer.cancel(); | ||
}).finally(() => transfer.end()); | ||
transfer.on('cancel', function () { | ||
writer.cancel(); | ||
return reader.cancel(); | ||
}); | ||
return transfer; | ||
let writer | ||
const transfer = new PushTransfer | ||
const writeData = () => { | ||
let endListener, errorListener, readableListener | ||
let resolver = Promise.defer() | ||
writer = Promise.resolve() | ||
.cancellable() | ||
stream.on('end', (endListener = () => { | ||
return writer.then(() => { | ||
this._sendCommandWithLength(Protocol.DONE, timeStamp) | ||
return resolver.resolve() | ||
}) | ||
}) | ||
) | ||
const waitForDrain = () => { | ||
let drainListener | ||
resolver = Promise.defer() | ||
this.connection.on('drain', (drainListener = () => resolver.resolve()) | ||
) | ||
return resolver.promise.finally(() => { | ||
return this.connection.removeListener('drain', drainListener) | ||
}) | ||
} | ||
const track = () => transfer.pop() | ||
var writeNext = () => { | ||
let chunk | ||
if (chunk = stream.read(DATA_MAX_LENGTH) || stream.read()) { | ||
this._sendCommandWithLength(Protocol.DATA, chunk.length) | ||
transfer.push(chunk.length) | ||
if (this.connection.write(chunk, track)) { | ||
return writeNext() | ||
} else { | ||
return waitForDrain() | ||
.then(writeNext) | ||
} | ||
} else { | ||
return Promise.resolve() | ||
} | ||
} | ||
stream.on('readable', (readableListener = () => writer.then(writeNext)) | ||
) | ||
stream.on('error', (errorListener = err => resolver.reject(err)) | ||
) | ||
return resolver.promise.finally(function() { | ||
stream.removeListener('end', endListener) | ||
stream.removeListener('readable', readableListener) | ||
stream.removeListener('error', errorListener) | ||
return writer.cancel() | ||
}) | ||
} | ||
const readReply = () => { | ||
return this.parser.readAscii(4) | ||
.then(reply => { | ||
switch (reply) { | ||
case Protocol.OKAY: | ||
return this.parser.readBytes(4) | ||
.then(zero => true) | ||
case Protocol.FAIL: | ||
return this._readError() | ||
default: | ||
return this.parser.unexpected(reply, 'OKAY or FAIL') | ||
} | ||
}) | ||
} | ||
// While I can't think of a case that would break this double-Promise | ||
// writer-reader arrangement right now, it's not immediately obvious | ||
// that the code is correct and it may or may not have some failing | ||
// edge cases. Refactor pending. | ||
writer = writeData() | ||
.cancellable() | ||
.catch(Promise.CancellationError, err => { | ||
return this.connection.end() | ||
}).catch(function(err) { | ||
transfer.emit('error', err) | ||
return reader.cancel() | ||
}) | ||
var reader = readReply() | ||
.cancellable() | ||
.catch(Promise.CancellationError, err => true).catch(function(err) { | ||
transfer.emit('error', err) | ||
return writer.cancel()}).finally(() => transfer.end()) | ||
transfer.on('cancel', function() { | ||
writer.cancel() | ||
return reader.cancel() | ||
}) | ||
return transfer | ||
} | ||
_readData() { | ||
let cancelListener; | ||
const transfer = new PullTransfer; | ||
var readNext = () => { | ||
return this.parser.readAscii(4) | ||
.cancellable() | ||
.then(reply => { | ||
switch (reply) { | ||
case Protocol.DATA: | ||
return this.parser.readBytes(4) | ||
.then(lengthData => { | ||
const length = lengthData.readUInt32LE(0); | ||
return this.parser.readByteFlow(length, transfer) | ||
.then(readNext); | ||
}); | ||
case Protocol.DONE: | ||
return this.parser.readBytes(4) | ||
.then(zero => true); | ||
case Protocol.FAIL: | ||
return this._readError(); | ||
default: | ||
return this.parser.unexpected(reply, 'DATA, DONE or FAIL'); | ||
} | ||
}); | ||
}; | ||
const reader = readNext() | ||
.catch(Promise.CancellationError, err => { | ||
return this.connection.end(); | ||
}).catch(err => transfer.emit('error', err)).finally(function () { | ||
transfer.removeListener('cancel', cancelListener); | ||
return transfer.end(); | ||
}); | ||
transfer.on('cancel', (cancelListener = () => reader.cancel())); | ||
return transfer; | ||
let cancelListener | ||
const transfer = new PullTransfer | ||
var readNext = () => { | ||
return this.parser.readAscii(4) | ||
.cancellable() | ||
.then(reply => { | ||
switch (reply) { | ||
case Protocol.DATA: | ||
return this.parser.readBytes(4) | ||
.then(lengthData => { | ||
const length = lengthData.readUInt32LE(0) | ||
return this.parser.readByteFlow(length, transfer) | ||
.then(readNext) | ||
}) | ||
case Protocol.DONE: | ||
return this.parser.readBytes(4) | ||
.then(zero => true) | ||
case Protocol.FAIL: | ||
return this._readError() | ||
default: | ||
return this.parser.unexpected(reply, 'DATA, DONE or FAIL') | ||
} | ||
}) | ||
} | ||
const reader = readNext() | ||
.catch(Promise.CancellationError, err => { | ||
return this.connection.end() | ||
}).catch(err => transfer.emit('error', err)).finally(function() { | ||
transfer.removeListener('cancel', cancelListener) | ||
return transfer.end() | ||
}) | ||
transfer.on('cancel', (cancelListener = () => reader.cancel()) | ||
) | ||
return transfer | ||
} | ||
_readError() { | ||
return this.parser.readBytes(4) | ||
.then(length => { | ||
return this.parser.readBytes(length.readUInt32LE(0)) | ||
.then(buf => Promise.reject(new Parser.FailError(buf.toString()))); | ||
return this.parser.readBytes(4) | ||
.then(length => { | ||
return this.parser.readBytes(length.readUInt32LE(0)) | ||
.then(buf => Promise.reject(new Parser.FailError(buf.toString()))) | ||
}).finally(() => { | ||
return this.parser.end(); | ||
}); | ||
return this.parser.end() | ||
}) | ||
} | ||
_sendCommandWithLength(cmd, length) { | ||
if (cmd !== Protocol.DATA) { | ||
debug(cmd); | ||
} | ||
const payload = Buffer.alloc(cmd.length + 4); | ||
payload.write(cmd, 0, cmd.length); | ||
payload.writeUInt32LE(length, cmd.length); | ||
return this.connection.write(payload); | ||
if (cmd !== Protocol.DATA) { debug(cmd) } | ||
const payload = Buffer.alloc(cmd.length + 4) | ||
payload.write(cmd, 0, cmd.length) | ||
payload.writeUInt32LE(length, cmd.length) | ||
return this.connection.write(payload) | ||
} | ||
_sendCommandWithArg(cmd, arg) { | ||
debug(`${cmd} ${arg}`); | ||
const payload = Buffer.alloc(cmd.length + 4 + arg.length); | ||
let pos = 0; | ||
payload.write(cmd, pos, cmd.length); | ||
pos += cmd.length; | ||
payload.writeUInt32LE(arg.length, pos); | ||
pos += 4; | ||
payload.write(arg, pos); | ||
return this.connection.write(payload); | ||
debug(`${cmd} ${arg}`) | ||
const payload = Buffer.alloc(cmd.length + 4 + arg.length) | ||
let pos = 0 | ||
payload.write(cmd, pos, cmd.length) | ||
pos += cmd.length | ||
payload.writeUInt32LE(arg.length, pos) | ||
pos += 4 | ||
payload.write(arg, pos) | ||
return this.connection.write(payload) | ||
} | ||
_enoent(path) { | ||
const err = new Error(`ENOENT, no such file or directory '${path}'`); | ||
err.errno = 34; | ||
err.code = 'ENOENT'; | ||
err.path = path; | ||
return Promise.reject(err); | ||
const err = new Error(`ENOENT, no such file or directory '${path}'`) | ||
err.errno = 34 | ||
err.code = 'ENOENT' | ||
err.path = path | ||
return Promise.reject(err) | ||
} | ||
} | ||
Sync.TEMP_PATH = '/data/local/tmp'; | ||
exports.Sync = Sync; | ||
//# sourceMappingURL=sync.js.map | ||
} | ||
Sync.initClass() | ||
return Sync | ||
})() | ||
module.exports = Sync |
// TODO: This file was created by bulk-decaffeinate. | ||
// Sanity-check the conversion and remove this comment. | ||
class Packet { | ||
static initClass() { | ||
this.A_SYNC = 0x434e5953 | ||
this.A_CNXN = 0x4e584e43 | ||
this.A_OPEN = 0x4e45504f | ||
this.A_OKAY = 0x59414b4f | ||
this.A_CLSE = 0x45534c43 | ||
this.A_WRTE = 0x45545257 | ||
this.A_AUTH = 0x48545541 | ||
} | ||
static initClass() { | ||
this.A_SYNC = 0x434e5953; | ||
this.A_CNXN = 0x4e584e43; | ||
this.A_OPEN = 0x4e45504f; | ||
this.A_OKAY = 0x59414b4f; | ||
this.A_CLSE = 0x45534c43; | ||
this.A_WRTE = 0x45545257; | ||
this.A_AUTH = 0x48545541 | ||
} | ||
static checksum(data) { | ||
let sum = 0 | ||
if (data) { for (let char of data) { sum += char } } | ||
return sum | ||
} | ||
static checksum(data) { | ||
let sum = 0; | ||
if (data) { | ||
for (let char of data) { | ||
sum += char | ||
} | ||
} | ||
return sum | ||
} | ||
static magic(command) { | ||
// We need the full uint32 range, which ">>> 0" thankfully allows us to use | ||
return (command ^ 0xffffffff) >>> 0 | ||
} | ||
static magic(command) { | ||
// We need the full uint32 range, which ">>> 0" thankfully allows us to use | ||
return (command ^ 0xffffffff) >>> 0 | ||
} | ||
static assemble(command, arg0, arg1, data) { | ||
let chunk | ||
if (data) { | ||
chunk = Buffer.alloc(24 + data.length) | ||
chunk.writeUInt32LE(command, 0) | ||
chunk.writeUInt32LE(arg0, 4) | ||
chunk.writeUInt32LE(arg1, 8) | ||
chunk.writeUInt32LE(data.length, 12) | ||
chunk.writeUInt32LE(Packet.checksum(data), 16) | ||
chunk.writeUInt32LE(Packet.magic(command), 20) | ||
data.copy(chunk, 24) | ||
return chunk | ||
} else { | ||
chunk = Buffer.alloc(24) | ||
chunk.writeUInt32LE(command, 0) | ||
chunk.writeUInt32LE(arg0, 4) | ||
chunk.writeUInt32LE(arg1, 8) | ||
chunk.writeUInt32LE(0, 12) | ||
chunk.writeUInt32LE(0, 16) | ||
chunk.writeUInt32LE(Packet.magic(command), 20) | ||
return chunk | ||
static assemble(command, arg0, arg1, data) { | ||
let chunk; | ||
if (data) { | ||
chunk = Buffer.alloc(24 + data.length); | ||
chunk.writeUInt32LE(command, 0); | ||
chunk.writeUInt32LE(arg0, 4); | ||
chunk.writeUInt32LE(arg1, 8); | ||
chunk.writeUInt32LE(data.length, 12); | ||
chunk.writeUInt32LE(Packet.checksum(data), 16); | ||
chunk.writeUInt32LE(Packet.magic(command), 20); | ||
data.copy(chunk, 24); | ||
return chunk | ||
} else { | ||
chunk = Buffer.alloc(24); | ||
chunk.writeUInt32LE(command, 0); | ||
chunk.writeUInt32LE(arg0, 4); | ||
chunk.writeUInt32LE(arg1, 8); | ||
chunk.writeUInt32LE(0, 12); | ||
chunk.writeUInt32LE(0, 16); | ||
chunk.writeUInt32LE(Packet.magic(command), 20); | ||
return chunk | ||
} | ||
} | ||
} | ||
static swap32(n) { | ||
const buffer = Buffer.alloc(4) | ||
buffer.writeUInt32LE(n, 0) | ||
return buffer.readUInt32BE(0) | ||
} | ||
static swap32(n) { | ||
const buffer = Buffer.alloc(4); | ||
buffer.writeUInt32LE(n, 0); | ||
return buffer.readUInt32BE(0) | ||
} | ||
constructor(command, arg0, arg1, length, check, magic, data) { | ||
this.command = command | ||
this.arg0 = arg0 | ||
this.arg1 = arg1 | ||
this.length = length | ||
this.check = check | ||
this.magic = magic | ||
this.data = data | ||
} | ||
constructor(command, arg0, arg1, length, check, magic, data) { | ||
this.command = command; | ||
this.arg0 = arg0; | ||
this.arg1 = arg1; | ||
this.length = length; | ||
this.check = check; | ||
this.magic = magic; | ||
this.data = data | ||
} | ||
verifyChecksum() { | ||
return this.check === Packet.checksum(this.data) | ||
} | ||
verifyChecksum() { | ||
return this.check === Packet.checksum(this.data) | ||
} | ||
verifyMagic() { | ||
return this.magic === Packet.magic(this.command) | ||
} | ||
verifyMagic() { | ||
return this.magic === Packet.magic(this.command) | ||
} | ||
toString() { | ||
const type = (() => { switch (this.command) { | ||
case Packet.A_SYNC: | ||
return 'SYNC' | ||
case Packet.A_CNXN: | ||
return 'CNXN' | ||
case Packet.A_OPEN: | ||
return 'OPEN' | ||
case Packet.A_OKAY: | ||
return 'OKAY' | ||
case Packet.A_CLSE: | ||
return 'CLSE' | ||
case Packet.A_WRTE: | ||
return 'WRTE' | ||
case Packet.A_AUTH: | ||
return 'AUTH' | ||
default: | ||
throw new Error('Unknown command {@command}') | ||
} })() | ||
return `${type} arg0=${this.arg0} arg1=${this.arg1} length=${this.length}` | ||
} | ||
toString() { | ||
const type = (() => { | ||
switch (this.command) { | ||
case Packet.A_SYNC: | ||
return 'SYNC'; | ||
case Packet.A_CNXN: | ||
return 'CNXN'; | ||
case Packet.A_OPEN: | ||
return 'OPEN'; | ||
case Packet.A_OKAY: | ||
return 'OKAY'; | ||
case Packet.A_CLSE: | ||
return 'CLSE'; | ||
case Packet.A_WRTE: | ||
return 'WRTE'; | ||
case Packet.A_AUTH: | ||
return 'AUTH'; | ||
default: | ||
throw new Error('Unknown command {@command}') | ||
} | ||
})(); | ||
return `${type} arg0=${this.arg0} arg1=${this.arg1} length=${this.length}` | ||
} | ||
} | ||
Packet.initClass() | ||
module.exports = Packet | ||
Packet.initClass(); | ||
module.exports = Packet; |
// TODO: This file was created by bulk-decaffeinate. | ||
// Sanity-check the conversion and remove this comment. | ||
const {EventEmitter} = require('events'); | ||
const {EventEmitter} = require('events') | ||
const Packet = require('./packet'); | ||
const Packet = require('./packet') | ||
class PacketReader extends EventEmitter { | ||
constructor(stream) { | ||
super(); | ||
this.stream = stream; | ||
this.inBody = false; | ||
this.buffer = null; | ||
this.packet = null; | ||
this.stream.on('readable', this._tryRead.bind(this)); | ||
this.stream.on('error', err => this.emit('error', err)); | ||
this.stream.on('end', () => this.emit('end')); | ||
super() | ||
this.stream = stream | ||
this.inBody = false | ||
this.buffer = null | ||
this.packet = null | ||
this.stream.on('readable', this._tryRead.bind(this)) | ||
this.stream.on('error', err => this.emit('error', err)) | ||
this.stream.on('end', () => this.emit('end')) | ||
setImmediate(this._tryRead.bind(this)) | ||
@@ -27,8 +27,8 @@ } | ||
} | ||
this.packet.data = this._consume(this.packet.length); | ||
this.packet.data = this._consume(this.packet.length) | ||
if (!this.packet.verifyChecksum()) { | ||
this.emit('error', new PacketReader.ChecksumError(this.packet)); | ||
this.emit('error', new PacketReader.ChecksumError(this.packet)) | ||
return | ||
} | ||
this.emit('packet', this.packet); | ||
this.emit('packet', this.packet) | ||
this.inBody = false | ||
@@ -39,3 +39,3 @@ } else { | ||
} | ||
const header = this._consume(24); | ||
const header = this._consume(24) | ||
this.packet = new Packet( | ||
@@ -49,5 +49,5 @@ header.readUInt32LE(0), | ||
Buffer.alloc(0) | ||
); | ||
) | ||
if (!this.packet.verifyMagic()) { | ||
this.emit('error', new PacketReader.MagicError(this.packet)); | ||
this.emit('error', new PacketReader.MagicError(this.packet)) | ||
return | ||
@@ -66,3 +66,3 @@ } | ||
_appendChunk() { | ||
let chunk; | ||
let chunk | ||
if ((chunk = this.stream.read())) { | ||
@@ -80,4 +80,4 @@ if (this.buffer) { | ||
_consume(length) { | ||
const chunk = this.buffer.slice(0, length); | ||
this.buffer = length === this.buffer.length ? null : this.buffer.slice(length); | ||
const chunk = this.buffer.slice(0, length) | ||
this.buffer = length === this.buffer.length ? null : this.buffer.slice(length) | ||
return chunk | ||
@@ -89,22 +89,22 @@ } | ||
constructor(packet) { | ||
super(); // TODO check sanity | ||
Error.call(this); | ||
this.name = 'ChecksumError'; | ||
this.message = 'Checksum mismatch'; | ||
this.packet = packet; | ||
super() // TODO check sanity | ||
Error.call(this) | ||
this.name = 'ChecksumError' | ||
this.message = 'Checksum mismatch' | ||
this.packet = packet | ||
Error.captureStackTrace(this, PacketReader.ChecksumError) | ||
} | ||
}; | ||
} | ||
PacketReader.MagicError = class MagicError extends Error { | ||
constructor(packet) { | ||
super(); // TODO check sanity | ||
Error.call(this); | ||
this.name = 'MagicError'; | ||
this.message = 'Magic value mismatch'; | ||
this.packet = packet; | ||
super() // TODO check sanity | ||
Error.call(this) | ||
this.name = 'MagicError' | ||
this.message = 'Magic value mismatch' | ||
this.packet = packet | ||
Error.captureStackTrace(this, PacketReader.MagicError) | ||
} | ||
}; | ||
} | ||
module.exports = PacketReader; | ||
module.exports = PacketReader |
@@ -1,51 +0,56 @@ | ||
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const socket_1 = require("./socket"); | ||
const Net = require('net'); | ||
const { EventEmitter } = require('events'); | ||
// TODO: This file was created by bulk-decaffeinate. | ||
// Sanity-check the conversion and remove this comment. | ||
const Net = require('net') | ||
const {EventEmitter} = require('events') | ||
const Socket = require('./socket') | ||
class Server extends EventEmitter { | ||
constructor(client, serial, options) { | ||
super(); | ||
this.client = client; | ||
this.serial = serial; | ||
this.options = options; | ||
this.connections = []; | ||
this.server = Net.createServer({ allowHalfOpen: true }); | ||
this.server.on('error', err => { | ||
return this.emit('error', err); | ||
}); | ||
this.server.on('listening', () => { | ||
return this.emit('listening'); | ||
}); | ||
this.server.on('close', () => { | ||
return this.emit('close'); | ||
}); | ||
this.server.on('connection', conn => { | ||
const socket = new socket_1.Socket(this.client, this.serial, conn, this.options); | ||
this.connections.push(socket); | ||
socket.on('error', err => { | ||
return this.emit('error', err); | ||
}); | ||
socket.once('end', () => { | ||
return this.connections = this.connections.filter(val => val !== socket); | ||
}); | ||
return this.emit('connection', socket); | ||
}); | ||
} | ||
listen() { | ||
this.server.listen.apply(this.server, arguments); | ||
return this; | ||
} | ||
close() { | ||
this.server.close(); | ||
return this; | ||
} | ||
end() { | ||
for (let conn of this.connections) { | ||
conn.end(); | ||
} | ||
return this; | ||
} | ||
constructor(client, serial, options) { | ||
super() | ||
this.client = client | ||
this.serial = serial | ||
this.options = options | ||
this.connections = [] | ||
this.server = Net.createServer({allowHalfOpen: true}) | ||
this.server.on('error', err => { | ||
return this.emit('error', err) | ||
}) | ||
this.server.on('listening', () => { | ||
return this.emit('listening') | ||
}) | ||
this.server.on('close', () => { | ||
return this.emit('close') | ||
}) | ||
this.server.on('connection', conn => { | ||
const socket = new Socket(this.client, this.serial, conn, this.options) | ||
this.connections.push(socket) | ||
socket.on('error', err => { | ||
// 'conn' is guaranteed to get ended | ||
return this.emit('error', err) | ||
}) | ||
socket.once('end', () => { | ||
// 'conn' is guaranteed to get ended | ||
return this.connections = this.connections.filter(val => val !== socket) | ||
}) | ||
return this.emit('connection', socket) | ||
}) | ||
} | ||
listen() { | ||
this.server.listen.apply(this.server, arguments) | ||
return this | ||
} | ||
close() { | ||
this.server.close() | ||
return this | ||
} | ||
end() { | ||
for (let conn of this.connections) { conn.end() } | ||
return this | ||
} | ||
} | ||
exports.Server = Server; | ||
//# sourceMappingURL=server.js.map | ||
module.exports = Server |
@@ -1,7 +0,15 @@ | ||
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const bluebird_1 = require("bluebird"); | ||
/* eslint-disable | ||
no-unused-vars, | ||
*/ | ||
// TODO: This file was created by bulk-decaffeinate. | ||
// Fix any style issues and re-enable lint. | ||
const crypto = require('crypto'); | ||
const { EventEmitter } = require('events'); | ||
const {EventEmitter} = require('events'); | ||
const Promise = require('bluebird'); | ||
const Forge = require('node-forge'); | ||
const debug = require('debug')('adb:tcpusb:socket'); | ||
const Parser = require('../parser'); | ||
const Protocol = require('../protocol'); | ||
const Auth = require('../auth'); | ||
@@ -13,224 +21,258 @@ const Packet = require('./packet'); | ||
const RollingCounter = require('./rollingcounter'); | ||
class Socket extends EventEmitter { | ||
constructor(client, serial, socket, options = {}) { | ||
super(); | ||
this.UINT32_MAX = 0xFFFFFFFF; | ||
this.UINT16_MAX = 0xFFFF; | ||
this.AUTH_TOKEN = 1; | ||
this.AUTH_SIGNATURE = 2; | ||
this.AUTH_RSAPUBLICKEY = 3; | ||
this.TOKEN_LENGTH = 20; | ||
this.client = client; | ||
this.serial = serial; | ||
this.socket = socket; | ||
this.options = options; | ||
if (!this.options.auth) { | ||
this.options.auth = bluebird_1.default.resolve(true); | ||
var Socket = (function () { | ||
let UINT32_MAX = undefined; | ||
let UINT16_MAX = undefined; | ||
let AUTH_TOKEN = undefined; | ||
let AUTH_SIGNATURE = undefined; | ||
let AUTH_RSAPUBLICKEY = undefined; | ||
let TOKEN_LENGTH = undefined; | ||
Socket = class Socket extends EventEmitter { | ||
static initClass() { | ||
UINT32_MAX = 0xFFFFFFFF; | ||
UINT16_MAX = 0xFFFF; | ||
AUTH_TOKEN = 1; | ||
AUTH_SIGNATURE = 2; | ||
AUTH_RSAPUBLICKEY = 3; | ||
TOKEN_LENGTH = 20 | ||
} | ||
this.ended = false; | ||
this.socket.setNoDelay(true); | ||
this.reader = new PacketReader(this.socket) | ||
.on('packet', this._handle.bind(this)) | ||
.on('error', err => { | ||
debug(`PacketReader error: ${err.message}`); | ||
return this.end(); | ||
}).on('end', this.end.bind(this)); | ||
this.version = 1; | ||
this.maxPayload = 4096; | ||
this.authorized = false; | ||
this.syncToken = new RollingCounter(this.UINT32_MAX); | ||
this.remoteId = new RollingCounter(this.UINT32_MAX); | ||
this.services = new ServiceMap; | ||
this.remoteAddress = this.socket.remoteAddress; | ||
this.token = null; | ||
this.signature = null; | ||
} | ||
end() { | ||
if (this.ended) { | ||
return this; | ||
constructor(client, serial, socket, options = {}) { | ||
super(); | ||
this.client = client; | ||
this.serial = serial; | ||
this.socket = socket; | ||
this.options = options; | ||
if (!this.options.auth) { | ||
this.options.auth = Promise.resolve(true) | ||
} | ||
this.ended = false; | ||
this.socket.setNoDelay(true); | ||
this.reader = new PacketReader(this.socket) | ||
.on('packet', this._handle.bind(this)) | ||
.on('error', err => { | ||
debug(`PacketReader error: ${err.message}`); | ||
return this.end() | ||
}).on('end', this.end.bind(this)); | ||
this.version = 1; | ||
this.maxPayload = 4096; | ||
this.authorized = false; | ||
this.syncToken = new RollingCounter(UINT32_MAX); | ||
this.remoteId = new RollingCounter(UINT32_MAX); | ||
this.services = new ServiceMap; | ||
this.remoteAddress = this.socket.remoteAddress; | ||
this.token = null; | ||
this.signature = null | ||
} | ||
this.services.end(); | ||
this.socket.end(); | ||
this.ended = true; | ||
this.emit('end'); | ||
return this; | ||
} | ||
_error(err) { | ||
this.emit('error', err); | ||
return this.end(); | ||
} | ||
_handle(packet) { | ||
if (this.ended) { | ||
return; | ||
end() { | ||
if (this.ended) { | ||
return this | ||
} | ||
// End services first so that they can send a final payload before FIN. | ||
this.services.end(); | ||
this.socket.end(); | ||
this.ended = true; | ||
this.emit('end'); | ||
return this | ||
} | ||
this.emit('userActivity', packet); | ||
return bluebird_1.default.try(() => { | ||
switch (packet.command) { | ||
case Packet.A_SYNC: | ||
return this._handleSyncPacket(packet); | ||
case Packet.A_CNXN: | ||
return this._handleConnectionPacket(packet); | ||
case Packet.A_OPEN: | ||
return this._handleOpenPacket(packet); | ||
case Packet.A_OKAY: | ||
case Packet.A_WRTE: | ||
case Packet.A_CLSE: | ||
return this._forwardServicePacket(packet); | ||
case Packet.A_AUTH: | ||
return this._handleAuthPacket(packet); | ||
default: | ||
throw new Error(`Unknown command ${packet.command}`); | ||
_error(err) { | ||
this.emit('error', err); | ||
return this.end() | ||
} | ||
_handle(packet) { | ||
if (this.ended) { | ||
return | ||
} | ||
}).catch(Socket.AuthError, () => { | ||
return this.end(); | ||
}).catch(Socket.UnauthorizedError, () => { | ||
return this.end(); | ||
}).catch(err => { | ||
return this._error(err); | ||
}); | ||
} | ||
_handleSyncPacket(packet) { | ||
debug('I:A_SYNC'); | ||
debug('O:A_SYNC'); | ||
return this.write(Packet.assemble(Packet.A_SYNC, 1, this.syncToken.next(), null)); | ||
} | ||
_handleConnectionPacket(packet) { | ||
debug('I:A_CNXN', packet); | ||
const version = Packet.swap32(packet.arg0); | ||
this.maxPayload = Math.min(this.UINT16_MAX, packet.arg1); | ||
return this._createToken() | ||
.then(token => { | ||
this.token = token; | ||
debug(`Created challenge '${this.token.toString('base64')}'`); | ||
debug('O:A_AUTH'); | ||
return this.write(Packet.assemble(Packet.A_AUTH, this.AUTH_TOKEN, 0, this.token)); | ||
}); | ||
} | ||
_handleAuthPacket(packet) { | ||
debug('I:A_AUTH', packet); | ||
switch (packet.arg0) { | ||
case this.AUTH_SIGNATURE: | ||
debug(`Received signature '${packet.data.toString('base64')}'`); | ||
if (!this.signature) { | ||
this.signature = packet.data; | ||
this.emit('userActivity', packet); | ||
return Promise.try(() => { | ||
switch (packet.command) { | ||
case Packet.A_SYNC: | ||
return this._handleSyncPacket(packet); | ||
case Packet.A_CNXN: | ||
return this._handleConnectionPacket(packet); | ||
case Packet.A_OPEN: | ||
return this._handleOpenPacket(packet); | ||
case Packet.A_OKAY: | ||
case Packet.A_WRTE: | ||
case Packet.A_CLSE: | ||
return this._forwardServicePacket(packet); | ||
case Packet.A_AUTH: | ||
return this._handleAuthPacket(packet); | ||
default: | ||
throw new Error(`Unknown command ${packet.command}`) | ||
} | ||
debug('O:A_AUTH'); | ||
return this.write(Packet.assemble(Packet.A_AUTH, this.AUTH_TOKEN, 0, this.token)); | ||
case this.AUTH_RSAPUBLICKEY: | ||
if (!this.signature) { | ||
throw new Socket.AuthError('Public key sent before signature'); | ||
} | ||
if (!packet.data || !(packet.data.length >= 2)) { | ||
throw new Socket.AuthError('Empty RSA public key'); | ||
} | ||
debug(`Received RSA public key '${packet.data.toString('base64')}'`); | ||
return Auth.parsePublicKey(this._skipNull(packet.data)) | ||
.then(key => { | ||
const digest = this.token.toString('binary'); | ||
const sig = this.signature.toString('binary'); | ||
if (!key.verify(digest, sig)) { | ||
debug('Signature mismatch'); | ||
throw new Socket.AuthError('Signature mismatch'); | ||
}).catch(Socket.AuthError, () => { | ||
return this.end() | ||
}).catch(Socket.UnauthorizedError, () => { | ||
return this.end() | ||
}).catch(err => { | ||
return this._error(err) | ||
}) | ||
} | ||
_handleSyncPacket(packet) { | ||
// No need to do anything? | ||
debug('I:A_SYNC'); | ||
debug('O:A_SYNC'); | ||
return this.write(Packet.assemble(Packet.A_SYNC, 1, this.syncToken.next(), null)) | ||
} | ||
_handleConnectionPacket(packet) { | ||
debug('I:A_CNXN', packet); | ||
const version = Packet.swap32(packet.arg0); | ||
this.maxPayload = Math.min(UINT16_MAX, packet.arg1); | ||
return this._createToken() | ||
.then(token => { | ||
this.token = token; | ||
debug(`Created challenge '${this.token.toString('base64')}'`); | ||
debug('O:A_AUTH'); | ||
return this.write(Packet.assemble(Packet.A_AUTH, AUTH_TOKEN, 0, this.token)) | ||
}) | ||
} | ||
_handleAuthPacket(packet) { | ||
debug('I:A_AUTH', packet); | ||
switch (packet.arg0) { | ||
case AUTH_SIGNATURE: | ||
// Store first signature, ignore the rest | ||
debug(`Received signature '${packet.data.toString('base64')}'`); | ||
if (!this.signature) { | ||
this.signature = packet.data | ||
} | ||
debug('Signature verified'); | ||
return key; | ||
}).then(key => { | ||
return this.options.auth(key) | ||
.catch(function (err) { | ||
debug('Connection rejected by user-defined auth handler'); | ||
throw new Socket.AuthError('Rejected by user-defined handler'); | ||
}); | ||
}).then(() => { | ||
return this._deviceId(); | ||
}).then(id => { | ||
this.authorized = true; | ||
debug('O:A_CNXN'); | ||
return this.write(Packet.assemble(Packet.A_CNXN, Packet.swap32(this.version), this.maxPayload, id)); | ||
}); | ||
default: | ||
throw new Error(`Unknown authentication method ${packet.arg0}`); | ||
debug('O:A_AUTH'); | ||
return this.write(Packet.assemble(Packet.A_AUTH, AUTH_TOKEN, 0, this.token)); | ||
case AUTH_RSAPUBLICKEY: | ||
if (!this.signature) { | ||
throw new Socket.AuthError('Public key sent before signature') | ||
} | ||
if (!packet.data || !(packet.data.length >= 2)) { | ||
throw new Socket.AuthError('Empty RSA public key') | ||
} | ||
debug(`Received RSA public key '${packet.data.toString('base64')}'`); | ||
return Auth.parsePublicKey(this._skipNull(packet.data)) | ||
.then(key => { | ||
const digest = this.token.toString('binary'); | ||
const sig = this.signature.toString('binary'); | ||
if (!key.verify(digest, sig)) { | ||
debug('Signature mismatch'); | ||
throw new Socket.AuthError('Signature mismatch') | ||
} | ||
debug('Signature verified'); | ||
return key | ||
}).then(key => { | ||
return this.options.auth(key) | ||
.catch(function (err) { | ||
debug('Connection rejected by user-defined auth handler'); | ||
throw new Socket.AuthError('Rejected by user-defined handler') | ||
}) | ||
}).then(() => { | ||
return this._deviceId() | ||
}).then(id => { | ||
this.authorized = true; | ||
debug('O:A_CNXN'); | ||
return this.write(Packet.assemble(Packet.A_CNXN, | ||
Packet.swap32(this.version), this.maxPayload, id) | ||
) | ||
}); | ||
default: | ||
throw new Error(`Unknown authentication method ${packet.arg0}`) | ||
} | ||
} | ||
} | ||
_handleOpenPacket(packet) { | ||
if (!this.authorized) { | ||
throw new Socket.UnauthorizedError(); | ||
_handleOpenPacket(packet) { | ||
if (!this.authorized) { | ||
throw new Socket.UnauthorizedError() | ||
} | ||
const remoteId = packet.arg0; | ||
const localId = this.remoteId.next(); | ||
if (!packet.data || !(packet.data.length >= 2)) { | ||
throw new Error('Empty service name') | ||
} | ||
const name = this._skipNull(packet.data); | ||
debug(`Calling ${name}`); | ||
const service = new Service(this.client, this.serial, localId, remoteId, this); | ||
return new Promise((resolve, reject) => { | ||
service.on('error', reject); | ||
service.on('end', resolve); | ||
this.services.insert(localId, service); | ||
debug(`Handling ${this.services.count} services simultaneously`); | ||
return service.handle(packet) | ||
}).catch(err => true).finally(() => { | ||
this.services.remove(localId); | ||
debug(`Handling ${this.services.count} services simultaneously`); | ||
return service.end() | ||
}) | ||
} | ||
const remoteId = packet.arg0; | ||
const localId = this.remoteId.next(); | ||
if (!packet.data || !(packet.data.length >= 2)) { | ||
throw new Error('Empty service name'); | ||
_forwardServicePacket(packet) { | ||
let service; | ||
if (!this.authorized) { | ||
throw new Socket.UnauthorizedError() | ||
} | ||
const remoteId = packet.arg0; | ||
const localId = packet.arg1; | ||
if ((service = this.services.get(localId))) { | ||
return service.handle(packet) | ||
} else { | ||
return debug('Received a packet to a service that may have been closed already') | ||
} | ||
} | ||
const name = this._skipNull(packet.data); | ||
debug(`Calling ${name}`); | ||
const service = new Service(this.client, this.serial, localId, remoteId, this); | ||
return new bluebird_1.default((resolve, reject) => { | ||
service.on('error', reject); | ||
service.on('end', resolve); | ||
this.services.insert(localId, service); | ||
debug(`Handling ${this.services.count} services simultaneously`); | ||
return service.handle(packet); | ||
}).catch(err => true).finally(() => { | ||
this.services.remove(localId); | ||
debug(`Handling ${this.services.count} services simultaneously`); | ||
return service.end(); | ||
}); | ||
} | ||
_forwardServicePacket(packet) { | ||
let service; | ||
if (!this.authorized) { | ||
throw new Socket.UnauthorizedError(); | ||
write(chunk) { | ||
if (this.ended) { | ||
return | ||
} | ||
return this.socket.write(chunk) | ||
} | ||
const remoteId = packet.arg0; | ||
const localId = packet.arg1; | ||
if ((service = this.services.get(localId))) { | ||
return service.handle(packet); | ||
_createToken() { | ||
return Promise.promisify(crypto.randomBytes)(TOKEN_LENGTH) | ||
} | ||
else { | ||
return debug('Received a packet to a service that may have been closed already'); | ||
_skipNull(data) { | ||
return data.slice(0, -1) // Discard null byte at end | ||
} | ||
} | ||
write(chunk) { | ||
if (this.ended) { | ||
return; | ||
_deviceId() { | ||
debug('Loading device properties to form a standard device ID'); | ||
return this.client.getProperties(this.serial) | ||
.then(function (properties) { | ||
const id = (([ | ||
'ro.product.name', | ||
'ro.product.model', | ||
'ro.product.device' | ||
]).map((prop) => `${prop}=${properties[prop]};`)).join(''); | ||
return Buffer.from(`device::${id}\0`) | ||
}) | ||
} | ||
return this.socket.write(chunk); | ||
} | ||
_createToken() { | ||
return bluebird_1.default.promisify(crypto.randomBytes)(this.TOKEN_LENGTH); | ||
} | ||
_skipNull(data) { | ||
return data.slice(0, -1); | ||
} | ||
_deviceId() { | ||
debug('Loading device properties to form a standard device ID'); | ||
return this.client.getProperties(this.serial) | ||
.then(function (properties) { | ||
const id = (([ | ||
'ro.product.name', | ||
'ro.product.model', | ||
'ro.product.device' | ||
]).map((prop) => `${prop}=${properties[prop]};`)).join(''); | ||
return Buffer.from(`device::${id}\0`); | ||
}); | ||
} | ||
} | ||
}; | ||
Socket.initClass(); | ||
return Socket | ||
})(); | ||
Socket.AuthError = class AuthError extends Error { | ||
constructor(message) { | ||
super(); | ||
super(); // TODO check sanity | ||
Error.call(this); | ||
this.name = 'AuthError'; | ||
this.message = message; | ||
Error.captureStackTrace(this, Socket.AuthError); | ||
Error.captureStackTrace(this, Socket.AuthError) | ||
} | ||
}; | ||
Socket.UnauthorizedError = class UnauthorizedError extends Error { | ||
constructor() { | ||
super(); | ||
super(); // TODO check sanity | ||
Error.call(this); | ||
this.name = 'UnauthorizedError'; | ||
this.message = 'Unauthorized access'; | ||
Error.captureStackTrace(this, Socket.UnauthorizedError); | ||
Error.captureStackTrace(this, Socket.UnauthorizedError) | ||
} | ||
}; | ||
exports.Socket = Socket; | ||
//# sourceMappingURL=socket.js.map | ||
module.exports = Socket; |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Network access
Supply chain riskThis module accesses the network.
Found 1 instance in 1 package
Shell access
Supply chain riskThis module accesses the system shell. Accessing the system shell increases the risk of executing arbitrary code.
Found 1 instance in 1 package
225518
93
4748