git ssb

3+

cel / ssb-publishguard



Tree: 0dc8433933a96a2338ce1948f308f6a305675003

Files: 0dc8433933a96a2338ce1948f308f6a305675003 / confirm.js

5586 bytesRaw
1var proc = require('child_process')
2var fs = require('fs')
3var http = require('http')
4var URL = require('url')
5var crypto = require('crypto')
6var os = require('os')
7var qs = require('querystring')
8
9function escapeHTML (html) {
10 return String(html)
11 .replace(/&/g, '&')
12 .replace(/"/g, '"')
13 .replace(/</g, '&lt;')
14 .replace(/>/g, '&gt;')
15 .replace(/\n/g, '&#x0a;')
16}
17
18function noPublish(content, cb) {
19 cb(new Error('missing publish function'))
20}
21
22function noPrivatePublish(content, recps, cb) {
23 cb(new Error('missing private publish function'))
24}
25
26function 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
42function 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
146module.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