1 | var 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 |
|
23 | module.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 |