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