2018-11-16 17:32:29 +00:00
|
|
|
import assets from '../common/assets';
|
|
|
|
import { version } from '../package.json';
|
2018-07-05 19:40:49 +00:00
|
|
|
import Keychain from './keychain';
|
2018-07-11 23:52:46 +00:00
|
|
|
import { downloadStream } from './api';
|
2018-07-19 21:46:12 +00:00
|
|
|
import { transformStream } from './streams';
|
2018-07-26 05:26:11 +00:00
|
|
|
import Zip from './zip';
|
2018-07-19 19:11:55 +00:00
|
|
|
import contentDisposition from 'content-disposition';
|
2018-07-11 23:52:46 +00:00
|
|
|
|
|
|
|
let noSave = false;
|
|
|
|
const map = new Map();
|
2018-07-05 19:40:49 +00:00
|
|
|
|
2018-07-06 22:49:50 +00:00
|
|
|
self.addEventListener('install', event => {
|
2018-11-16 17:32:29 +00:00
|
|
|
event.waitUntil(precache());
|
2018-07-05 19:40:49 +00:00
|
|
|
});
|
|
|
|
|
2018-07-11 23:52:46 +00:00
|
|
|
self.addEventListener('activate', event => {
|
2018-11-16 17:32:29 +00:00
|
|
|
event.waitUntil(self.clients.claim());
|
2018-07-11 23:52:46 +00:00
|
|
|
});
|
|
|
|
|
2018-07-23 22:12:58 +00:00
|
|
|
async function decryptStream(id) {
|
2018-07-25 19:29:19 +00:00
|
|
|
const file = map.get(id);
|
|
|
|
if (!file) {
|
|
|
|
return new Response(null, { status: 400 });
|
|
|
|
}
|
2018-07-11 23:52:46 +00:00
|
|
|
try {
|
2018-07-26 05:26:11 +00:00
|
|
|
let size = file.size;
|
|
|
|
let type = file.type;
|
2018-07-19 20:20:10 +00:00
|
|
|
const keychain = new Keychain(file.key, file.nonce);
|
2018-07-23 16:49:16 +00:00
|
|
|
if (file.requiresPassword) {
|
|
|
|
keychain.setPassword(file.password, file.url);
|
|
|
|
}
|
2018-07-10 00:00:19 +00:00
|
|
|
|
2018-07-18 23:39:14 +00:00
|
|
|
file.download = downloadStream(id, keychain);
|
2018-07-05 19:40:49 +00:00
|
|
|
|
2018-07-18 23:39:14 +00:00
|
|
|
const body = await file.download.result;
|
2018-07-05 19:40:49 +00:00
|
|
|
|
2018-07-23 22:12:58 +00:00
|
|
|
const decrypted = keychain.decryptStream(body);
|
2018-07-26 05:26:11 +00:00
|
|
|
|
|
|
|
let zipStream = null;
|
|
|
|
if (file.type === 'send-archive') {
|
|
|
|
const zip = new Zip(file.manifest, decrypted);
|
|
|
|
zipStream = zip.stream;
|
|
|
|
type = 'application/zip';
|
|
|
|
size = zip.size;
|
|
|
|
}
|
2018-09-05 17:35:36 +00:00
|
|
|
const responseStream = transformStream(
|
2018-07-26 05:26:11 +00:00
|
|
|
zipStream || decrypted,
|
2018-07-23 22:12:58 +00:00
|
|
|
{
|
|
|
|
transform(chunk, controller) {
|
|
|
|
file.progress += chunk.length;
|
|
|
|
controller.enqueue(chunk);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
function oncancel() {
|
|
|
|
// NOTE: cancel doesn't currently fire on chrome
|
|
|
|
// https://bugs.chromium.org/p/chromium/issues/detail?id=638494
|
|
|
|
file.download.cancel();
|
|
|
|
map.delete(id);
|
2018-07-11 23:52:46 +00:00
|
|
|
}
|
2018-07-23 22:12:58 +00:00
|
|
|
);
|
2018-07-09 22:39:06 +00:00
|
|
|
|
2018-07-11 23:52:46 +00:00
|
|
|
const headers = {
|
2018-07-19 19:11:55 +00:00
|
|
|
'Content-Disposition': contentDisposition(file.filename),
|
2018-07-26 05:26:11 +00:00
|
|
|
'Content-Type': type,
|
|
|
|
'Content-Length': size
|
2018-07-11 23:52:46 +00:00
|
|
|
};
|
2018-09-05 17:35:36 +00:00
|
|
|
return new Response(responseStream, { headers });
|
2018-07-11 23:52:46 +00:00
|
|
|
} catch (e) {
|
|
|
|
if (noSave) {
|
|
|
|
return new Response(null, { status: e.message });
|
2018-07-06 22:49:50 +00:00
|
|
|
}
|
2018-07-05 19:40:49 +00:00
|
|
|
|
2018-09-05 17:35:36 +00:00
|
|
|
return new Response(null, {
|
|
|
|
status: 302,
|
|
|
|
headers: {
|
2018-09-07 17:53:40 +00:00
|
|
|
Location: `/download/${id}/#${file.key}`
|
2018-09-05 17:35:36 +00:00
|
|
|
}
|
|
|
|
});
|
2018-07-11 23:52:46 +00:00
|
|
|
}
|
2018-07-05 19:40:49 +00:00
|
|
|
}
|
|
|
|
|
2018-11-16 17:32:29 +00:00
|
|
|
async function precache() {
|
2018-11-16 20:31:36 +00:00
|
|
|
const oldCaches = await caches.keys();
|
|
|
|
for (const c of oldCaches) {
|
|
|
|
if (c !== version) {
|
|
|
|
await caches.delete(c);
|
|
|
|
}
|
|
|
|
}
|
2018-11-16 17:32:29 +00:00
|
|
|
const cache = await caches.open(version);
|
2018-11-16 20:31:36 +00:00
|
|
|
const images = assets.match(/.*\.(png|svg|jpg)$/);
|
|
|
|
await cache.addAll(images);
|
2018-11-16 17:32:29 +00:00
|
|
|
return self.skipWaiting();
|
|
|
|
}
|
|
|
|
|
|
|
|
async function cachedOrFetch(req) {
|
|
|
|
const cache = await caches.open(version);
|
|
|
|
const cached = await cache.match(req);
|
|
|
|
return cached || fetch(req);
|
|
|
|
}
|
|
|
|
|
2018-07-06 22:49:50 +00:00
|
|
|
self.onfetch = event => {
|
2018-07-23 22:12:58 +00:00
|
|
|
const req = event.request;
|
2018-09-05 17:35:36 +00:00
|
|
|
const match = /\/api\/download\/([A-Fa-f0-9]{4,})/.exec(req.url);
|
|
|
|
if (match) {
|
|
|
|
event.respondWith(decryptStream(match[1]));
|
2018-11-16 17:32:29 +00:00
|
|
|
} else {
|
|
|
|
event.respondWith(cachedOrFetch(req));
|
2018-07-05 19:40:49 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2018-07-06 22:49:50 +00:00
|
|
|
self.onmessage = event => {
|
2018-07-11 23:52:46 +00:00
|
|
|
if (event.data.request === 'init') {
|
|
|
|
noSave = event.data.noSave;
|
|
|
|
const info = {
|
2018-07-18 23:39:14 +00:00
|
|
|
key: event.data.key,
|
2018-07-19 20:20:10 +00:00
|
|
|
nonce: event.data.nonce,
|
2018-07-11 23:52:46 +00:00
|
|
|
filename: event.data.filename,
|
2018-07-23 16:49:16 +00:00
|
|
|
requiresPassword: event.data.requiresPassword,
|
|
|
|
password: event.data.password,
|
|
|
|
url: event.data.url,
|
2018-07-13 18:13:09 +00:00
|
|
|
type: event.data.type,
|
2018-07-26 05:26:11 +00:00
|
|
|
manifest: event.data.manifest,
|
2018-07-12 22:32:07 +00:00
|
|
|
size: event.data.size,
|
2018-07-23 22:12:58 +00:00
|
|
|
progress: 0
|
2018-07-11 23:52:46 +00:00
|
|
|
};
|
|
|
|
map.set(event.data.id, info);
|
|
|
|
|
2018-07-10 00:00:19 +00:00
|
|
|
event.ports[0].postMessage('file info received');
|
2018-07-11 23:52:46 +00:00
|
|
|
} else if (event.data.request === 'progress') {
|
|
|
|
const file = map.get(event.data.id);
|
2018-07-18 23:39:14 +00:00
|
|
|
if (!file) {
|
2018-07-10 00:00:19 +00:00
|
|
|
event.ports[0].postMessage({ error: 'cancelled' });
|
2018-07-09 22:39:06 +00:00
|
|
|
} else {
|
2018-07-23 22:12:58 +00:00
|
|
|
if (file.progress === file.size) {
|
|
|
|
map.delete(event.data.id);
|
|
|
|
}
|
2018-07-11 23:52:46 +00:00
|
|
|
event.ports[0].postMessage({ progress: file.progress });
|
2018-07-09 22:39:06 +00:00
|
|
|
}
|
2018-07-11 23:52:46 +00:00
|
|
|
} else if (event.data.request === 'cancel') {
|
|
|
|
const file = map.get(event.data.id);
|
2018-07-18 23:39:14 +00:00
|
|
|
if (file) {
|
|
|
|
if (file.download) {
|
|
|
|
file.download.cancel();
|
|
|
|
}
|
2018-07-23 22:12:58 +00:00
|
|
|
map.delete(event.data.id);
|
2018-07-09 22:39:06 +00:00
|
|
|
}
|
2018-07-10 00:00:19 +00:00
|
|
|
event.ports[0].postMessage('download cancelled');
|
2018-07-09 22:39:06 +00:00
|
|
|
}
|
2018-07-06 22:49:50 +00:00
|
|
|
};
|