UNPKG

7.04 kBJavaScriptView Raw
1var fs = require('fs')
2 , loadRecipients = require('../utils/loadRecipients')
3 , crypto = require('crypto')
4 , constants = require('../lib/constants')
5 , makeNonce = require('../utils/makeNonce')
6 , encrypt = require('../lib/encrypt')
7 , printHeader = require('../utils/printHeader')
8 , translateHeader = require('../utils/translateHeader')
9 , Progress = require('progress')
10 , loadWallet = require('../utils/loadWallet')
11 , pempal = require('pempal')
12 , through = require('through')
13 , prompt = require('cli-prompt')
14 , createGist = require('../utils/createGist')
15 , tar = require('tar')
16 , zlib = require('zlib')
17 , fstream = require('fstream')
18 , tmpDir = require('os').tmpDir()
19 , path = require('path')
20 , minimist = require('minimist')
21 , headersFromArgs = require('../utils/headersFromArgs')
22
23module.exports = function (inFile, outFile, options) {
24 options.headers = headersFromArgs()
25 var walletDir = options.parent.wallet
26 if (options.message || options.gist) options.armor = true
27 if (!options.armor) {
28 var stat = fs.statSync(inFile)
29 if (!outFile) {
30 outFile = crypto.randomBytes(4).toString('hex') + '.salty'
31 }
32 try {
33 fs.statSync(outFile)
34 if (!options.parent.force) {
35 throw new Error('Refusing to overwrite ' + outFile + '. Use --force to ignore this.')
36 }
37 }
38 catch (err) {
39 if (err && err.code !== 'ENOENT') {
40 throw err
41 }
42 }
43 }
44 var nonce = makeNonce()
45 loadRecipients(walletDir, function (err, recipients) {
46 if (err) throw err
47 var recipient = options.to ? recipients[options.to] : recipients.self
48 if (!recipient) {
49 throw new Error('Recipient not found')
50 }
51 if (options.sign) {
52 loadWallet(walletDir, function (err, wallet) {
53 if (err) throw err
54 withWallet(wallet)
55 })
56 }
57 else withWallet()
58 function withWallet (wallet) {
59 if (options.message) {
60 process.stderr.write('Compose message: (CTL-D when done)\n\n> ')
61 var lines = []
62 process.stdin.once('end', function () {
63 lines.push('')
64 var m = Buffer(lines.join('\n'))
65 withMessage(m)
66 })
67 ;(function getLine () {
68 prompt(null, function (line) {
69 lines.push(line)
70 getLine()
71 })
72 })()
73 function withMessage (m) {
74 var mStream = through()
75 var encryptor = encrypt(mStream, recipient, nonce, m.length, wallet, true, options.headers)
76 withEncryptor(encryptor, m.length)
77 mStream.end(m)
78 }
79 }
80 else {
81 var inStat = fs.statSync(inFile)
82 var inStream, encryptor
83 if (inStat.isDirectory()) {
84 var tarStream = tar.Pack({fromBase: true})
85 gzipStream = tarStream.pipe(zlib.createGzip())
86 var tmpFile = path.join(tmpDir, crypto.randomBytes(16).toString('hex'))
87 var tmpStream = fs.createWriteStream(tmpFile, {mode: parseInt('0600', 8)})
88 process.on('uncaughtException', function (err) {
89 try {
90 fs.unlinkSync(tmpFile)
91 }
92 catch (e) {}
93 throw err
94 })
95 function onExit () {
96 try {
97 fs.unlinkSync(tmpFile)
98 }
99 catch (e) {}
100 process.exit(1)
101 }
102 process.once('SIGINT', onExit)
103 process.once('SIGTERM', onExit)
104 tmpStream.once('finish', function () {
105 inStat = fs.statSync(tmpFile)
106 if (options.armor && inStat.size > constants.PEM_MAX_SIZE) {
107 throw new Error('File is too big for ascii armor')
108 }
109 inStream = fs.createReadStream(tmpFile)
110 inStream.once('end', function () {
111 fs.unlinkSync(tmpFile)
112 })
113 encryptor = encrypt(inStream, recipient, nonce, inStat.size, wallet, options.armor, options.headers)
114 withEncryptor(encryptor, inStat.size)
115 })
116 options.headers['content-type'] = 'application/x-tar'
117 options.headers['content-encoding'] = 'x-gzip'
118 var reader = fstream.Reader({
119 path: inFile,
120 type: 'Directory',
121 sort: 'alpha',
122 mode: parseInt('0700', 8)
123 })
124 var bar
125 reader.once('entries', function (entries) {
126 bar = new Progress(' packing [:bar] :percent ETA: :etas', { total: entries.length, width: 80 })
127 })
128 reader.on('entry', function (entry) {
129 bar.tick()
130 })
131 reader.on('end', function () {
132 bar.terminate()
133 })
134 gzipStream.pipe(tmpStream)
135 reader.pipe(tarStream)
136 }
137 else {
138 if (options.armor && inStat.size > constants.PEM_MAX_SIZE) {
139 throw new Error('File is too big for ascii armor')
140 }
141 inStream = fs.createReadStream(inFile)
142 encryptor = encrypt(inStream, recipient, nonce, inStat.size, wallet, options.armor, options.headers)
143 withEncryptor(encryptor, inStat.size)
144 }
145 }
146 var header, outStream
147 function withEncryptor (encryptor, totalSize) {
148 encryptor.once('header', function (h) {
149 header = options.translate ? translateHeader(h, recipients) : h
150 })
151 if (options.armor) {
152 var chunks = []
153 outStream = through(function write (buf) {
154 chunks.push(buf)
155 }, function end () {
156 var buf = Buffer.concat(chunks)
157 var str = pempal.encode(buf, {tag: 'SALTY MESSAGE'})
158 if (options.gist) {
159 createGist(str, function (err, gist) {
160 if (err) throw err
161 console.log(gist.html_url)
162 })
163 }
164 else console.log(str)
165 })
166 }
167 else {
168 outStream = fs.createWriteStream(outFile, {mode: parseInt('0644', 8)})
169 outStream.once('finish', function () {
170 bar.terminate()
171 printHeader(header)
172 console.log('Encrypted to', outFile)
173 })
174 process.on('uncaughtException', function (err) {
175 try {
176 fs.unlinkSync(outFile)
177 }
178 catch (e) {}
179 throw err
180 })
181 function onExit () {
182 try {
183 fs.unlinkSync(outFile)
184 }
185 catch (e) {}
186 process.exit(1)
187 }
188 process.once('SIGINT', onExit)
189 process.once('SIGTERM', onExit)
190 var bar = new Progress(' encrypting [:bar] :percent ETA: :etas', { total: totalSize, width: 80 })
191 var chunkCounter = 0
192 var tickCounter = 0
193 encryptor.on('data', function (chunk) {
194 chunkCounter++
195 tickCounter += chunk.length
196 if (chunkCounter % 100 === 0) {
197 bar.tick(tickCounter)
198 tickCounter = 0
199 }
200 })
201 }
202 encryptor.pipe(outStream)
203 }
204 }
205 })
206}
\No newline at end of file