diff --git a/lib/units/device/index.js b/lib/units/device/index.js index fa2a3a29..62ef8de8 100644 --- a/lib/units/device/index.js +++ b/lib/units/device/index.js @@ -38,6 +38,7 @@ module.exports = function(options) { .dependency(require('./plugins/ringer')) .dependency(require('./plugins/wifi')) .dependency(require('./plugins/sd')) + .dependency(require('./plugins/filesystem')) .define(function(options, heartbeat, solo) { if (process.send) { // Only if we have a parent process diff --git a/lib/units/device/plugins/filesystem.js b/lib/units/device/plugins/filesystem.js new file mode 100644 index 00000000..fc2fb990 --- /dev/null +++ b/lib/units/device/plugins/filesystem.js @@ -0,0 +1,132 @@ +var util = require('util') + +var syrup = require('stf-syrup') +var adbkit = require('adbkit') +var path = require('path') + +var logger = require('../../../util/logger') +var wire = require('../../../wire') +var wireutil = require('../../../wire/util') + +module.exports = syrup.serial() + .dependency(require('../support/adb')) + .dependency(require('../support/router')) + .dependency(require('../support/push')) + .dependency(require('../support/storage')) + .dependency(require('../resources/minicap')) + .dependency(require('./util/display')) + .define(function(options, adb, router, push, storage, minicap, display) { + var log = logger.createLogger('device:plugins:filesystem') + var plugin = Object.create(null) + + plugin.retrive = function(file) { + log.info('Retriving file %s', file) + + return adb.pull(options.serial, file) + .then(adbkit.util.readAll) + .then(function(){ + return adb.stat(options.serial, file) + }) + .then(function(stats){ + if (stats.size == 0){ + log.info(util.format("File %s is empty", file)) + } + + return adb.pull(options.serial, file) + .then(function(transfer){ + // if this is a new store type, somethings need add to units/storage/plugins/ + return storage.store('blob', transfer, { + filename: path.basename(file), + contentType: 'text/plain', // FIXME(ssx): need to detect file type + knownLength: stats.size + }) + }) + }) + .finally(function(){ + log.info(util.format("file %s transfer finished", file)) + }) + } + + router.on(wire.FileSystemGetMessage, function(channel, message) { + var reply = wireutil.reply(options.serial) + plugin.retrive(message.file) + .then(function(file){ + push.send([ + channel, + reply.okay('success', file) + ]) + }) + .catch(function(err){ + log.error('File retrive %s failed\n%s', message.file, err.stack) + push.send([ + channel, + reply.fail(err.message) + ]) + }) + }) + + return plugin; + + + + // function projectionFormat() { + // return util.format( + // '%dx%d@%dx%d/%d' + // , display.properties.width + // , display.properties.height + // , display.properties.width + // , display.properties.height + // , display.properties.rotation + // ) + // } + + // plugin.capture = function() { + // log.info('Capturing screenshot') + + // var file = util.format('/data/local/tmp/minicap_%d.jpg', Date.now()) + // return minicap.run(util.format( + // '-P %s -s >%s', projectionFormat(), file)) + // .then(adbkit.util.readAll) + // .then(function() { + // return adb.stat(options.serial, file) + // }) + // .then(function(stats) { + // if (stats.size === 0) { + // throw new Error('Empty screenshot; possibly secure screen?') + // } + + // return adb.pull(options.serial, file) + // .then(function(transfer) { + // return storage.store('image', transfer, { + // filename: util.format('%s.jpg', options.serial) + // , contentType: 'image/jpeg' + // , knownLength: stats.size + // }) + // }) + // }) + // .finally(function() { + // return adb.shell(options.serial, ['rm', '-f', file]) + // .then(adbkit.util.readAll) + // }) + // } + + // router.on(wire.ScreenCaptureMessage, function(channel) { + // var reply = wireutil.reply(options.serial) + // plugin.capture() + // .then(function(file) { + // push.send([ + // channel + // , reply.okay('success', file) + // ]) + // }) + // .catch(function(err) { + // log.error('Screen capture failed', err.stack) + // push.send([ + // channel + // , reply.fail(err.message) + // ]) + // }) + // }) + + // return plugin + }) diff --git a/lib/units/storage/temp.js b/lib/units/storage/temp.js index 363ced16..58345afc 100644 --- a/lib/units/storage/temp.js +++ b/lib/units/storage/temp.js @@ -138,6 +138,10 @@ module.exports = function(options) { app.get('/s/blob/:id/:name', function(req, res) { var file = storage.retrieve(req.params.id) if (file) { + if (typeof req.query.download !== 'undefined') { + res.set('Content-Disposition', + 'attachment; filename="' + path.basename(file.name) + '"') + } res.set('Content-Type', file.type) res.sendFile(file.path) } diff --git a/lib/units/websocket/index.js b/lib/units/websocket/index.js index aa6a1955..cd6b980e 100644 --- a/lib/units/websocket/index.js +++ b/lib/units/websocket/index.js @@ -826,6 +826,16 @@ module.exports = function(options) { ) ]) }) + .on('fs.retrive', function(channel, responseChannel, data) { + joinChannel(responseChannel) + push.send([ + channel + , wireutil.transaction( + responseChannel + , new wire.FileSystemGetMessage(data) + ) + ]) + }) }) .finally(function() { // Clean up all listeners and subscriptions diff --git a/lib/wire/wire.proto b/lib/wire/wire.proto index e3e50b9b..2af27426 100644 --- a/lib/wire/wire.proto +++ b/lib/wire/wire.proto @@ -72,6 +72,27 @@ enum MessageType { AccountRemoveMessage = 55; SdStatusMessage = 61; ReverseForwardsEvent = 72; + FileSystemListEvent = 80; + FileSystemListMessage = 81; + FileSystemGetMessage = 82; +} + +message FileSystemListEvent { + required string serial = 1; + required string dir = 2; +} +message FileSystemListMessage { + required string dir = 1; + repeated FileSystemProperty files = 2; +} + +message FileSystemProperty { + required string name = 1; + required string mod = 2; +} + +message FileSystemGetMessage { + required string file = 1; } message Envelope { diff --git a/res/app/components/stf/control/control-service.js b/res/app/components/stf/control/control-service.js index b004941e..0a7642a3 100644 --- a/res/app/components/stf/control/control-service.js +++ b/res/app/components/stf/control/control-service.js @@ -225,6 +225,12 @@ module.exports = function ControlServiceFactory( return sendTwoWay('screen.capture') } + this.fsretrive = function(file){ + return sendTwoWay('fs.retrive', { + file: file, + }) + } + this.checkAccount = function(type, account) { return sendTwoWay('account.check', { type: type