UNPKG

domotz-remote-pawn

Version:

Domotz Agent

213 lines (184 loc) 7.21 kB
/** This file is part of Domotz Agent. * Copyright (C) 2020 Domotz Ltd * * Domotz Agent is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Domotz Agent is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Domotz Agent. If not, see <http://www.gnu.org/licenses/>. * * Created by Alessandro Pagiaro <apagiaro@domotz.com> on 10/02/19. * */ // To add options check ssh manual. We assume to use standard openssh client. const OPTIONS_MAP = { cipher: '-c', ssh_config: '-F', limit: '-l', pubkey_accepted_key_types: '-oPubkeyAcceptedKeyTypes=', host_key_algorithms: '-oHostKeyAlgorithms=', kex_algorithm: '-oKexAlgorithms=', hmac: '-oMACs=', port: '-P', program: '-S', recursive: '-r', verbose: '-v', }; const SCP_BINARY = 'scp'; const READ = 'read'; const WRITE = 'write'; const LINUX_SSHPASS_BINARY = 'domotz_sshpass'; const WINDOWS_SCP_BINARY = 'domotz_pscp.exe'; module.exports.factory = function (fileUtils, childProcess, q, platform, log) { var myConsole = log.decorateLogs(); var sshInfo = platform.getSSHInfo(); var sshCommon = require('./sshCommon'); /** * SCP is an external tool. It must be linked with system libraries! */ function _getCleanEnv() { var cleanedEnv = Object.create(process.env); cleanedEnv.LD_LIBRARY_PATH = ''; return cleanedEnv; } function getArgsForAuthentication(password) { var args = []; args.push(LINUX_SSHPASS_BINARY); args.push('-p'); args.push(password); args.push(SCP_BINARY); var currentPlatform = platform.getFullPlatform(); if (currentPlatform && currentPlatform.indexOf('ubuntu_core') > -1) { args.push('-S'); args.push(process.env.SNAP + '/usr/bin/ssh'); } return args; } function _attachEventOnOutputs(scpProcess) { var result = {}; scpProcess.stderr.on('data', function (data) { myConsole.warn('ERR DATA ' + data); result.stdErr += data; }); scpProcess.stdout.on('data', function (data) { myConsole.verbose('STDOUT DATA ' + data); result.stdOut += data; }); scpProcess.on('error', function (err) { myConsole.warn('scp error', JSON.stringify(err)); }); return result; } function _performRead(temporaryOutputFile, scp, args, options) { var deferred = q.defer(); var scpProcess = childProcess.spawn(scp, args, options); myConsole.info('Spawning process %s %s', scp, args.join(' ')); var processOutputs = _attachEventOnOutputs(scpProcess); scpProcess.on('close', function (code) { myConsole.info('scp process ended'); return fileUtils .readFile(temporaryOutputFile, null, true) .then(function (fileRead) { deferred.resolve({ content: Buffer.from(fileRead).toString('base64'), stdout: processOutputs.stdOut, stderr: processOutputs.stdErr, return_code: code, }); }) .catch(function () { deferred.resolve({ content: null, stdout: processOutputs.stdOut, stderr: processOutputs.stdErr, return_code: code, }); }) .finally(function () { fileUtils.deleteFile(temporaryOutputFile); scpProcess = null; }); }); return deferred.promise; } function _performWrite(temporaryOutputFile, content, scp, args, options) { var buff = new Buffer(content, 'base64'); return fileUtils.writeFile(temporaryOutputFile, buff, true).then(function () { var deferred = q.defer(); var scpProcess = childProcess.spawn(scp, args, options); myConsole.info('Spawning process %s %s', scp, args.join(' ')); var processOutputs = _attachEventOnOutputs(scpProcess); scpProcess.on('close', function (code) { myConsole.info('Scp process ended'); scpProcess = null; fileUtils.deleteFile(temporaryOutputFile); deferred.resolve({ stdout: processOutputs.stdOut, stderr: processOutputs.stdErr, return_code: code, }); }); return deferred.promise; }); } function _generateFileName() { var os = require('os'); var path = require('path'); return path.join(os.tmpdir(), 'scp_file') + Math.floor(Date.now() / 1000); } function getArgs(password, options) { var args; if (platform.isWindows()) { args = []; args.push(WINDOWS_SCP_BINARY); args.push('-pw'); args.push(password); args.push('-batch'); args.push('-scp'); args.push('-q'); args.push('-P'); args.push(options.port || '22'); } else { args = getArgsForAuthentication(password); args = args.concat(sshCommon.getArgsFromOptions(options, OPTIONS_MAP, sshInfo, myConsole)); args = args.concat(['-oStrictHostKeyChecking=no', '-oUserKnownHostsFile=/dev/null']); } return args; } function exec(operation, user, host, filePath, password, options, content) { var args = getArgs(password, options); var op = operation.toLowerCase(); var OUTPUT_FILE = command._generateFileName(); myConsole.verbose('scp temp file: ' + OUTPUT_FILE); var baseExecutable = args.shift(); options.env = command._getCleanEnv(); if (op === READ) { args.push(user + '@' + host + ':' + filePath); args.push(OUTPUT_FILE); return command._performRead(OUTPUT_FILE, baseExecutable, args, options); } else if (op === WRITE) { args.push(OUTPUT_FILE); args.push(user + '@' + host + ':' + filePath); return command._performWrite(OUTPUT_FILE, content, baseExecutable, args, options); } else { throw new Error('SCP operation not allowed. ' + operation); } } var command = { _performRead: _performRead, _performWrite: _performWrite, _generateFileName: _generateFileName, _getCleanEnv: _getCleanEnv, exec: exec, READ: READ, WRITE: WRITE, }; return command; };