UNPKG

6.96 kBJavaScriptView Raw
1var fs = require('fs')
2 , loadWallet = require('../utils/loadWallet')
3 , loadRecipients = require('../utils/loadRecipients')
4 , printHeader = require('../utils/printHeader')
5 , decrypt = require('../lib/decrypt')
6 , Progress = require('progress')
7 , translateHeader = require('../utils/translateHeader')
8 , through = require('through')
9 , pempal = require('pempal')
10 , request = require('micro-request')
11 , assert = require('assert')
12 , fetchGist = require('../utils/fetchGist')
13 , tar = require('tar')
14 , zlib = require('zlib')
15 , path = require('path')
16 , tmpDir = require('os').tmpDir()
17 , crypto = require('crypto')
18 , rimraf = require('rimraf')
19
20module.exports = function (inFile, outFile, options) {
21 if (inFile.match(/\.pem$/) || options.gist) {
22 options.armor = true
23 }
24 if (inFile.indexOf('https://gist') === 0) options.gist = true
25 loadWallet(options.parent.wallet, function (err, wallet) {
26 if (err) throw err
27 if (options.gist) {
28 options.armor = true
29 fetchGist(inFile, function (err, str, gist) {
30 if (err) throw err
31 var inStream = through()
32 withInstream(inStream, gist)
33 setImmediate(function () {
34 inStream.end(Buffer(str))
35 })
36 })
37 }
38 else {
39 var inStat = fs.statSync(inFile)
40 var inStream = fs.createReadStream(inFile)
41 withInstream(inStream)
42 }
43 function withInstream (inStream, gist) {
44 var outStream
45 var decryptor, header, outChunks = []
46 function withOutfile () {
47 if (options.gist && !outFile) {
48 outFile = gist.id
49 }
50 if (!outFile) {
51 outFile = inFile.replace(/\.(salty|pem)$/, '')
52 }
53 try {
54 fs.statSync(outFile)
55 if (!options.parent.force) {
56 throw new Error('Refusing to overwrite ' + outFile + '. Use --force to ignore this.')
57 }
58 rimraf.sync(outFile)
59 }
60 catch (err) {
61 if (err && err.code !== 'ENOENT') {
62 throw err
63 }
64 }
65 process.on('uncaughtException', function (err) {
66 console.error('uncaught', err.stack)
67 try {
68 rimraf.sync(outFile)
69 }
70 catch (e) {}
71 throw err
72 })
73 }
74 if (options.armor) {
75 var decodeChunks = [], totalSize
76 var decodeStream = through(function write (buf) {
77 decodeChunks.push(buf)
78 }, function end () {
79 var str = Buffer.concat(decodeChunks).toString('utf8')
80 try {
81 var pem = pempal.decode(str, {tag: 'SALTY MESSAGE'})
82 }
83 catch (e) {
84 throw new Error('invalid PEM')
85 }
86 var tmpStream = through()
87 decryptor = decrypt(tmpStream, wallet, pem.body.length)
88 withDecryptor(decryptor)
89 tmpStream.end(pem.body)
90 })
91 if (options.gist || outFile) {
92 withOutfile()
93 outStream = fs.createWriteStream(outFile, {mode: parseInt('0600', 8)})
94 }
95 else {
96 outStream = through(function write (buf) {
97 outChunks.push(buf)
98 }, function end () {
99 this.on('end', function () {
100 this.emit('finish')
101 })
102 this.queue(null)
103 })
104 }
105 inStream.pipe(decodeStream)
106 }
107 else {
108 withOutfile()
109 outStream = fs.createWriteStream(outFile, {mode: parseInt('0600', 8)})
110 decryptor = decrypt(inStream, wallet, inStat.size)
111 withDecryptor(decryptor)
112 var bar = new Progress(' decrypting [:bar] :percent ETA: :etas', { total: inStat.size, width: 80 })
113 var chunkCounter = 0
114 var tickCounter = 0
115 decryptor.on('data', function (chunk) {
116 tickCounter += chunk.length
117 chunkCounter++
118 if (chunkCounter % 100 === 0) {
119 bar.tick(tickCounter)
120 tickCounter = 0
121 }
122 })
123 }
124 function withDecryptor (decryptor) {
125 decryptor.once('header', function (h) {
126 if (options.sig && !h['signature']) {
127 throw new Error('no signature')
128 }
129 header = translateHeader(h, wallet.recipients)
130 if (h['content-encoding'] === 'x-gzip' && h['content-type'] === 'application/x-tar') {
131 var tmpPath = '.' + crypto.randomBytes(16).toString('hex')
132 var extractStream = tar.Extract({path: tmpPath, mode: parseInt('0700', 8)})
133 function onExit () {
134 try {
135 rimraf.sync(tmpPath)
136 rimraf.sync(outFile)
137 }
138 catch (e) {}
139 process.exit(1)
140 }
141 process.once('SIGINT', onExit)
142 process.once('SIGTERM', onExit)
143 var gunzipStream = zlib.createGunzip()
144 extractStream.once('end', function () {
145 rimraf.sync(outFile)
146 fs.renameSync(tmpPath, outFile)
147 printHeader(header)
148 console.log('Restored to', outFile)
149 })
150 gunzipStream.pipe(extractStream)
151 var readStream
152 if (!outFile) {
153 readStream = through()
154 setImmediate(function () {
155 readStream.end(Buffer.concat(outChunks))
156 })
157 withOutfile()
158 }
159 else {
160 var outStat = fs.statSync(outFile)
161 var bar = new Progress(' unpacking [:bar] :percent ETA: :etas', { total: outStat.size, width: 80 })
162 readStream = fs.createReadStream(outFile)
163 var chunkCounter = 0
164 var tickCounter = 0
165 readStream.on('data', function (chunk) {
166 tickCounter += chunk.length
167 chunkCounter++
168 if (chunkCounter % 100 === 0) {
169 bar.tick(tickCounter)
170 tickCounter = 0
171 }
172 })
173 }
174 readStream.pipe(gunzipStream)
175 }
176 else {
177 if (!options.armor) {
178 if (bar) bar.terminate()
179 console.error()
180 printHeader(header)
181 console.log('Decrypted to', outFile)
182 }
183 else {
184 printHeader(header)
185 if (outFile) {
186 var readStream = fs.createReadStream(outFile)
187 readStream.once('end', function () {
188 rimraf.sync(outFile)
189 })
190 readStream.pipe(process.stdout)
191 }
192 else {
193 process.stdout.write(Buffer.concat(outChunks))
194 }
195 }
196 }
197 })
198 outStream.once('finish', function () {
199 if (options.delete && inFile) {
200 try {
201 rimraf.sync(inFile)
202 }
203 catch (e) {}
204 }
205 })
206 decryptor.pipe(outStream)
207 }
208 inStream.resume()
209 }
210 })
211}
\No newline at end of file