@rndmjoker/proton-mcp 0.4.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +20 -0
- package/README.md +405 -0
- package/dist/activity.d.ts +49 -0
- package/dist/activity.js +72 -0
- package/dist/activity.js.map +1 -0
- package/dist/bridge/certificate.d.ts +75 -0
- package/dist/bridge/certificate.js +176 -0
- package/dist/bridge/certificate.js.map +1 -0
- package/dist/bridge/connection.d.ts +99 -0
- package/dist/bridge/connection.js +244 -0
- package/dist/bridge/connection.js.map +1 -0
- package/dist/bridge/errors.d.ts +35 -0
- package/dist/bridge/errors.js +56 -0
- package/dist/bridge/errors.js.map +1 -0
- package/dist/config.d.ts +92 -0
- package/dist/config.js +142 -0
- package/dist/config.js.map +1 -0
- package/dist/credentials/availability.d.ts +62 -0
- package/dist/credentials/availability.js +107 -0
- package/dist/credentials/availability.js.map +1 -0
- package/dist/credentials/encrypted-file.d.ts +50 -0
- package/dist/credentials/encrypted-file.js +156 -0
- package/dist/credentials/encrypted-file.js.map +1 -0
- package/dist/credentials/keyring.d.ts +48 -0
- package/dist/credentials/keyring.js +97 -0
- package/dist/credentials/keyring.js.map +1 -0
- package/dist/credentials/plain-file.d.ts +24 -0
- package/dist/credentials/plain-file.js +78 -0
- package/dist/credentials/plain-file.js.map +1 -0
- package/dist/credentials/probe-keyring.d.ts +11 -0
- package/dist/credentials/probe-keyring.js +57 -0
- package/dist/credentials/probe-keyring.js.map +1 -0
- package/dist/credentials/session.d.ts +15 -0
- package/dist/credentials/session.js +21 -0
- package/dist/credentials/session.js.map +1 -0
- package/dist/credentials/store.d.ts +80 -0
- package/dist/credentials/store.js +94 -0
- package/dist/credentials/store.js.map +1 -0
- package/dist/in-flight.d.ts +33 -0
- package/dist/in-flight.js +78 -0
- package/dist/in-flight.js.map +1 -0
- package/dist/mail/actions.d.ts +116 -0
- package/dist/mail/actions.js +265 -0
- package/dist/mail/actions.js.map +1 -0
- package/dist/mail/attachments.d.ts +39 -0
- package/dist/mail/attachments.js +86 -0
- package/dist/mail/attachments.js.map +1 -0
- package/dist/mail/compose.d.ts +264 -0
- package/dist/mail/compose.js +395 -0
- package/dist/mail/compose.js.map +1 -0
- package/dist/mail/drafts.d.ts +138 -0
- package/dist/mail/drafts.js +449 -0
- package/dist/mail/drafts.js.map +1 -0
- package/dist/mail/ids.d.ts +78 -0
- package/dist/mail/ids.js +135 -0
- package/dist/mail/ids.js.map +1 -0
- package/dist/mail/labels.d.ts +61 -0
- package/dist/mail/labels.js +139 -0
- package/dist/mail/labels.js.map +1 -0
- package/dist/mail/markup.d.ts +91 -0
- package/dist/mail/markup.js +265 -0
- package/dist/mail/markup.js.map +1 -0
- package/dist/mail/messages.d.ts +147 -0
- package/dist/mail/messages.js +219 -0
- package/dist/mail/messages.js.map +1 -0
- package/dist/mail/quote.d.ts +65 -0
- package/dist/mail/quote.js +157 -0
- package/dist/mail/quote.js.map +1 -0
- package/dist/mail/search.d.ts +66 -0
- package/dist/mail/search.js +107 -0
- package/dist/mail/search.js.map +1 -0
- package/dist/mail/send.d.ts +61 -0
- package/dist/mail/send.js +132 -0
- package/dist/mail/send.js.map +1 -0
- package/dist/mime/parse.d.ts +110 -0
- package/dist/mime/parse.js +194 -0
- package/dist/mime/parse.js.map +1 -0
- package/dist/server.d.ts +13 -0
- package/dist/server.js +387 -0
- package/dist/server.js.map +1 -0
- package/dist/session.d.ts +86 -0
- package/dist/session.js +220 -0
- package/dist/session.js.map +1 -0
- package/dist/settings.d.ts +52 -0
- package/dist/settings.js +80 -0
- package/dist/settings.js.map +1 -0
- package/dist/tools/actions.d.ts +21 -0
- package/dist/tools/actions.js +133 -0
- package/dist/tools/actions.js.map +1 -0
- package/dist/tools/capabilities.d.ts +52 -0
- package/dist/tools/capabilities.js +96 -0
- package/dist/tools/capabilities.js.map +1 -0
- package/dist/tools/confirm.d.ts +84 -0
- package/dist/tools/confirm.js +212 -0
- package/dist/tools/confirm.js.map +1 -0
- package/dist/tools/drafts.d.ts +21 -0
- package/dist/tools/drafts.js +199 -0
- package/dist/tools/drafts.js.map +1 -0
- package/dist/tools/failures.d.ts +46 -0
- package/dist/tools/failures.js +157 -0
- package/dist/tools/failures.js.map +1 -0
- package/dist/tools/format.d.ts +55 -0
- package/dist/tools/format.js +220 -0
- package/dist/tools/format.js.map +1 -0
- package/dist/tools/guidance.d.ts +38 -0
- package/dist/tools/guidance.js +226 -0
- package/dist/tools/guidance.js.map +1 -0
- package/dist/tools/interface.d.ts +28 -0
- package/dist/tools/interface.js +63 -0
- package/dist/tools/interface.js.map +1 -0
- package/dist/tools/labels.d.ts +21 -0
- package/dist/tools/labels.js +92 -0
- package/dist/tools/labels.js.map +1 -0
- package/dist/tools/list-folders.d.ts +14 -0
- package/dist/tools/list-folders.js +70 -0
- package/dist/tools/list-folders.js.map +1 -0
- package/dist/tools/messages.d.ts +9 -0
- package/dist/tools/messages.js +221 -0
- package/dist/tools/messages.js.map +1 -0
- package/dist/tools/send.d.ts +29 -0
- package/dist/tools/send.js +236 -0
- package/dist/tools/send.js.map +1 -0
- package/dist/tools/sign-in-required.d.ts +94 -0
- package/dist/tools/sign-in-required.js +168 -0
- package/dist/tools/sign-in-required.js.map +1 -0
- package/dist/web/layout.d.ts +75 -0
- package/dist/web/layout.js +590 -0
- package/dist/web/layout.js.map +1 -0
- package/dist/web/pages.d.ts +79 -0
- package/dist/web/pages.js +284 -0
- package/dist/web/pages.js.map +1 -0
- package/dist/web/sections.d.ts +105 -0
- package/dist/web/sections.js +207 -0
- package/dist/web/sections.js.map +1 -0
- package/dist/web/security.d.ts +122 -0
- package/dist/web/security.js +180 -0
- package/dist/web/security.js.map +1 -0
- package/dist/web/server.d.ts +153 -0
- package/dist/web/server.js +622 -0
- package/dist/web/server.js.map +1 -0
- package/package.json +70 -0
|
@@ -0,0 +1,622 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The local web interface.
|
|
3
|
+
*
|
|
4
|
+
* Built on node:http with no framework. For something that handles credentials,
|
|
5
|
+
* every dependency is attack surface, and the routing here fits on one screen.
|
|
6
|
+
*
|
|
7
|
+
* It serves no files from disk at all: every response is generated. That removes
|
|
8
|
+
* path traversal as a category rather than guarding against it.
|
|
9
|
+
*
|
|
10
|
+
* All security checks live in security.ts and run before any handler. See there
|
|
11
|
+
* for what each one defends against.
|
|
12
|
+
*/
|
|
13
|
+
var _a;
|
|
14
|
+
import { createServer } from 'node:http';
|
|
15
|
+
import { STORE_DESCRIPTIONS } from '../credentials/store.js';
|
|
16
|
+
import { listStores, recommend, recommendationReason } from '../credentials/availability.js';
|
|
17
|
+
import { createSession, checkRequest, csrfToken, securityHeaders } from './security.js';
|
|
18
|
+
import { loginPage, rejectionPage, unlockPage } from './pages.js';
|
|
19
|
+
import { overviewSection, bridgeSection, credentialsSection, activitySection, mailboxSection, } from './sections.js';
|
|
20
|
+
/**
|
|
21
|
+
* Upper bound for a request body.
|
|
22
|
+
*
|
|
23
|
+
* The forms here carry an address, a password and a token. Anything larger is
|
|
24
|
+
* either a mistake or an attempt to make the process allocate memory.
|
|
25
|
+
*/
|
|
26
|
+
const MAX_BODY_BYTES = 8 * 1024;
|
|
27
|
+
/** Only loopback. There is no option to change this, by design. */
|
|
28
|
+
const BIND_ADDRESS = '127.0.0.1';
|
|
29
|
+
/**
|
|
30
|
+
* Which address shows which section.
|
|
31
|
+
*
|
|
32
|
+
* One table rather than a chain of comparisons, so that a path nobody listed
|
|
33
|
+
* cannot quietly reach a renderer. Everything not in here falls through to the
|
|
34
|
+
* rejection at the end of the handler.
|
|
35
|
+
*/
|
|
36
|
+
const SECTION_PATHS = {
|
|
37
|
+
'/': 'overview',
|
|
38
|
+
'/mailboxes': 'mailboxes',
|
|
39
|
+
'/bridge': 'bridge',
|
|
40
|
+
'/credentials': 'credentials',
|
|
41
|
+
'/activity': 'activity',
|
|
42
|
+
};
|
|
43
|
+
/** The same names as a set, for checking a section that arrived in a form. */
|
|
44
|
+
const SECTIONS = new Set(Object.values(SECTION_PATHS));
|
|
45
|
+
/**
|
|
46
|
+
* The port tried first.
|
|
47
|
+
*
|
|
48
|
+
* A fixed default rather than a random one, because the interface runs for as
|
|
49
|
+
* long as the server does and is meant to be opened again and again. A port that
|
|
50
|
+
* moved on every start would make that needlessly awkward.
|
|
51
|
+
*/
|
|
52
|
+
export const DEFAULT_WEB_PORT = 7345;
|
|
53
|
+
/**
|
|
54
|
+
* How many consecutive ports to try.
|
|
55
|
+
*
|
|
56
|
+
* Several clients may each start their own server, and the second one must not
|
|
57
|
+
* fail over a port the first one took. Failing outright would be worse than
|
|
58
|
+
* moving up by one.
|
|
59
|
+
*/
|
|
60
|
+
export const PORT_ATTEMPTS = 10;
|
|
61
|
+
/**
|
|
62
|
+
* Reads a form body, refusing anything oversized.
|
|
63
|
+
*
|
|
64
|
+
* The limit is enforced while reading rather than after, so an oversized body is
|
|
65
|
+
* never held in memory in full.
|
|
66
|
+
*/
|
|
67
|
+
export function readBody(request, maxBytes = MAX_BODY_BYTES) {
|
|
68
|
+
return new Promise((resolve) => {
|
|
69
|
+
const chunks = [];
|
|
70
|
+
let size = 0;
|
|
71
|
+
let aborted = false;
|
|
72
|
+
request.on('data', (chunk) => {
|
|
73
|
+
if (aborted)
|
|
74
|
+
return;
|
|
75
|
+
size += chunk.length;
|
|
76
|
+
if (size > maxBytes) {
|
|
77
|
+
aborted = true;
|
|
78
|
+
chunks.length = 0;
|
|
79
|
+
// Deliberately not destroy(): that resets the connection and the client
|
|
80
|
+
// sees ECONNRESET instead of an explanation. The rest of the body is
|
|
81
|
+
// drained and discarded so the handler can still answer with 413.
|
|
82
|
+
request.resume();
|
|
83
|
+
resolve({ fields: {}, tooLarge: true });
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
chunks.push(chunk);
|
|
87
|
+
});
|
|
88
|
+
request.on('end', () => {
|
|
89
|
+
if (aborted)
|
|
90
|
+
return;
|
|
91
|
+
const params = new URLSearchParams(Buffer.concat(chunks).toString('utf8'));
|
|
92
|
+
const fields = {};
|
|
93
|
+
for (const [key, value] of params)
|
|
94
|
+
fields[key] = value;
|
|
95
|
+
resolve({ fields, tooLarge: false });
|
|
96
|
+
});
|
|
97
|
+
request.on('error', () => {
|
|
98
|
+
if (!aborted)
|
|
99
|
+
resolve({ fields: {}, tooLarge: false });
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
export class WebInterface {
|
|
104
|
+
#server;
|
|
105
|
+
#secrets = createSession();
|
|
106
|
+
#port = 0;
|
|
107
|
+
#options;
|
|
108
|
+
#notify;
|
|
109
|
+
constructor(options) {
|
|
110
|
+
this.#options = options;
|
|
111
|
+
this.#notify = options.notify ?? ((m) => process.stderr.write(`proton-mcp: ${m}\n`));
|
|
112
|
+
}
|
|
113
|
+
/** The URL the user has to open, including the access token. */
|
|
114
|
+
get url() {
|
|
115
|
+
return `http://${BIND_ADDRESS}:${this.#port}/?token=${this.#secrets.accessToken}`;
|
|
116
|
+
}
|
|
117
|
+
get port() {
|
|
118
|
+
return this.#port;
|
|
119
|
+
}
|
|
120
|
+
get running() {
|
|
121
|
+
return this.#server?.listening === true;
|
|
122
|
+
}
|
|
123
|
+
async start() {
|
|
124
|
+
if (this.#server?.listening)
|
|
125
|
+
return this.url;
|
|
126
|
+
const build = () => {
|
|
127
|
+
const created = createServer((request, response) => {
|
|
128
|
+
void this.#handle(request, response).catch(() => {
|
|
129
|
+
// A handler that throws must not take the process down, and must not
|
|
130
|
+
// leak anything either.
|
|
131
|
+
this.#send(response, 500, 'text/plain; charset=utf-8', 'Internal error.');
|
|
132
|
+
});
|
|
133
|
+
});
|
|
134
|
+
// Slow clients must not be able to tie up the server indefinitely.
|
|
135
|
+
created.headersTimeout = 10_000;
|
|
136
|
+
created.requestTimeout = 30_000;
|
|
137
|
+
return created;
|
|
138
|
+
};
|
|
139
|
+
const wanted = this.#options.port ?? DEFAULT_WEB_PORT;
|
|
140
|
+
// Port 0 means "any free port" and needs no retry loop.
|
|
141
|
+
const attempts = wanted === 0 ? 1 : PORT_ATTEMPTS;
|
|
142
|
+
let server;
|
|
143
|
+
let lastError;
|
|
144
|
+
for (let i = 0; i < attempts; i++) {
|
|
145
|
+
const candidate = wanted === 0 ? 0 : wanted + i;
|
|
146
|
+
// A fresh instance per attempt: after a failed listen() the old one is not
|
|
147
|
+
// reliably reusable.
|
|
148
|
+
const attempt = build();
|
|
149
|
+
try {
|
|
150
|
+
await new Promise((resolve, reject) => {
|
|
151
|
+
const onError = (error) => reject(error);
|
|
152
|
+
attempt.once('error', onError);
|
|
153
|
+
// Explicitly loopback: binding to 0.0.0.0 would put a page that stores
|
|
154
|
+
// credentials onto the network.
|
|
155
|
+
attempt.listen(candidate, BIND_ADDRESS, () => {
|
|
156
|
+
attempt.removeListener('error', onError);
|
|
157
|
+
resolve();
|
|
158
|
+
});
|
|
159
|
+
});
|
|
160
|
+
server = attempt;
|
|
161
|
+
lastError = undefined;
|
|
162
|
+
break;
|
|
163
|
+
}
|
|
164
|
+
catch (error) {
|
|
165
|
+
attempt.close();
|
|
166
|
+
lastError = error;
|
|
167
|
+
if (error.code !== 'EADDRINUSE')
|
|
168
|
+
break;
|
|
169
|
+
// Taken, most likely by another instance of this server. Try the next.
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
if (!server || lastError) {
|
|
173
|
+
const code = lastError?.code;
|
|
174
|
+
throw new Error(code === 'EADDRINUSE'
|
|
175
|
+
? `Ports ${wanted} to ${wanted + attempts - 1} are all in use, so the web interface ` +
|
|
176
|
+
'could not start. Set PROTON_MCP_WEB_PORT to a free port.'
|
|
177
|
+
: `The web interface could not start: ${lastError?.message ?? 'unknown reason'}`);
|
|
178
|
+
}
|
|
179
|
+
const address = server.address();
|
|
180
|
+
this.#port = typeof address === 'object' && address ? address.port : 0;
|
|
181
|
+
this.#server = server;
|
|
182
|
+
return this.url;
|
|
183
|
+
}
|
|
184
|
+
async stop() {
|
|
185
|
+
const server = this.#server;
|
|
186
|
+
this.#server = undefined;
|
|
187
|
+
if (!server)
|
|
188
|
+
return;
|
|
189
|
+
// close() alone only stops accepting new connections and then waits for the
|
|
190
|
+
// existing ones. A browser keeps its connection alive, so a single open tab
|
|
191
|
+
// would hold the shutdown forever and the MCP server would never exit.
|
|
192
|
+
server.closeIdleConnections();
|
|
193
|
+
server.closeAllConnections();
|
|
194
|
+
await new Promise((resolve) => server.close(() => resolve()));
|
|
195
|
+
}
|
|
196
|
+
/** Invalidates the current link and issues a new one. */
|
|
197
|
+
rotateToken() {
|
|
198
|
+
this.#secrets = createSession();
|
|
199
|
+
return this.url;
|
|
200
|
+
}
|
|
201
|
+
#send(response, status, contentType, body) {
|
|
202
|
+
response.writeHead(status, { ...securityHeaders(), 'Content-Type': contentType });
|
|
203
|
+
response.end(body);
|
|
204
|
+
}
|
|
205
|
+
#reject(response, status = 404) {
|
|
206
|
+
// Always the same page and the same status, whatever the reason. Telling a
|
|
207
|
+
// caller which check failed would help them work around it.
|
|
208
|
+
this.#send(response, status, 'text/html; charset=utf-8', rejectionPage());
|
|
209
|
+
}
|
|
210
|
+
async #handle(request, response) {
|
|
211
|
+
const url = new URL(request.url ?? '/', `http://${BIND_ADDRESS}:${this.#port}`);
|
|
212
|
+
const path = url.pathname;
|
|
213
|
+
const method = request.method ?? 'GET';
|
|
214
|
+
const writing = method === 'POST';
|
|
215
|
+
if (method !== 'GET' && method !== 'POST') {
|
|
216
|
+
this.#reject(response, 405);
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
// Answered before the token check and without a log line. Browsers ask for
|
|
220
|
+
// it on every page view, and each one would otherwise be recorded as a
|
|
221
|
+
// refused request, which buries the refusals that mean something.
|
|
222
|
+
if (path === '/favicon.ico') {
|
|
223
|
+
response.writeHead(404, { ...securityHeaders(), 'Content-Length': '0' });
|
|
224
|
+
response.end();
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
// The body has to be read before the checks for a POST, because the CSRF
|
|
228
|
+
// token is in it. It is bounded, so this cannot be used to make us allocate.
|
|
229
|
+
const body = writing ? await readBody(request) : { fields: {}, tooLarge: false };
|
|
230
|
+
if (body.tooLarge) {
|
|
231
|
+
// Close the connection after answering: the client may still be sending,
|
|
232
|
+
// and keeping the socket alive would mean reading the rest for nothing.
|
|
233
|
+
response.setHeader('Connection', 'close');
|
|
234
|
+
this.#reject(response, 413);
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
const purpose = path;
|
|
238
|
+
const check = checkRequest({
|
|
239
|
+
request,
|
|
240
|
+
secrets: this.#secrets,
|
|
241
|
+
port: this.#port,
|
|
242
|
+
presentedToken: url.searchParams.get('token') ?? undefined,
|
|
243
|
+
writing,
|
|
244
|
+
...(writing ? { csrf: { purpose, presented: body.fields.csrf } } : {}),
|
|
245
|
+
});
|
|
246
|
+
if (!check.ok) {
|
|
247
|
+
// Worth a line on stderr: a rejected request on a loopback-only interface
|
|
248
|
+
// is either a mistake or something worth knowing about.
|
|
249
|
+
this.#notify(`web interface refused a ${method} to ${path} (${check.reason})`);
|
|
250
|
+
this.#reject(response);
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
253
|
+
if (method === 'GET') {
|
|
254
|
+
const section = SECTION_PATHS[path];
|
|
255
|
+
if (section) {
|
|
256
|
+
await this.#render(section, response);
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
if (writing && path === '/dismiss-notice') {
|
|
261
|
+
await this.#handleDismissNotice(response, body.fields);
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
264
|
+
if (writing && path === '/settings') {
|
|
265
|
+
await this.#handlePorts(response, body.fields);
|
|
266
|
+
return;
|
|
267
|
+
}
|
|
268
|
+
if (writing && path === '/test-connection') {
|
|
269
|
+
await this.#handleTest(response);
|
|
270
|
+
return;
|
|
271
|
+
}
|
|
272
|
+
if (writing && path === '/sign-in') {
|
|
273
|
+
await this.#handleSignIn(response, body.fields);
|
|
274
|
+
return;
|
|
275
|
+
}
|
|
276
|
+
if (writing && path === '/unlock') {
|
|
277
|
+
await this.#handleUnlock(response, body.fields);
|
|
278
|
+
return;
|
|
279
|
+
}
|
|
280
|
+
if (writing && path === '/discard-encrypted') {
|
|
281
|
+
await this.#options.onDiscardEncrypted?.();
|
|
282
|
+
// Both of these end with nobody signed in, so the section asked for is
|
|
283
|
+
// beside the point: #render lands on the sign-in form either way.
|
|
284
|
+
await this.#render('overview', response);
|
|
285
|
+
return;
|
|
286
|
+
}
|
|
287
|
+
if (writing && path === '/sign-out') {
|
|
288
|
+
await this.#options.onSignOut();
|
|
289
|
+
await this.#render('overview', response);
|
|
290
|
+
return;
|
|
291
|
+
}
|
|
292
|
+
this.#reject(response);
|
|
293
|
+
}
|
|
294
|
+
/**
|
|
295
|
+
* Renders one section, or whichever page the current state demands instead.
|
|
296
|
+
*
|
|
297
|
+
* The state outranks the request: stored-but-locked leads to the unlock page
|
|
298
|
+
* and nothing stored to the sign-in form, whichever section was asked for.
|
|
299
|
+
* That is what puts a wrong master password on the unlock page and a refused
|
|
300
|
+
* Bridge login on the sign-in form without either having to say so, and it is
|
|
301
|
+
* also why no section has to check whether anybody is signed in.
|
|
302
|
+
*/
|
|
303
|
+
async #render(section, response, outcome = {}) {
|
|
304
|
+
const { error, notice, address, store, showAdvanced } = outcome;
|
|
305
|
+
const status = await this.#options.getStatus();
|
|
306
|
+
const token = this.#secrets.accessToken;
|
|
307
|
+
const disclaimer = this.#disclaimerFor(section);
|
|
308
|
+
if (!status.connected && status.locked) {
|
|
309
|
+
const page = unlockPage({
|
|
310
|
+
token,
|
|
311
|
+
disclaimer,
|
|
312
|
+
csrf: csrfToken(this.#secrets, '/unlock'),
|
|
313
|
+
csrfDiscard: csrfToken(this.#secrets, '/discard-encrypted'),
|
|
314
|
+
path: status.encryptedPath ?? 'your user directory',
|
|
315
|
+
...(error ? { error } : {}),
|
|
316
|
+
});
|
|
317
|
+
this.#send(response, 200, 'text/html; charset=utf-8', page);
|
|
318
|
+
return;
|
|
319
|
+
}
|
|
320
|
+
if (!status.connected) {
|
|
321
|
+
const stores = await listStores();
|
|
322
|
+
const page = loginPage({
|
|
323
|
+
token,
|
|
324
|
+
disclaimer,
|
|
325
|
+
csrf: csrfToken(this.#secrets, '/sign-in'),
|
|
326
|
+
stores,
|
|
327
|
+
suggested: recommend(stores),
|
|
328
|
+
suggestionReason: recommendationReason(stores),
|
|
329
|
+
bridgeHost: status.bridgeHost,
|
|
330
|
+
bridgeImapPort: status.bridgeImapPort,
|
|
331
|
+
bridgeSmtpPort: status.bridgeSmtpPort,
|
|
332
|
+
portsLocked: this.#options.onPorts === undefined,
|
|
333
|
+
...(store ? { chosen: store } : {}),
|
|
334
|
+
...(error ? { error } : {}),
|
|
335
|
+
...(address ? { address } : {}),
|
|
336
|
+
...(showAdvanced ? { showAdvanced } : {}),
|
|
337
|
+
});
|
|
338
|
+
this.#send(response, 200, 'text/html; charset=utf-8', page);
|
|
339
|
+
return;
|
|
340
|
+
}
|
|
341
|
+
const banners = {
|
|
342
|
+
disclaimer,
|
|
343
|
+
...(error ? { error } : {}),
|
|
344
|
+
...(notice ? { notice } : {}),
|
|
345
|
+
};
|
|
346
|
+
const storeTitle = status.storeKind
|
|
347
|
+
? { storeTitle: STORE_DESCRIPTIONS[status.storeKind].title }
|
|
348
|
+
: {};
|
|
349
|
+
let page;
|
|
350
|
+
switch (section) {
|
|
351
|
+
case 'mailboxes':
|
|
352
|
+
page = await this.#renderMailboxes(token, banners);
|
|
353
|
+
break;
|
|
354
|
+
case 'bridge':
|
|
355
|
+
page = bridgeSection({
|
|
356
|
+
token,
|
|
357
|
+
csrfSettings: csrfToken(this.#secrets, '/settings'),
|
|
358
|
+
csrfTest: csrfToken(this.#secrets, '/test-connection'),
|
|
359
|
+
bridgeHost: status.bridgeHost,
|
|
360
|
+
bridgeImapPort: status.bridgeImapPort,
|
|
361
|
+
bridgeSmtpPort: status.bridgeSmtpPort,
|
|
362
|
+
// No handler means the ports are not ours to change.
|
|
363
|
+
portsLocked: this.#options.onPorts === undefined,
|
|
364
|
+
...(status.certificate ? { certificate: status.certificate } : {}),
|
|
365
|
+
...banners,
|
|
366
|
+
});
|
|
367
|
+
break;
|
|
368
|
+
case 'credentials':
|
|
369
|
+
page = credentialsSection({
|
|
370
|
+
token,
|
|
371
|
+
csrfSignOut: csrfToken(this.#secrets, '/sign-out'),
|
|
372
|
+
...(status.address ? { address: status.address } : {}),
|
|
373
|
+
...(status.storeKind ? { storeKind: status.storeKind } : {}),
|
|
374
|
+
...storeTitle,
|
|
375
|
+
...banners,
|
|
376
|
+
});
|
|
377
|
+
break;
|
|
378
|
+
case 'activity': {
|
|
379
|
+
const activity = this.#options.getActivity?.() ?? { running: [], recent: [] };
|
|
380
|
+
page = activitySection({ token, ...activity, ...banners });
|
|
381
|
+
break;
|
|
382
|
+
}
|
|
383
|
+
default:
|
|
384
|
+
page = overviewSection({
|
|
385
|
+
token,
|
|
386
|
+
connected: true,
|
|
387
|
+
...(status.address ? { address: status.address } : {}),
|
|
388
|
+
...storeTitle,
|
|
389
|
+
...(status.mailboxCount !== undefined ? { mailboxCount: status.mailboxCount } : {}),
|
|
390
|
+
...(status.messageCount !== undefined ? { messageCount: status.messageCount } : {}),
|
|
391
|
+
...(status.unseenCount !== undefined ? { unseenCount: status.unseenCount } : {}),
|
|
392
|
+
bridgeHost: status.bridgeHost,
|
|
393
|
+
bridgeImapPort: status.bridgeImapPort,
|
|
394
|
+
bridgeSmtpPort: status.bridgeSmtpPort,
|
|
395
|
+
readOnly: status.readOnly === true,
|
|
396
|
+
webPort: this.#port,
|
|
397
|
+
uptimeMs: status.uptimeMs ?? 0,
|
|
398
|
+
...banners,
|
|
399
|
+
});
|
|
400
|
+
}
|
|
401
|
+
this.#send(response, 200, 'text/html; charset=utf-8', page);
|
|
402
|
+
}
|
|
403
|
+
/**
|
|
404
|
+
* Whether the notice is shown, and whether it carries a button.
|
|
405
|
+
*
|
|
406
|
+
* A server wired without a way to remember the dismissal still shows the
|
|
407
|
+
* notice, just without the button. Silently dropping it because a handler is
|
|
408
|
+
* missing would be the one outcome nobody wants.
|
|
409
|
+
*/
|
|
410
|
+
#disclaimerFor(section) {
|
|
411
|
+
if (this.#options.noticeDismissed?.() === true)
|
|
412
|
+
return 'dismissed';
|
|
413
|
+
if (!this.#options.onDismissNotice)
|
|
414
|
+
return 'plain';
|
|
415
|
+
return { csrf: csrfToken(this.#secrets, '/dismiss-notice'), from: section };
|
|
416
|
+
}
|
|
417
|
+
/**
|
|
418
|
+
* Sends the notice away for good.
|
|
419
|
+
*
|
|
420
|
+
* The section it was dismissed from travels in the form, so the answer is the
|
|
421
|
+
* page the user was already on rather than a jump back to the overview. An
|
|
422
|
+
* unknown value falls back to the overview instead of being trusted.
|
|
423
|
+
*/
|
|
424
|
+
async #handleDismissNotice(response, fields) {
|
|
425
|
+
await this.#options.onDismissNotice?.();
|
|
426
|
+
const from = SECTIONS.has(fields.from ?? '') ? fields.from : 'overview';
|
|
427
|
+
await this.#render(from, response);
|
|
428
|
+
}
|
|
429
|
+
/**
|
|
430
|
+
* The folders and labels, only ever on this page.
|
|
431
|
+
*
|
|
432
|
+
* Never folded into another section: these names belong to the whole Proton
|
|
433
|
+
* account and give away banks, employers and authorities on their own. The
|
|
434
|
+
* caller has already established that somebody is signed in, so the only
|
|
435
|
+
* question left here is whether the Bridge answers.
|
|
436
|
+
*/
|
|
437
|
+
async #renderMailboxes(token, banners) {
|
|
438
|
+
let mailboxes = [];
|
|
439
|
+
let error = banners.error;
|
|
440
|
+
if (this.#options.getMailboxes) {
|
|
441
|
+
try {
|
|
442
|
+
mailboxes = await this.#options.getMailboxes();
|
|
443
|
+
}
|
|
444
|
+
catch (failure) {
|
|
445
|
+
error = failure.message;
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
return mailboxSection({
|
|
449
|
+
token,
|
|
450
|
+
mailboxes,
|
|
451
|
+
disclaimer: banners.disclaimer,
|
|
452
|
+
...(error ? { error } : {}),
|
|
453
|
+
...(banners.notice ? { notice: banners.notice } : {}),
|
|
454
|
+
});
|
|
455
|
+
}
|
|
456
|
+
/**
|
|
457
|
+
* Reads a pair of ports out of a form.
|
|
458
|
+
*
|
|
459
|
+
* One place for it, because two forms carry them now: the bridge section and
|
|
460
|
+
* the advanced block of the sign-in page. A port that is wrong has to be
|
|
461
|
+
* caught before anything tries to connect with it, or the answer is
|
|
462
|
+
* "connection refused" and the cause is invisible.
|
|
463
|
+
*/
|
|
464
|
+
static #readPorts(fields) {
|
|
465
|
+
const parsed = { imapPort: 0, smtpPort: 0 };
|
|
466
|
+
for (const field of ['imapPort', 'smtpPort']) {
|
|
467
|
+
const raw = (fields[field] ?? '').trim();
|
|
468
|
+
const value = Number(raw);
|
|
469
|
+
if (!raw || !Number.isInteger(value) || value < 1 || value > 65535) {
|
|
470
|
+
return {
|
|
471
|
+
error: `"${raw}" is not a usable port for ${field === 'imapPort' ? 'IMAP' : 'SMTP'}. ` +
|
|
472
|
+
'Expected a whole number between 1 and 65535.',
|
|
473
|
+
};
|
|
474
|
+
}
|
|
475
|
+
parsed[field] = value;
|
|
476
|
+
}
|
|
477
|
+
return { ports: parsed };
|
|
478
|
+
}
|
|
479
|
+
async #handlePorts(response, fields) {
|
|
480
|
+
if (!this.#options.onPorts) {
|
|
481
|
+
await this.#render('bridge', response, {
|
|
482
|
+
error: 'The ports are set through the environment on this server.',
|
|
483
|
+
});
|
|
484
|
+
return;
|
|
485
|
+
}
|
|
486
|
+
const read = _a.#readPorts(fields);
|
|
487
|
+
if ('error' in read) {
|
|
488
|
+
await this.#render('bridge', response, { error: read.error });
|
|
489
|
+
return;
|
|
490
|
+
}
|
|
491
|
+
const parsed = read.ports;
|
|
492
|
+
const error = await this.#options.onPorts(parsed);
|
|
493
|
+
await this.#render('bridge', response, {
|
|
494
|
+
...(error
|
|
495
|
+
? { error }
|
|
496
|
+
: { notice: `Ports saved: IMAP ${parsed.imapPort}, SMTP ${parsed.smtpPort}.` }),
|
|
497
|
+
});
|
|
498
|
+
}
|
|
499
|
+
async #handleTest(response) {
|
|
500
|
+
if (!this.#options.onTest) {
|
|
501
|
+
await this.#render('bridge', response, { error: 'This server cannot test the connection.' });
|
|
502
|
+
return;
|
|
503
|
+
}
|
|
504
|
+
const result = await this.#options.onTest();
|
|
505
|
+
await this.#render('bridge', response, result.ok ? { notice: result.message } : { error: result.message });
|
|
506
|
+
}
|
|
507
|
+
async #handleUnlock(response, fields) {
|
|
508
|
+
const master = fields.master ?? '';
|
|
509
|
+
if (!master) {
|
|
510
|
+
await this.#render('overview', response, { error: 'The master password is required.' });
|
|
511
|
+
return;
|
|
512
|
+
}
|
|
513
|
+
if (!this.#options.onUnlock) {
|
|
514
|
+
await this.#render('overview', response, {
|
|
515
|
+
error: 'This server cannot open encrypted credential files.',
|
|
516
|
+
});
|
|
517
|
+
return;
|
|
518
|
+
}
|
|
519
|
+
const error = await this.#options.onUnlock(master);
|
|
520
|
+
await this.#render('overview', response, { ...(error ? { error } : {}) });
|
|
521
|
+
}
|
|
522
|
+
/**
|
|
523
|
+
* Reads the storage option out of the form.
|
|
524
|
+
*
|
|
525
|
+
* It arrives as a radio value now rather than being remembered from an
|
|
526
|
+
* earlier post, so it is ordinary untrusted input: a name that is not one of
|
|
527
|
+
* the four, or one that cannot work on this machine, falls back to the
|
|
528
|
+
* recommendation rather than being passed on.
|
|
529
|
+
*/
|
|
530
|
+
async #storeFromForm(fields) {
|
|
531
|
+
const stores = await listStores();
|
|
532
|
+
const wanted = fields.store;
|
|
533
|
+
if (!wanted || !(wanted in STORE_DESCRIPTIONS))
|
|
534
|
+
return { store: recommend(stores) };
|
|
535
|
+
const kind = wanted;
|
|
536
|
+
const entry = stores.find((s) => s.kind === kind);
|
|
537
|
+
if (!entry?.available) {
|
|
538
|
+
return {
|
|
539
|
+
store: recommend(stores),
|
|
540
|
+
error: `${STORE_DESCRIPTIONS[kind].title} cannot be used on this machine. ${entry?.reason ?? ''}`,
|
|
541
|
+
};
|
|
542
|
+
}
|
|
543
|
+
return { store: kind };
|
|
544
|
+
}
|
|
545
|
+
async #handleSignIn(response, fields) {
|
|
546
|
+
const address = (fields.address ?? '').trim();
|
|
547
|
+
const password = fields.password ?? '';
|
|
548
|
+
const { store, error: storeError } = await this.#storeFromForm(fields);
|
|
549
|
+
if (storeError) {
|
|
550
|
+
await this.#render('overview', response, { error: storeError, address, store });
|
|
551
|
+
return;
|
|
552
|
+
}
|
|
553
|
+
// The ports come first, before the credentials are tried. Somebody whose
|
|
554
|
+
// Bridge listens elsewhere would otherwise be told their password is wrong,
|
|
555
|
+
// because the attempt went to a port with nothing behind it. Only when the
|
|
556
|
+
// form carries them: the block is read-only when the environment decided.
|
|
557
|
+
if (this.#options.onPorts && fields.imapPort !== undefined) {
|
|
558
|
+
const read = _a.#readPorts(fields);
|
|
559
|
+
if ('error' in read) {
|
|
560
|
+
await this.#render('overview', response, {
|
|
561
|
+
error: read.error,
|
|
562
|
+
address,
|
|
563
|
+
store,
|
|
564
|
+
showAdvanced: true,
|
|
565
|
+
});
|
|
566
|
+
return;
|
|
567
|
+
}
|
|
568
|
+
const failed = await this.#options.onPorts(read.ports);
|
|
569
|
+
if (failed) {
|
|
570
|
+
await this.#render('overview', response, {
|
|
571
|
+
error: failed,
|
|
572
|
+
address,
|
|
573
|
+
store,
|
|
574
|
+
showAdvanced: true,
|
|
575
|
+
});
|
|
576
|
+
return;
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
if (!address || !password) {
|
|
580
|
+
await this.#render('overview', response, {
|
|
581
|
+
error: 'Both the address and the Bridge password are required.',
|
|
582
|
+
address,
|
|
583
|
+
store,
|
|
584
|
+
});
|
|
585
|
+
return;
|
|
586
|
+
}
|
|
587
|
+
// Checked here rather than in the session, because this is where the second
|
|
588
|
+
// field exists to compare against. A typo in a password nothing can recover
|
|
589
|
+
// has to be caught before it is used to encrypt anything.
|
|
590
|
+
//
|
|
591
|
+
// Read only for the store that uses it. The field is in the document for
|
|
592
|
+
// every option now, hidden by CSS, so a value left behind by switching
|
|
593
|
+
// options would otherwise be taken for a choice.
|
|
594
|
+
let master;
|
|
595
|
+
if (store === 'encrypted-file') {
|
|
596
|
+
master = fields.master ?? '';
|
|
597
|
+
if (!master) {
|
|
598
|
+
await this.#render('overview', response, {
|
|
599
|
+
error: 'The encrypted file needs a master password to encrypt the credentials with.',
|
|
600
|
+
address,
|
|
601
|
+
store,
|
|
602
|
+
});
|
|
603
|
+
return;
|
|
604
|
+
}
|
|
605
|
+
if (master !== (fields.masterRepeat ?? '')) {
|
|
606
|
+
await this.#render('overview', response, {
|
|
607
|
+
error: 'The two master passwords do not match.',
|
|
608
|
+
address,
|
|
609
|
+
store,
|
|
610
|
+
});
|
|
611
|
+
return;
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
const error = await this.#options.onSignIn({ user: address, pass: password }, store, master);
|
|
615
|
+
// No password is kept anywhere in this class, whatever the outcome.
|
|
616
|
+
await this.#render('overview', response, {
|
|
617
|
+
...(error ? { error, address, store } : {}),
|
|
618
|
+
});
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
_a = WebInterface;
|
|
622
|
+
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/web/server.ts"],"names":[],"mappings":"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"}
|