diff --git a/lib/conf/package.js b/lib/conf/package.js index 403fc2b30..6e3856315 100644 --- a/lib/conf/package.js +++ b/lib/conf/package.js @@ -1,18 +1,17 @@ -var fs = require('fs'), - path = require('path'), - needle = require('needle'), - system = require('./../system'), - rmdir = require('./../utils/rmdir'), - os_name = system.os_name; - -var releases_host = 'http://s3.amazonaws.com', +var fs = require('fs'), + path = require('path'), + needle = require('needle'), + createHash = require('crypto').createHash, + system = require('./../system'), + rmdir = require('./../utils/rmdir'), + os_name = system.os_name; + +var releases_host = 'https://s3.amazonaws.com', releases_url = releases_host + '/prey-releases/node-client/', latest_text = 'latest.txt', - latest_checksums = 'latest/shasums.txt', + checksums = 'shasums.json', package_format = '.zip'; -// TODO: validate checksums - var log = function(str) { if (process.stdout.writable) process.stdout.write(str + '\n'); @@ -25,6 +24,43 @@ var is_greater_than = function(first, second){ return a > b ? true : false; }; +// returns sha1 checksum for file +var checksum_for = function(file, cb) { + var error, + hash = createHash('sha1'), + stream = fs.ReadStream(file); + + stream.on('error', function(e) { + if (!error) cb(e); + error = e; + }) + + stream.on('data', function(d) { + hash.update(d); + }); + + stream.on('end', function() { + if (!error) cb(null, hash.digest('hex')); + }); +} + +var verify_checksum = function(url, filename, file, cb) { + log('Fetching checksum: ' + url); + needle.get(url, { parse: true }, function(err, resp) { + if (err) return cb(err); + + var checksum = resp.body[filename]; + if (!checksum) + return cb(new Error('Unable to retrieve checksum for ' + filename)); + + log('Got checksum from remote: ' + checksum + '. Calculating file hash...'); + checksum_for(file, function(err, res) { + var valid = (res && res.trim() == checksum.trim()); + cb(err, valid); + }) + }) +} + var package = module.exports; package.new_version_available = function(current, cb) { @@ -39,46 +75,33 @@ package.new_version_available = function(current, cb) { package.get_upstream_version = function(cb){ // log('Checking latest version...'); - var done = function(ver) { - // log('Latest upstream version: ' + ver); - cb(null, ver); - } - - needle.get(releases_url + latest_text, function(err, resp, body){ - if (!err && body != '') - return done(body.toString().trim()); - - needle.get(releases_url + latest_checksums, function(err, resp, body){ - if (err) return cb(err); - - var match = body.toString().match(/prey-([\d\.]+)\.zip/); - if (!match) - return cb(new Error('Unable to determine latest version from ' + latest_checksums)) + needle.get(releases_url + latest_text, function(err, resp, body) { + var ver = body && body.toString().trim(); + log('Latest upstream version: ' + ver); - done(match[1]); - }); + cb(err, ver); }); - } package.get_latest = function(current_version, dest, cb){ - package.new_version_available(current_version, function(err, version){ + package.new_version_available(current_version, function(err, version) { if (err || !version) return cb(err || new Error('Already running latest version.')); if (fs.existsSync(path.join(dest, version))) return cb(new Error('Version ' + version + ' already installed.')); - package.get_version(version, dest, function(err){ + package.get_version(version, dest, function(err) { cb(err, version); }); }); }; -package.get_version = function(version, dest, cb){ +package.get_version = function(version, dest, cb) { log('Fetching version ' + version); - package.download_release(version, function(err, file){ + package.download_release(version, function(err, file) { if (err) return cb(err); + package.install(file, dest, function(err, installed_version) { fs.unlink(file, function() { cb(err, installed_version); @@ -87,23 +110,37 @@ package.get_version = function(version, dest, cb){ }); } -package.download_release = function(version, cb){ - var arch = process.arch == 'x64' ? 'x64' : 'x86'; - var release = ['prey', os_name, version, arch].join('-') + package_format; - var host_path = releases_url + version + '/'; - package.download(host_path + release, cb); +package.download_release = function(version, cb) { + + var arch = process.arch == 'x64' ? 'x64' : 'x86', + release = ['prey', os_name, version, arch].join('-') + package_format, + host_path = releases_url + version + '/'; + + package.download(host_path + release, function(err, file) { + + verify_checksum(host_path + checksums, release, file, function(err, valid) { + if (err || !valid) + return cb && cb(err || new Error('Invalid checksum for file: ' + release)); + + log('File checksum is valid! ' + file) + return cb && cb(null, file); + }) + }); } -package.download = function(url, cb){ +package.download = function(url, cb) { log('Downloading package: ' + url); var file = system.tempfile_path(path.basename(url)); - needle.get(url, { output: file }, function(err, resp, data){ + return cb(null, file); + + needle.get(url, { output: file }, function(err, resp, data) { + if (err || resp.statusCode != 200) return cb && cb(err || new Error('Unexpected response: \n\n' + data.toString())); fs.exists(file, function(exists) { - if (!exists) return cb(new Error('File not found!')); + if (!exists) return cb && cb(new Error('File not found!')); log('Got file: ' + file) return cb && cb(null, file);