Files: 0dc8433933a96a2338ce1948f308f6a305675003 / confirm.js
5586 bytesRaw
1 | var proc = require('child_process') |
2 | var fs = require('fs') |
3 | var http = require('http') |
4 | var URL = require('url') |
5 | var crypto = require('crypto') |
6 | var os = require('os') |
7 | var qs = require('querystring') |
8 | |
9 | function escapeHTML (html) { |
10 | return String(html) |
11 | .replace(/&/g, '&') |
12 | .replace(/"/g, '"') |
13 | .replace(/</g, '<') |
14 | .replace(/>/g, '>') |
15 | .replace(/\n/g, '
') |
16 | } |
17 | |
18 | function noPublish(content, cb) { |
19 | cb(new Error('missing publish function')) |
20 | } |
21 | |
22 | function noPrivatePublish(content, recps, cb) { |
23 | cb(new Error('missing private publish function')) |
24 | } |
25 | |
26 | function getForm(stream, cb) { |
27 | var bufs = [] |
28 | stream.on('data', function (buf) { |
29 | bufs.push(buf) |
30 | }) |
31 | stream.on('end', function () { |
32 | var data |
33 | try { |
34 | data = qs.parse(Buffer.concat(bufs).toString('utf8')) |
35 | } catch(e) { |
36 | return cb(e) |
37 | } |
38 | cb(null, data) |
39 | }) |
40 | } |
41 | |
42 | function confirm(opts, cb) { |
43 | var content = opts.content |
44 | var browser = opts.browser |
45 | var recps = opts.recps |
46 | var publish = opts.publish || noPublish |
47 | var getUrl = opts.getUrl |
48 | var privatePublish = opts.privatePublish || noPrivatePublish |
49 | var redirectBase = opts.redirectBase |
50 | |
51 | if (typeof cb !== 'function') { |
52 | throw new TypeError('bad callback') |
53 | } |
54 | |
55 | if (browser && getUrl) { |
56 | return cb(new TypeError('browser option and getUrl option conflict')) |
57 | } |
58 | if (!browser && !getUrl) { |
59 | return cb(new TypeError('missing browser option')) |
60 | } |
61 | if (recps && !Array.isArray(recps)) { |
62 | return cb(new TypeError('recps should be array')) |
63 | } |
64 | |
65 | var token = crypto.randomBytes(32).toString('base64') |
66 | |
67 | function publish1(content, recps, cb) { |
68 | if (recps) privatePublish(content, recps, cb) |
69 | else publish(content, cb) |
70 | } |
71 | |
72 | var server = http.createServer(function (req, res) { |
73 | var url = URL.parse(req.url, true) |
74 | if (url.pathname !== '/') { |
75 | res.writeHead(404) |
76 | return res.end('Not Found') |
77 | } |
78 | |
79 | if (req.method === 'POST') return getForm(req, function (err, data) { |
80 | if (err) { |
81 | res.writeHead(500) |
82 | return res.end(err.stack || err) |
83 | } |
84 | |
85 | if (data.token !== token) { |
86 | res.writeHead(403) |
87 | return res.end('Invalid token') |
88 | } |
89 | |
90 | if (data.publish) { |
91 | return publish1(content, recps, function (err, msg) { |
92 | if (err) { |
93 | res.writeHead(500, {'Content-Type': 'text/html'}) |
94 | res.end('<!doctype html><html><head><meta charset=utf8><title>publish error</title></head><body>' |
95 | + '<h1>' + escapeHTML(err.name || err) + '</h1>' |
96 | + '<pre>' + escapeHTML(err.stack || '') + '</pre>' |
97 | + '</body></html>') |
98 | server.close() |
99 | return !getUrl && cb(err) |
100 | } |
101 | var json = JSON.stringify(msg, 0, 2) |
102 | if (redirectBase) { |
103 | var url = redirectBase + encodeURIComponent(msg.key) |
104 | res.writeHead(302, {'Location': url}) |
105 | res.end() |
106 | } else { |
107 | res.writeHead(200, {'Content-Type': 'text/html'}) |
108 | res.end('<!doctype html><html><head><meta charset=utf8><title>published</title></head><body>' |
109 | + '<pre>' + escapeHTML(json) + '</pre>' |
110 | + '</body></html>') |
111 | } |
112 | server.close() |
113 | return !getUrl && cb(null, msg) |
114 | }) |
115 | } |
116 | if (data.cancel) { |
117 | res.writeHead(200, {'Content-Type': 'text/html'}) |
118 | res.end('<!doctype html><html><head><meta charset=utf8><title>canceled</title></head><body>' |
119 | + '<p>Cancelled</p>' |
120 | + '</body></html>') |
121 | if (server) server.close() |
122 | return !getUrl && cb(new Error('User cancelled')) |
123 | } |
124 | }) |
125 | |
126 | var contentJson = JSON.stringify(content, null, 2) |
127 | var recpsJson = JSON.stringify(recps, null, 2) |
128 | res.writeHead(200, {'Content-Type': 'text/html'}) |
129 | res.end('<!doctype html><html><head><meta charset=utf8><title>publish</title></head><body>' |
130 | + '<form action="/" method="post">' |
131 | + (recps ? '<pre>' + recpsJson + '</pre>' : '') |
132 | + '<pre>' + escapeHTML(contentJson) + '</pre>' |
133 | + '<input type="hidden" name="token" value="' + token + '">' |
134 | + '<input type="submit" name="publish" value="Publish"> ' |
135 | + '<input type="submit" name="cancel" value="Cancel">' |
136 | + '</form>' |
137 | + '</body></html>') |
138 | }).listen(0, '127.0.0.1', function () { |
139 | var port = this.address().port |
140 | var url = 'http://127.0.0.1:' + port + '/' |
141 | if (browser) proc.spawn(browser, [url], {stdio: 'inherit'}) |
142 | else cb(null, url) |
143 | }).on('error', cb) |
144 | } |
145 | |
146 | module.exports = function (opts) { |
147 | var publish = opts.publish |
148 | var privatePublish = opts.privatePublish |
149 | var config = opts.config || {} |
150 | var browser = config.browser || |
151 | (os.platform() === 'darwin' ? 'open' : 'xdg-open') |
152 | var name = opts.name || 'confirm' |
153 | |
154 | return { |
155 | publish: function (content, cb) { |
156 | confirm({ |
157 | publish: publish, |
158 | content: content, |
159 | browser: browser, |
160 | }, cb) |
161 | }, |
162 | privatePublish: function (content, recps, cb) { |
163 | confirm({ |
164 | privatePublish: privatePublish, |
165 | recps: recps, |
166 | content: content, |
167 | browser: browser, |
168 | }, cb) |
169 | }, |
170 | publishGetUrl: function (opts, cb) { |
171 | confirm({ |
172 | publish: publish, |
173 | content: opts.content, |
174 | redirectBase: opts.redirectBase, |
175 | getUrl: true, |
176 | }, cb) |
177 | }, |
178 | privatePublishGetUrl: function (opts, cb) { |
179 | confirm({ |
180 | privatePublish: privatePublish, |
181 | recps: opts.recps, |
182 | content: opts.content, |
183 | redirectBase: opts.redirectBase, |
184 | getUrl: true, |
185 | }, cb) |
186 | } |
187 | } |
188 | } |
189 |
Built with git-ssb-web