Compare commits

...

61 Commits

Author SHA1 Message Date
Danny Coates
ea1ea2aa68 v3.0.19 2019-09-23 09:24:10 -07:00
Danny Coates
90d06d71d3 updated deps 2019-09-23 09:22:22 -07:00
Kim Younggeon
11f2deb7a6 Pontoon: Update Korean (ko) localization of Firefox Send
Localization authors:
- Kim Younggeon <mail@kyg.kr>
2019-09-23 03:52:14 +00:00
Danny Coates
0f5edf023a Merge pull request #1400 from mozilla/task/update-engines
task: update engines in package.json and README to clarify that node 10x is expected.
2019-09-19 11:02:42 -07:00
Rachel Tublitz
3be0621647 task: update engines in package.json and README to clarify that node 10.x is expected 2019-09-19 13:37:05 -04:00
Marcelo Ghelman
fc07bfdb85 Pontoon: Update Portuguese (Brazil) (pt-BR) localization of Firefox Send
Localization authors:
- Marcelo Ghelman <marcelo.ghelman@gmail.com>
2019-09-15 05:17:20 +00:00
صفا الفليج
581a989304 Pontoon: Update Arabic (ar) localization of Firefox Send
Localization authors:
- صفا الفليج <safa1996alfulaij@gmail.com>
2019-09-11 18:34:38 +00:00
ItielMaN
047d6a85ab Pontoon: Update Hebrew (he) localization of Firefox Send
Localization authors:
- ItielMaN <itiel_yn8@walla.com>
2019-09-08 21:12:53 +00:00
Luiz Felipe F M Costa
90f6a07d4a refactored Dockerfile 2019-09-06 20:04:45 -07:00
Arash Mousavi
8f4a53db64 Pontoon: Update Persian (fa) localization of Firefox Send
Localization authors:
- Arash Mousavi <mousavi.arash@gmail.com>
2019-09-06 21:53:28 +00:00
Danny Coates
c2dd51c638 v3.0.18 2019-09-06 10:40:36 -07:00
Danny Coates
cdd98af86a updated deps 2019-09-06 10:39:42 -07:00
Danny Coates
883f2bc0f9 wait until serviceWorker activate to precache 2019-09-05 19:04:03 -07:00
Danny Coates
94f1eabbc7 v3.0.17 2019-09-05 15:59:42 -07:00
Danny Coates
902bc6628e cache fonts 2019-09-05 15:57:07 -07:00
Danny Coates
460b741f17 clean service worker cache after activate instead of on install 2019-09-05 15:24:26 -07:00
Danny Coates
d5c488196d no-cache harder 2019-09-05 13:33:12 -07:00
Danny Coates
9ad9c9feb2 fixed git hooks 2019-09-05 13:16:32 -07:00
Danny Coates
6576e4a74c added sync-npm-dependencies script and hooks 2019-09-05 08:14:14 -07:00
Danny Coates
950872109e updated deps 2019-09-05 08:11:20 -07:00
Martijn Dekker
87051d27ed Pontoon: Update Interlingua (ia) localization of Firefox Send
Localization authors:
- Martijn Dekker <martijn@inlv.org>
2019-09-03 22:52:26 +00:00
Marcelo Ghelman
3451803b37 Pontoon: Update Portuguese (Brazil) (pt-BR) localization of Firefox Send
Localization authors:
- Marcelo Ghelman <marcelo.ghelman@gmail.com>
2019-08-29 10:32:02 +00:00
dskmori
ac15153e8f Pontoon: Update Japanese (ja) localization of Firefox Send
Localization authors:
- dskmori <ghoti.fish.dsk@gmail.com>
2019-08-28 13:12:29 +00:00
Danny Coates
924f5dc682 disable streaming download on mobile firefox 2019-08-26 10:49:19 -07:00
Danny Coates
ff9be6a213 updated deps 2019-08-26 08:58:34 -07:00
Roberto Alvarado
883728570e Pontoon: Update Spanish (Mexico) (es-MX) localization of Firefox Send
Localization authors:
- Roberto Alvarado <ralv888@gmail.com>
- Adolfo Jayme Barrientos <fitoschido@gmail.com>
2019-08-25 05:52:41 +00:00
Adolfo Jayme Barrientos
0435f17f9a Pontoon: Update Spanish (Mexico) (es-MX) localization of Firefox Send
Localization authors:
- Roberto Alvarado <ralv888@gmail.com>
- Adolfo Jayme Barrientos <fitoschido@gmail.com>
2019-08-20 16:34:33 +00:00
Danny Coates
1e1268fff0 fixed hebrew fluent variable name 2019-08-19 10:59:35 -07:00
Quế Tùng
252d7817e3 Pontoon: Update Vietnamese (vi) localization of Firefox Send
Localization authors:
- Quế Tùng <best.cloney.1301@gmail.com>
2019-08-17 15:53:31 +00:00
Danny Coates
ce28c38ebe v3.0.16 2019-08-12 10:10:07 -07:00
Danny Coates
f0407f9beb use custom configstore that doesn't use the fs 2019-08-12 10:00:57 -07:00
Sahithi
c6f222eb57 Pontoon: Update Telugu (te) localization of Firefox Send
Localization authors:
- Sahithi <sahithi@swecha.net>
2019-08-11 08:54:01 +00:00
leo.toneff
6dd6135185 Pontoon: Update Norwegian Bokmål (nb-NO) localization of Firefox Send
Localization authors:
- leo.toneff <leo.toneff@gmail.com>
- Håvar Henriksen <havar@firefox.no>
2019-08-10 16:33:36 +00:00
Danny Coates
8df339b66d v3.0.15 2019-08-09 12:26:09 -07:00
Danny Coates
8702fda651 handle websocket construction exceptions 2019-08-09 11:47:44 -07:00
Danny Coates
807ecff471 updated sentry libs from raven to @sentry 2019-08-09 11:06:21 -07:00
Danny Coates
927c981cd7 added size/time info to up/download error reporting 2019-08-09 10:09:34 -07:00
Danny Coates
7073cc8ce6 added download hang detection and error reporting 2019-08-08 13:54:02 -07:00
Danny Coates
c925fae696 updated deps 2019-08-08 13:01:57 -07:00
Danny Coates
966d7a5e35 create configstore dir in docker container 2019-08-08 10:42:57 -07:00
Danny Coates
96c750c098 added size and duration to connection error reporting 2019-08-08 09:52:22 -07:00
Danny Coates
0729064753 Merge pull request #1378 from mozilla/new-ws
Use resumable uploads to GCS
2019-08-08 08:54:00 -07:00
Joergen
259a5a5f24 Pontoon: Update Danish (da) localization of Firefox Send
Localization authors:
- Joergen <joergenr@stofanet.dk>
2019-08-08 14:02:32 +00:00
Danny Coates
27be72e0cd refactored client side upload loop 2019-08-07 13:51:23 -07:00
Danny Coates
e4231bbc0f updated deps 2019-08-07 13:47:26 -07:00
Danny Coates
1d184f06bf revert webpack for broken css extraction 2019-08-07 12:14:27 -07:00
Quentí
f7b46a99ac Pontoon: Update Occitan (oc) localization of Firefox Send
Localization authors:
- Quentí <quentinantonin@free.fr>
2019-08-07 18:12:20 +00:00
Quentí
3fadb489c7 Pontoon: Update Occitan (oc) localization of Firefox Send
Localization authors:
- Quentí <quentinantonin@free.fr>
2019-08-07 17:13:10 +00:00
Danny Coates
6378676c2d use resumable uploads to GCS 2019-08-07 10:10:42 -07:00
Quentí
014d84e4c7 Pontoon: Update Occitan (oc) localization of Firefox Send
Localization authors:
- Quentí <quentinantonin@free.fr>
2019-08-07 16:54:33 +00:00
Quentí
a08d8435a9 Pontoon: Update Occitan (oc) localization of Firefox Send
Localization authors:
- Quentí <quentinantonin@free.fr>
2019-08-07 07:12:56 +00:00
Quentí
40a05c9ecf Pontoon: Update Occitan (oc) localization of Firefox Send
Localization authors:
- Quentí <quentinantonin@free.fr>
2019-08-07 06:52:27 +00:00
Danny Coates
527040afef updated ws dependency and slightly improved client side error handling, hung uploads will error instead of hang forever 2019-08-06 14:47:21 -07:00
Michal Stanke
a48a447808 Pontoon: Update Czech (cs) localization of Firefox Send
Localization authors:
- Michal Stanke <mstanke@mozilla.cz>
2019-08-06 12:14:20 +00:00
julen
f3569d7f98 Pontoon: Update Basque (eu) localization of Firefox Send
Localization authors:
- julen <julenx@gmail.com>
- Ander Elortondo <ander.elor@gmail.com>
2019-08-05 08:52:40 +00:00
صفا الفليج
6ca7d11efb Pontoon: Update Arabic (ar) localization of Firefox Send
Localization authors:
- صفا الفليج <safa1996alfulaij@gmail.com>
2019-08-05 08:52:37 +00:00
julen
b71ae4a0ff Pontoon: Update Basque (eu) localization of Firefox Send
Localization authors:
- p.sanroman.bengoetxea <p.sanroman.bengoetxea@gmail.com>
- julen <julenx@gmail.com>
- Ander Elortondo <ander.elor@gmail.com>
2019-08-05 08:34:11 +00:00
Aman Alam
7ba25664b5 Pontoon: Update Punjabi (pa-IN) localization of Firefox Send
Localization authors:
- Aman Alam <amanpreet.alam@gmail.com>
2019-08-05 00:52:41 +00:00
Danny Coates
80fb42ad3d v3.0.14 2019-08-02 12:13:39 -07:00
Danny Coates
f036df5f47 updated eslint config 2019-08-02 12:03:53 -07:00
Danny Coates
20c063db7c fixed logged error in integration tests 2019-08-02 11:26:52 -07:00
53 changed files with 2026 additions and 1274 deletions

View File

@@ -1,10 +1,8 @@
node_modules
.git
.tox
.DS_Store
firefox
assets
docs
test
coverage
.circleci
.nyc_output
.vscode
.DS_Store
coverage
docs
firefox
node_modules

View File

@@ -2,5 +2,6 @@ dist
assets
firefox
coverage
android/app/build
app/locale.js
app/capabilities.js

View File

@@ -4,6 +4,7 @@ env:
extends:
- eslint:recommended
- prettier
- plugin:node/recommended
- plugin:security/recommended
@@ -22,13 +23,5 @@ rules:
security/detect-non-literal-fs-filename: off
security/detect-object-injection: off
eol-last: [error, always]
eqeqeq: error
no-alert: warn
no-console: warn
no-path-concat: error
no-unused-vars: [error, {argsIgnorePattern: "^_|err|event|next|reject"}]
no-var: error
one-var: [error, never]
prefer-const: error
quotes: [error, single, {avoidEscape: true}]
require-atomic-updates: warn

View File

@@ -1,18 +1,59 @@
FROM node:10 AS builder
RUN addgroup --gid 10001 app && adduser --disabled-password --gecos '' --gid 10001 --home /app --uid 10001 app
COPY package*.json /app/
WORKDIR /app
RUN npm install --production
##
# Firefox Send - Mozilla
#
# License https://github.com/mozilla/send/blob/master/LICENSE
##
FROM node:10-slim
RUN addgroup --gid 10001 app && adduser --disabled-password --gecos '' --gid 10001 --home /app --uid 10001 app
# Build project
FROM node:10 AS builder
RUN set -x \
# Add user
&& addgroup --gid 10001 app \
&& adduser --disabled-password \
--gecos '' \
--gid 10001 \
--home /app \
--uid 10001 \
app
COPY --chown=app:app . /app
USER app
WORKDIR /app
COPY --chown=app:app --from=builder /app .
COPY --chown=app:app . .
RUN ls -la
RUN set -x \
# Build
&& npm ci \
&& npm run build
# Main image
FROM node:10-slim
RUN set -x \
# Add user
&& addgroup --gid 10001 app \
&& adduser --disabled-password \
--gecos '' \
--gid 10001 \
--home /app \
--uid 10001 \
app
RUN apt-get update && apt-get -y install git-core
USER app
WORKDIR /app
COPY --chown=app:app package*.json ./
COPY --chown=app:app app app
COPY --chown=app:app common common
COPY --chown=app:app public/locales public/locales
COPY --chown=app:app server server
COPY --chown=app:app --from=builder /app/dist dist
RUN ls -la
RUN npm ci --production && npm cache clean --force
RUN mkdir -p /app/.config/configstore
RUN ln -s dist/version.json version.json
ENV PORT=1443
EXPOSE $PORT
EXPOSE ${PORT}
CMD ["node", "server/bin/prod.js"]

View File

@@ -30,7 +30,7 @@ A file sharing experiment which allows you to send encrypted files to other user
## Requirements
- [Node.js 10.0+](https://nodejs.org/)
- [Node.js 10.x](https://nodejs.org/)
- [Redis server](https://redis.io/) (optional for development)
- [AWS S3](https://aws.amazon.com/s3/) or compatible service. (optional)

View File

@@ -1,7 +1,7 @@
import 'intl-pluralrules';
import choo from 'choo';
import html from 'choo/html';
import Raven from 'raven-js';
import * as Sentry from '@sentry/browser';
import { setApiUrlPrefix, getConstants } from '../app/api';
import metrics from '../app/metrics';
@@ -82,7 +82,7 @@ function body(main) {
state.archive = new Archive([], DEFAULTS.EXPIRE_SECONDS);
state.storage = storage;
state.user = new User(storage, LIMITS);
state.raven = Raven;
state.sentry = Sentry;
});
app.use(metrics);
app.route('/', body(home));

View File

@@ -12,7 +12,7 @@ export default function initialState(state, emitter) {
getAsset(name) {
return `${state.prefix}/${name}`;
},
raven: {
sentry: {
captureException: e => {
console.error('ERROR ' + e + ' ' + e.stack);
}

View File

@@ -11,6 +11,15 @@ if (!fileProtocolWssUrl) {
fileProtocolWssUrl = 'wss://send.firefox.com/api/ws';
}
export class ConnectionError extends Error {
constructor(cancelled, duration, size) {
super(cancelled ? '0' : 'connection closed');
this.cancelled = cancelled;
this.duration = duration;
this.size = size;
}
}
export function setFileProtocolWssUrl(url) {
localStorage && localStorage.setItem('wssURL', url);
fileProtocolWssUrl = url;
@@ -137,17 +146,25 @@ export async function setPassword(id, owner_token, keychain) {
}
function asyncInitWebSocket(server) {
return new Promise(resolve => {
const ws = new WebSocket(server);
ws.onopen = () => {
resolve(ws);
};
return new Promise((resolve, reject) => {
try {
const ws = new WebSocket(server);
ws.addEventListener('open', () => resolve(ws), { once: true });
} catch (e) {
reject(new ConnectionError(false));
}
});
}
function listenForResponse(ws, canceller) {
return new Promise((resolve, reject) => {
function handleClose(event) {
// a 'close' event before a 'message' event means the request failed
ws.removeEventListener('message', handleMessage);
reject(new ConnectionError(canceller.cancelled));
}
function handleMessage(msg) {
ws.removeEventListener('close', handleClose);
try {
const response = JSON.parse(msg.data);
if (response.error) {
@@ -156,13 +173,11 @@ function listenForResponse(ws, canceller) {
resolve(response);
}
} catch (e) {
ws.close();
canceller.cancelled = true;
canceller.error = e;
reject(e);
}
}
ws.addEventListener('message', handleMessage, { once: true });
ws.addEventListener('close', handleClose, { once: true });
});
}
@@ -176,6 +191,8 @@ async function upload(
onprogress,
canceller
) {
let size = 0;
const start = Date.now();
const host = window.location.hostname;
const port = window.location.port;
const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
@@ -203,31 +220,41 @@ async function upload(
const reader = stream.getReader();
let state = await reader.read();
let size = 0;
while (!state.done) {
const buf = state.value;
if (canceller.cancelled) {
throw canceller.error;
ws.close();
}
if (ws.readyState !== WebSocket.OPEN) {
break;
}
const buf = state.value;
ws.send(buf);
onprogress(size);
size += buf.length;
state = await reader.read();
while (ws.bufferedAmount > ECE_RECORD_SIZE * 2) {
while (
ws.bufferedAmount > ECE_RECORD_SIZE * 2 &&
ws.readyState === WebSocket.OPEN &&
!canceller.cancelled
) {
await delay();
}
}
const footer = new Uint8Array([0]);
ws.send(footer);
if (ws.readyState === WebSocket.OPEN) {
ws.send(new Uint8Array([0])); //EOF
}
await completedResponse;
ws.close();
uploadInfo.duration = Date.now() - start;
return uploadInfo;
} catch (e) {
ws.close(4000);
e.size = size;
e.duration = Date.now() - start;
throw e;
} finally {
if (![WebSocket.CLOSED, WebSocket.CLOSING].includes(ws.readyState)) {
ws.close();
}
}
}
@@ -244,7 +271,6 @@ export function uploadWs(
return {
cancel: function() {
canceller.error = new Error(0);
canceller.cancelled = true;
},
@@ -284,7 +310,7 @@ async function downloadS(id, keychain, signal) {
return response.body;
}
async function tryDownloadStream(id, keychain, signal, tries = 1) {
async function tryDownloadStream(id, keychain, signal, tries = 2) {
try {
const result = await downloadS(id, keychain, signal);
return result;
@@ -306,18 +332,19 @@ export function downloadStream(id, keychain) {
}
return {
cancel,
result: tryDownloadStream(id, keychain, controller.signal, 2)
result: tryDownloadStream(id, keychain, controller.signal)
};
}
//////////////////
function download(id, keychain, onprogress, canceller) {
async function download(id, keychain, onprogress, canceller) {
const auth = await keychain.authHeader();
const xhr = new XMLHttpRequest();
canceller.oncancel = function() {
xhr.abort();
};
return new Promise(async function(resolve, reject) {
return new Promise(function(resolve, reject) {
xhr.addEventListener('loadend', function() {
canceller.oncancel = function() {};
const authHeader = xhr.getResponseHeader('WWW-Authenticate');
@@ -337,7 +364,6 @@ function download(id, keychain, onprogress, canceller) {
onprogress(event.loaded);
}
});
const auth = await keychain.authHeader();
xhr.open('get', getApiUrl(`/api/download/blob/${id}`));
xhr.setRequestHeader('Authorization', auth);
xhr.responseType = 'blob';
@@ -346,7 +372,7 @@ function download(id, keychain, onprogress, canceller) {
});
}
async function tryDownload(id, keychain, onprogress, canceller, tries = 1) {
async function tryDownload(id, keychain, onprogress, canceller, tries = 2) {
try {
const result = await download(id, keychain, onprogress, canceller);
return result;
@@ -367,7 +393,7 @@ export function downloadFile(id, keychain, onprogress) {
}
return {
cancel,
result: tryDownload(id, keychain, onprogress, canceller, 2)
result: tryDownload(id, keychain, onprogress, canceller)
};
}

View File

@@ -76,8 +76,8 @@ async function polyfillStreams() {
}
export default async function getCapabilities() {
const serviceWorker =
'serviceWorker' in navigator && browserName() !== 'edge';
const browser = browserName();
const serviceWorker = 'serviceWorker' in navigator && browser !== 'edge';
let crypto = await checkCrypto();
const nativeStreams = checkStreams();
let polyStreams = false;
@@ -97,13 +97,16 @@ export default async function getCapabilities() {
window.matchMedia('(display-mode: standalone)').matches ||
navigator.standalone;
const mobileFirefox =
browser === 'firefox' && /mobile/i.test(navigator.userAgent);
return {
account,
crypto,
serviceWorker,
streamUpload: nativeStreams || polyStreams,
streamDownload:
nativeStreams && serviceWorker && browserName() !== 'safari',
nativeStreams && serviceWorker && browser !== 'safari' && !mobileFirefox,
multifile: nativeStreams || polyStreams,
share,
standalone

View File

@@ -76,7 +76,7 @@ export default function(state, emitter) {
state.storage.remove(ownedFile.id);
await ownedFile.del();
} catch (e) {
state.raven.captureException(e);
state.sentry.captureException(e);
}
render();
});
@@ -176,14 +176,17 @@ export default function(state, emitter) {
} catch (err) {
if (err.message === '0') {
//cancelled. do nothing
const duration = Date.now() - start;
metrics.cancelledUpload(archive, duration);
metrics.cancelledUpload(archive, err.duration);
render();
} else {
// eslint-disable-next-line no-console
console.error(err);
state.raven.captureException(err);
metrics.stoppedUpload(archive);
state.sentry.withScope(scope => {
scope.setExtra('duration', err.duration);
scope.setExtra('size', err.size);
state.sentry.captureException(err);
});
metrics.stoppedUpload(archive, err.duration);
emitter.emit('pushState', '/error');
}
} finally {
@@ -262,7 +265,12 @@ export default function(state, emitter) {
state.transfer = null;
const location = err.message === '404' ? '/404' : '/error';
if (location === '/error') {
state.raven.captureException(err);
state.sentry.withScope(scope => {
scope.setExtra('duration', err.duration);
scope.setExtra('size', err.size);
scope.setExtra('progress', err.progress);
state.sentry.captureException(err);
});
const duration = Date.now() - start;
metrics.stoppedDownload({
size,

View File

@@ -112,6 +112,7 @@ export default class FileReceiver extends Nanobus {
}
async downloadStream(noSave = false) {
const start = Date.now();
const onprogress = p => {
this.progress = [p, this.fileInfo.size];
this.emit('progress');
@@ -153,7 +154,7 @@ export default class FileReceiver extends Nanobus {
const downloadPath = `/api/download/${this.fileInfo.id}`;
let downloadUrl = getApiUrl(downloadPath);
if (downloadUrl === downloadPath) {
downloadUrl = `${location.protocol}//${location.host}/api/download/${this.fileInfo.id}`;
downloadUrl = `${location.protocol}//${location.host}${downloadPath}`;
}
const a = document.createElement('a');
a.href = downloadUrl;
@@ -162,11 +163,29 @@ export default class FileReceiver extends Nanobus {
}
let prog = 0;
let hangs = 0;
while (prog < this.fileInfo.size) {
const msg = await this.sendMessageToSw({
request: 'progress',
id: this.fileInfo.id
});
if (msg.progress === prog) {
hangs++;
} else {
hangs = 0;
}
if (hangs > 30) {
// TODO: On Chrome we don't get a cancel
// signal so one is indistinguishable from
// a hang. We may be able to detect
// which end is hung in the service worker
// to improve on this.
const e = new Error('hung download');
e.duration = Date.now() - start;
e.size = this.fileInfo.size;
e.progress = prog;
throw e;
}
prog = msg.progress;
onprogress(prog);
await delay(1000);

View File

@@ -44,7 +44,6 @@ export default class FileSender extends Nanobus {
}
async upload(archive, bearerToken) {
const start = Date.now();
if (this.cancelled) {
throw new Error(0);
}
@@ -76,7 +75,6 @@ export default class FileSender extends Nanobus {
this.emit('progress'); // HACK to kick MS Edge
try {
const result = await this.uploadRequest.result;
const time = Date.now() - start;
this.msg = 'notifyUploadEncryptDone';
this.uploadRequest = null;
this.progress = [1, 1];
@@ -87,8 +85,8 @@ export default class FileSender extends Nanobus {
name: archive.name,
size: archive.size,
manifest: archive.manifest,
time: time,
speed: archive.size / (time / 1000),
time: result.duration,
speed: archive.size / (result.duration / 1000),
createdAt: Date.now(),
expiresAt: Date.now() + archive.timeLimit * 1000,
secretKey: secretKey,

View File

@@ -12,15 +12,15 @@ import pasteManager from './pasteManager';
import storage from './storage';
import metrics from './metrics';
import experiments from './experiments';
import Raven from 'raven-js';
import * as Sentry from '@sentry/browser';
import './main.css';
import User from './user';
import { getTranslator } from './locale';
import Archive from './archive';
import { setTranslate, locale } from './utils';
if (navigator.doNotTrack !== '1' && window.RAVEN_CONFIG) {
Raven.config(window.SENTRY_ID, window.RAVEN_CONFIG).install();
if (navigator.doNotTrack !== '1' && window.SENTRY_CONFIG) {
Sentry.init(window.SENTRY_CONFIG);
}
if (process.env.NODE_ENV === 'production') {
@@ -56,13 +56,13 @@ if (process.env.NODE_ENV === 'production') {
capabilities,
translate,
storage,
raven: Raven,
sentry: Sentry,
user: new User(storage, LIMITS, window.AUTH_CONFIG),
transfer: null,
fileInfo: null
};
const app = routes(choo());
const app = routes(choo({ hash: true }));
// eslint-disable-next-line require-atomic-updates
window.app = app;
app.use(experiments);

View File

@@ -107,9 +107,10 @@ function completedUpload(archive, duration) {
});
}
function stoppedUpload(archive) {
function stoppedUpload(archive, duration = 0) {
return addEvent('client_upload', {
download_limit: archive.dlimit,
duration: sizeOrder(duration),
file_count: archive.numFiles,
password_protected: !!archive.password,
size: sizeOrder(archive.size),

View File

@@ -2,7 +2,7 @@ const choo = require('choo');
const download = require('./ui/download');
const body = require('./ui/body');
module.exports = function(app = choo()) {
module.exports = function(app = choo({ hash: true })) {
app.route('/', body(require('./ui/home')));
app.route('/download/:id', body(download));
app.route('/download/:id/:key', body(download));

View File

@@ -11,13 +11,14 @@ const map = new Map();
const IMAGES = /.*\.(png|svg|jpg)$/;
const VERSIONED_ASSET = /\.[A-Fa-f0-9]{8}\.(js|css|png|svg|jpg)$/;
const DOWNLOAD_URL = /\/api\/download\/([A-Fa-f0-9]{4,})/;
const FONT = /\.woff2?$/;
self.addEventListener('install', event => {
event.waitUntil(precache());
self.addEventListener('install', () => {
self.skipWaiting();
});
self.addEventListener('activate', event => {
event.waitUntil(self.clients.claim());
event.waitUntil(self.clients.claim().then(precache));
});
async function decryptStream(id) {
@@ -83,16 +84,28 @@ async function decryptStream(id) {
}
async function precache() {
try {
await cleanCache();
const cache = await caches.open(version);
const images = assets.match(IMAGES);
await cache.addAll(images);
} catch (e) {
console.error(e);
// cache will get populated on demand
}
}
async function cleanCache() {
const oldCaches = await caches.keys();
for (const c of oldCaches) {
if (c !== version) {
await caches.delete(c);
}
}
const cache = await caches.open(version);
const images = assets.match(IMAGES);
await cache.addAll(images);
return self.skipWaiting();
}
function cacheable(url) {
return VERSIONED_ASSET.test(url) || FONT.test(url);
}
async function cachedOrFetched(req) {
@@ -102,7 +115,7 @@ async function cachedOrFetched(req) {
return cached;
}
const fetched = await fetch(req);
if (fetched.ok && VERSIONED_ASSET.test(req.url)) {
if (fetched.ok && cacheable(req.url)) {
cache.put(req, fetched.clone());
}
return fetched;
@@ -115,7 +128,7 @@ self.onfetch = event => {
const dlmatch = DOWNLOAD_URL.exec(url.pathname);
if (dlmatch) {
event.respondWith(decryptStream(dlmatch[1]));
} else if (VERSIONED_ASSET.test(url.pathname)) {
} else if (cacheable(url.pathname)) {
event.respondWith(cachedOrFetched(req));
}
};

View File

@@ -1,4 +1,4 @@
/* global ReadableStream TransformStream */
/* global TransformStream */
export function transformStream(readable, transformer, oncancel) {
try {

View File

@@ -1,8 +1,8 @@
# Custom Loaders
## Generate Asset Map
## Android Index Plugin
This loader enumerates all the files in `assets/` so that `common/assets.js` can provide mappings from the source filename to the hashed filename used on the site.
Generates the `index.html` page for the native android client
## Version Plugin

View File

@@ -1,4 +1,4 @@
const genmap = require('../build/generate_asset_map');
const genmap = require('./generate_asset_map');
const isServer = typeof genmap === 'function';
let prefix = '';
let manifest = {};

View File

@@ -1,3 +1,7 @@
# Common Code
This directory contains code loaded by both the frontend `app` and backend `server`. The code here can be challenging to understand at first because the contexts for the two (three counting the dev server) environments that include them are quite different, but the purpose of these modules are quite simple, to provide mappings from the source assets (`copy-16.png`) to the concrete production assets (`copy-16.db66e0bf.svg`).
This directory contains code loaded by both the frontend `app` and backend `server`. The code here can be challenging to understand at first because the contexts for the two (three counting the dev server) environments that include them are quite different, but the purpose of these modules are quite simple, to provide mappings from the source assets (`copy-16.png`) to the concrete production assets (`copy-16.db66e0bf.svg`).
## Generate Asset Map
This loader enumerates all the files in `assets/` so that `common/assets.js` can provide mappings from the source filename to the hashed filename used on the site.

View File

@@ -1,13 +1,6 @@
## Setup
Before building the Docker image, you must build the production assets:
```sh
npm run build
```
Then you can run either `docker build` or `docker-compose up`.
Run `docker build -t send:latest .` to create an image or `docker-compose up` to run a full testable stack. *We don't recommend using docker-compose for production.*
## Environment variables:

2478
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,7 @@
{
"name": "firefox-send",
"description": "File Sharing Experiment",
"version": "3.0.13",
"version": "3.0.19",
"author": "Mozilla (https://mozilla.org)",
"repository": "mozilla/send",
"homepage": "https://github.com/mozilla/send/",
@@ -35,7 +35,8 @@
"hooks": {
"pre-commit": "lint-staged",
"pre-push": "npm test",
"post-merge": "npm install"
"post-merge": "npm install",
"post-checkout": "scripts/sync-npm-dependencies.sh"
}
},
"lint-staged": {
@@ -57,68 +58,69 @@
"cache": true
},
"engines": {
"node": ">=10.0.0"
"node": ">=10.0.0 <11.0.0"
},
"devDependencies": {
"@babel/core": "^7.5.5",
"@babel/core": "^7.6.0",
"@babel/plugin-proposal-class-properties": "^7.5.5",
"@babel/plugin-syntax-dynamic-import": "^7.2.0",
"@babel/preset-env": "^7.5.5",
"@babel/preset-env": "^7.6.0",
"@dannycoates/webcrypto-liner": "^0.1.37",
"@fullhuman/postcss-purgecss": "^1.2.0",
"@fullhuman/postcss-purgecss": "^1.3.0",
"@mattiasbuelens/web-streams-polyfill": "0.2.1",
"@sentry/browser": "^5.6.3",
"asmcrypto.js": "^0.22.0",
"babel-loader": "^8.0.6",
"babel-plugin-istanbul": "^5.2.0",
"base64-js": "^1.3.0",
"base64-js": "^1.3.1",
"content-disposition": "^0.5.3",
"copy-webpack-plugin": "^5.0.4",
"core-js": "^3.1.4",
"core-js": "^3.2.1",
"crc": "^3.8.0",
"cross-env": "^5.2.0",
"css-loader": "^3.1.0",
"cross-env": "^5.2.1",
"css-loader": "^3.2.0",
"css-mqpacker": "^7.0.0",
"cssnano": "^4.1.10",
"eslint": "^6.1.0",
"eslint-plugin-mocha": "^6.0.0",
"eslint-plugin-node": "^9.1.0",
"eslint": "^6.4.0",
"eslint-config-prettier": "^6.3.0",
"eslint-plugin-mocha": "^6.1.1",
"eslint-plugin-node": "^9.2.0",
"eslint-plugin-security": "^1.4.0",
"expose-loader": "^0.7.5",
"extract-loader": "^3.1.0",
"extract-text-webpack-plugin": "^4.0.0-beta.0",
"fast-text-encoding": "^1.0.0",
"file-loader": "^4.1.0",
"file-loader": "^4.2.0",
"git-rev-sync": "^1.12.0",
"html-loader": "^0.5.5",
"http_ece": "^1.1.0",
"husky": "^3.0.2",
"husky": "^3.0.5",
"intl-pluralrules": "^1.0.3",
"lint-staged": "^9.2.1",
"lint-staged": "^9.3.0",
"mocha": "^6.2.0",
"morgan": "^1.9.1",
"nanobus": "^4.4.0",
"nanohtml": "^1.6.3",
"nanohtml": "^1.8.1",
"nanotiming": "^7.3.1",
"npm-run-all": "^4.1.5",
"nyc": "^14.1.1",
"postcss-loader": "^3.0.0",
"postcss-preset-env": "^6.7.0",
"prettier": "^1.18.2",
"proxyquire": "^2.1.1",
"puppeteer": "^1.19.0",
"raven-js": "^3.27.2",
"proxyquire": "^2.1.3",
"puppeteer": "^1.20.0",
"raw-loader": "^3.1.0",
"redis-mock": "^0.45.0",
"rimraf": "^2.6.3",
"redis-mock": "^0.46.0",
"rimraf": "^3.0.0",
"script-loader": "^0.7.2",
"sinon": "^7.3.2",
"sinon": "^7.5.0",
"string-hash": "^1.1.3",
"stylelint": "^10.1.0",
"stylelint-config-standard": "^18.3.0",
"stylelint-no-unsupported-browser-features": "^3.0.2",
"svgo": "^1.3.0",
"svgo-loader": "^2.2.1",
"tailwindcss": "^1.0.5",
"tailwindcss": "^1.1.2",
"val-loader": "^1.1.1",
"wdio-docker-service": "^1.4.2",
"wdio-dot-reporter": "0.0.10",
@@ -128,34 +130,34 @@
"wdio-selenium-standalone-service": "0.0.12",
"wdio-spec-reporter": "^0.1.5",
"webdriverio": "^4.14.4",
"webpack": "^4.38.0",
"webpack-cli": "^3.3.6",
"webpack-dev-middleware": "^3.7.0",
"webpack-dev-server": "^3.7.2",
"webpack-manifest-plugin": "^2.0.4",
"webpack": "4.38.0",
"webpack-cli": "^3.3.9",
"webpack-dev-middleware": "^3.7.1",
"webpack-dev-server": "^3.8.1",
"webpack-manifest-plugin": "^2.1.0",
"webpack-unassert-loader": "^1.2.0"
},
"dependencies": {
"@google-cloud/storage": "^3.0.3",
"aws-sdk": "^2.502.0",
"body-parser": "^1.19.0",
"choo": "^6.13.3",
"cldr-core": "^35.1.0",
"convict": "^5.1.0",
"express": "^4.17.1",
"express-ws": "github:dannycoates/express-ws",
"@dannycoates/express-ws": "^5.0.3",
"@fluent/bundle": "^0.13.0",
"@fluent/langneg": "^0.3.0",
"@google-cloud/storage": "^3.3.0",
"@sentry/node": "^5.6.2",
"aws-sdk": "^2.533.0",
"body-parser": "^1.19.0",
"choo": "^7.0.0",
"cldr-core": "^35.1.0",
"configstore": "github:dannycoates/configstore#master",
"convict": "^5.1.0",
"express": "^4.17.1",
"fxa-geodb": "^1.0.4",
"helmet": "^3.20.0",
"helmet": "^3.21.1",
"mkdirp": "^0.5.1",
"mozlog": "^2.2.0",
"node-fetch": "^2.6.0",
"raven": "^2.6.4",
"redis": "^2.8.0",
"selenium-standalone": "^6.15.6",
"ua-parser-js": "^0.7.20",
"websocket-stream": "^5.5.0"
"ua-parser-js": "^0.7.20"
},
"availableLanguages": [
"en-US",
@@ -178,6 +180,7 @@
"es-ES",
"es-MX",
"et",
"eu",
"fi",
"fr",
"fy-NL",
@@ -194,8 +197,10 @@
"ko",
"lt",
"ml",
"nb-NO",
"nl",
"nn-NO",
"oc",
"pa-IN",
"pl",
"pt-BR",

View File

@@ -182,3 +182,5 @@ shareLinkDescription = شارِك الرابط الذي يصل إلى الملف
shareLinkButton = شارِك الرابط
# $name is the name of the file
shareMessage = نزِّل ”{ $name }“ عبر { -send-brand }: خدمة لمشاركة الملفات بلا عناء وبخصوصية تامة
trailheadPromo = يمكنك حماية خصوصيتك، طبعا. انضم إلى فَيَرفُكس.
learnMore = اطّلع على المزيد.

View File

@@ -196,5 +196,5 @@ shareLinkDescription = Sdílet odkaz na soubor:
shareLinkButton = Sdílet odkaz
# $name is the name of the file
shareMessage = Stáhněte si soubor „{ $name }“ s { -send-brand(case: "ins") } - jednoduché a bezpečné sdílení souborů
trailheadPromo = Existuje snadný způsob pro ochranu vašeho soukromí. Používejte Firefox.
trailheadPromo = Existuje způsob, jak ochránit své soukromí. Používejte Firefox.
learnMore = Zjistit více.

View File

@@ -151,3 +151,5 @@ shareLinkDescription = Del linket til din fil:
shareLinkButton = Del link
# $name is the name of the file
shareMessage = Hent { $name } med { -send-brand } - simpel og sikker fildeling
trailheadPromo = Beskyt dine digitale rettigheder. Slut dig til Firefox.
learnMore = Læs mere.

View File

@@ -126,7 +126,7 @@ addPassword = Protegido con contraseña
emailPlaceholder = Ingresa tu correo electrónico
# $size is the size of the file, displayed using the fileSize message as format (e.g. "2.5MB")
signInSizeBump = Iniciar sesión para enviar hasta { $size }
signInButton = Iniciar sesión/registrarse
signInOnlyButton = Iniciar sesión
accountBenefitTitle = Crear una cuenta de { -firefox } o iniciar sesión
# $size is the size of the file, displayed using the fileSize message as format (e.g. "2.5MB")
accountBenefitLargeFiles = Compartir archivos de hasta { $size }
@@ -145,3 +145,11 @@ noStreamsWarning = Puede que este navegador no pueda descifrar un archivo tan gr
noStreamsOptionCopy = Copiar el enlace para abrir en otro navegador
noStreamsOptionFirefox = Prueba nuestro navegador favorito
noStreamsOptionDownload = Continuar con este navegador
downloadFirefoxPromo = { -send-short-brand } te lo ofrece el nuevo { -firefox }.
# the next line after the colon contains a file name
shareLinkDescription = Comparte el enlace a tu archivo:
shareLinkButton = Enlace para compartir
# $name is the name of the file
shareMessage = Descarga «{ $name }» con { -send-brand }: es sencillo y seguro
trailheadPromo = Existe una forma de proteger tu privacidad. Únete a Firefox.
learnMore = Saber más.

View File

@@ -23,7 +23,7 @@ fileSizeProgress = ({ $totalSize } / { $partialSize })
sendYourFilesLink = Probatu Firefox Send
errorPageHeader = Zerbait gaizki joan da!
fileTooBig = Fitxategia handiegia da kargatzeko. { $size } baino txikiagoa izan behar du.
linkExpiredAlt = Lotura iraungita
linkExpiredAlt = Lotura iraungi da
notSupportedHeader = Zure nabigatzailea ez da onartzen.
notSupportedLink = Zergatik ez da nire nabigatzailea onartzen?
notSupportedOutdatedDetail = Zoritxarrez Firefox bertsio honek ez du Firefox Send-ek behar duen web teknologia onartzen. Zure nabigatzailea eguneratu behar duzu.
@@ -54,7 +54,30 @@ passwordSetError = Pasahitz hau ezin da ezarri
-firefox = Firefox
-mozilla = Mozilla
introTitle = Partekatu fitxategiak modu sinple eta pribatuan
introDescription = { -send-brand } tresna fitxategiak partekatzeko da, muturretik muturrera zifratuta eta automatikoki iraungitzen diren loturekin. Hortaz, partekatzen duzuna pribatua izango da eta ziur egon zaitezke zure fitxategiak ez direla online egongo betirako.
notifyUploadEncryptDone = Zure fitxategia zifratuta eta bidaltzeko prest dago
# downloadCount is from the downloadCount string and timespan is a timespanMinutes string. ex. 'Expires after 2 downloads or 25 minutes'
archiveExpiryInfo = { $downloadCount } edo { $timespan } ondoren iraungiko da
timespanMinutes =
{ $num ->
[one] minutu 1
*[other] { $num } minutu
}
timespanDays =
{ $num ->
[one] egun 1
*[other] { $num } egun
}
timespanWeeks =
{ $num ->
[one] aste 1
*[other] { $num } aste
}
fileCount =
{ $num ->
[one] fitxategi 1
*[other] { $num } fitxategi
}
# byte abbreviation
bytes = B
# kibibyte abbreviation
@@ -65,9 +88,33 @@ mb = MB
gb = GB
# localized number and byte abbreviation. example "2.5MB"
fileSize = { $num }{ $units }
# $size is the size of the file, displayed using the fileSize message as format (e.g. "2.5MB")
totalSize = Tamaina guztira: { $size }
# the next line after the colon contains a file name
copyLinkDescription = Kopiatu fitxategia partekatzeko lotura:
copyLinkButton = Kopiatu lotura
downloadTitle = Deskargatu fitxategiak
downloadDescription = { -send-brand } bidez partekatu da fitxategia muturretik muturrera zifratuta eta automatikoki iraungitzen den lotura batekin.
trySendDescription = Probatu { -send-brand } fitxategiak partekatzeko modu sinple eta segururako.
# count will always be > 10
tooManyFiles =
{ $count ->
[one] Soilik fitxategi bakarra igo daiteke aldi berean.
*[other] Soilik { $count } fitxategi igo daitezke aldi berean.
}
# count will always be > 10
tooManyArchives =
{ $count ->
[one] Soilik artxibo bakarra onartzen da.
*[other] Soilik { $count } artxibo onartzen dira.
}
expiredTitle = Lotura hau iraungi da.
notSupportedDescription = { -send-brand } ez da nabigatzaile honetan ibiliko. { -send-short-brand } hobeto dabil { -firefox }(r)en azken bertsioarekin; halaber, nabigatzaile gehienen azken bertsioarekin ibiliko da.
downloadFirefox = Deskargatu { -firefox }
legalTitle = { -send-short-brand } pribatutasun-oharra
legalDateStamp = 1.0 bertsioa, 2019ko martxoaren 12koa.
# A short representation of a countdown timer containing the number of days, hours, and minutes remaining as digits, example "2d 11h 56m"
expiresDaysHoursMinutes = { $days }e { $hours }h { $minutes }m
addFilesButton = Hautatu igotzeko fitxategiak
uploadButton = Igo
# the first part of the string 'Drag and drop files or click to send up to 1GB'
@@ -77,7 +124,32 @@ dragAndDropFiles = Arrastatu eta jaregin fitxategiak
orClickWithSize = edo egin klik { $size } arte igotzeko
addPassword = Babestu pasahitzarekin
emailPlaceholder = Idatzi zure helbide elektronikoa
# $size is the size of the file, displayed using the fileSize message as format (e.g. "2.5MB")
signInSizeBump = Hasi saioa { $size } arte bidaltzeko
signInOnlyButton = Hasi saioa
accountBenefitTitle = Sortu { -firefox } kontu bat edo hasi saioa
# $size is the size of the file, displayed using the fileSize message as format (e.g. "2.5MB")
accountBenefitLargeFiles = Partekatu { $size } arteko fitxategiak
accountBenefitDownloadCount = Partekatu fitxategiak jende gehiagorekin
accountBenefitTimeLimit =
{ $count ->
[one] Utzi loturak erabilgarri egun batez
*[other] Utzi loturak erabilgarri { $count } egunez
}
accountBenefitSync = Kudeatu partekatutako fitxategiak edozein gailutatik
accountBenefitMoz = { -mozilla }ren beste zerbitzuei buruzko argibide gehiago
signOut = Amaitu saioa
okButton = Ados
downloadingTitle = Deskargatzen
noStreamsWarning = Baliteke nabigatzailea gai ez izatea horrelako tamaina handiko fitxategiak deszifratzeko.
noStreamsOptionCopy = Kopiatu lotura beste nabigatzaile batean irekitzeko
noStreamsOptionFirefox = Probatu gure nabigatzaile gogokoena
noStreamsOptionDownload = Jarraitu nabigatzaile honekin
downloadFirefoxPromo = Erabat berritutako { -firefox }(e)k eskaintzen dizu { -send-short-brand }
# the next line after the colon contains a file name
shareLinkDescription = Partekatu zure fitxategirako lotura:
shareLinkButton = Partekatu lotura
# $name is the name of the file
shareMessage = Deskargatu "{ $name }" { -send-brand } erabiliz: fitxategi-partekatze sinple eta segurua
trailheadPromo = Badago zure pribatutasuna babesteko modua. Egizu bat Firefoxekin.
learnMore = Argibide gehiago.

View File

@@ -53,6 +53,7 @@ passwordSetError = امکان ثبت این گذواژه نیست
-send-short-brand = ارسال
-firefox = فایرفاکس
-mozilla = موزیلا
introTitle = اشتراک‌گذاری ساده و خصوصیِ پرونده‌ها
# byte abbreviation
bytes = B
# kibibyte abbreviation

View File

@@ -1,19 +1,19 @@
# Firefox Send is a brand name and should not be localized.
title = Firefox Send
siteFeedback = משוב
importingFile = מתבצע ייבוא...
importingFile = מתבצע ייבוא
encryptingFile = מתבצעת הצפנה...
decryptingFile = מתבצע פענוח...
downloadCount =
{ $num ->
[one] הורדה אחת
*[other] { $number } הורדות
*[other] { $num } הורדות
}
timespanHours =
{ $num ->
[one] שעה אחת
[two] שעתיים
*[other] { $number } שעות
*[other] { $num } שעות
}
copiedUrl = הועתק!
unlockInputPlaceholder = ססמה

View File

@@ -123,7 +123,7 @@ dragAndDropFiles = Traher e deponer files
# $size is the size of the file, displayed using the fileSize message as format (e.g. "2.5MB")
orClickWithSize = o cliccar pro inviar usque { $size }
addPassword = Proteger per contrasigno
emailPlaceholder = Insere tu adresse email
emailPlaceholder = Insere tu adresse de e-mail
# $size is the size of the file, displayed using the fileSize message as format (e.g. "2.5MB")
signInSizeBump = Accede pro inviar usque { $size }
signInOnlyButton = Authentica te

View File

@@ -140,4 +140,5 @@ shareLinkDescription = ファイルへのリンクを共有しましょう:
shareLinkButton = リンクを共有
# $name is the name of the file
shareMessage = { -send-brand } で "{ $name }" をダウンロード: シンプルで安全なファイル共有
trailheadPromo = プライバシーを保護する方法があります。Firefox を試してください。
learnMore = 詳細情報

View File

@@ -5,7 +5,7 @@ importingFile = 가져오는 중…
encryptingFile = 암호화 중…
decryptingFile = 복호화 중…
downloadCount = 다운로드 { $num }회
timespanHours = 1 시간
timespanHours = { $num }시간
copiedUrl = 복사 완료!
unlockInputPlaceholder = 비밀번호
unlockButtonLabel = 잠금 해제

View File

@@ -54,6 +54,7 @@ passwordSetError = Dette passordet kunne ikke settes
-firefox = Firefox
-mozilla = Mozilla
introTitle = Enkel, privat fildeling
introDescription = { -send-brand } lar deg dele filer via en tidsbegrenset lenke med ende-til-ende-kryptering. På den måten kan du dele filer privat og samtidig være trygg på at filene dine ikke blir liggende på nettet for alltid.
notifyUploadEncryptDone = Filen din er kryptert og klar til å sende
# downloadCount is from the downloadCount string and timespan is a timespanMinutes string. ex. 'Expires after 2 downloads or 25 minutes'
archiveExpiryInfo = Utløper etter { $downloadCount } eller { $timespan }
@@ -77,9 +78,78 @@ fileCount =
[one] 1 fil
*[other] { $num } filer
}
# size is a localized number followed by a unit of bytes, ex. 2.5GB
# byte abbreviation
bytes = B
# kibibyte abbreviation
kb = KB
# mebibyte abbreviation
mb = MB
# gibibyte abbreviation
gb = GB
# localized number and byte abbreviation. example "2.5MB"
fileSize = { $num }{ $units }
# $size is the size of the file, displayed using the fileSize message as format (e.g. "2.5MB")
totalSize = Total størrelse: { $size }
# the next line after the colon contains a file name
copyLinkDescription = Kopier lenken for å dele filen din:
copyLinkButton = Kopier lenke
downloadTitle = Last ned filer
downloadDescription = Denne filen ble delt via { -send-brand } med ende-til-ende-kryptering og en lenke som automatisk utløper.
trySendDescription = Prøv { -send-brand } for enkel, sikker fildeling.
# count will always be > 10
tooManyFiles =
{ $count ->
[one] Kun 1 fil kan lastes opp om gangen.
*[other] Kun { $count } filer kan lastes opp om gangen.
}
# count will always be > 10
tooManyArchives =
{ $count ->
[one] Kun 1 arkiv er tillatt.
*[other] Kun { $count } arkiver er tillatt.
}
expiredTitle = Denne lenken er utløpt.
notSupportedDescription = { -send-brand } virker ikke med denne nettleseren. { -send-short-brand } fungerer best med den nyeste versjonen av { -firefox }, og vil fungere med den nyeste versjonen av de fleste nettlesere.
downloadFirefox = Last ned { -firefox }
legalTitle = { -send-short-brand } Personvernerklæring
legalDateStamp = Versjon 1.0, datert den 12. mars 2019
# A short representation of a countdown timer containing the number of days, hours, and minutes remaining as digits, example "2d 11h 56m"
expiresDaysHoursMinutes = { $days }d { $hours }t { $minutes }m
addFilesButton = Velg filer du vil laste opp
uploadButton = Last opp
# the first part of the string 'Drag and drop files or click to send up to 1GB'
dragAndDropFiles = Dra og slipp filer
# the second part of the string 'Drag and drop files or click to send up to 1GB'
# $size is the size of the file, displayed using the fileSize message as format (e.g. "2.5MB")
orClickWithSize = eller klikk for å sende filer på opptil { $size }
addPassword = Beskytt med passord
emailPlaceholder = Skriv inn e-postadressen din
# $size is the size of the file, displayed using the fileSize message as format (e.g. "2.5MB")
signInSizeBump = Logg inn for å sende opptil { $size }
signInOnlyButton = Logg inn
accountBenefitTitle = Opprett en { -firefox }-konto eller logg inn
# $size is the size of the file, displayed using the fileSize message as format (e.g. "2.5MB")
accountBenefitLargeFiles = Del filer på opptil { $size }
accountBenefitDownloadCount = Del filer med flere personer
accountBenefitTimeLimit =
{ $count ->
[one] Hold lenker aktiv opptil 1 dag
*[other] Hold lenker aktiv opptil { $count } dager
}
accountBenefitSync = Behandle delte filer fra en hvilken som helst enhet
accountBenefitMoz = Les om andre { -mozilla }-tjenester
signOut = Logg ut
okButton = OK
downloadingTitle = Laster ned
noStreamsWarning = Denne nettleseren kan kanskje ikke dekryptere en så stor fil.
noStreamsOptionCopy = Kopier lenken for å åpne den i en annen nettleser
noStreamsOptionFirefox = Prøv favorittnettleseren vår
noStreamsOptionDownload = Fortsett med denne nettleseren
downloadFirefoxPromo = { -send-short-brand } presenteres for deg av den helt nye { -firefox }.
# the next line after the colon contains a file name
shareLinkDescription = Del lenken til filen din:
shareLinkButton = Del lenke
# $name is the name of the file
shareMessage = Last ned { $name } med { -send-brand }: enkel, trygg fildeling
trailheadPromo = Det finnes en måte å ta vare på personvernet ditt. Bruk Firefox.
learnMore = Les mer.

155
public/locales/oc/send.ftl Normal file
View File

@@ -0,0 +1,155 @@
# Firefox Send is a brand name and should not be localized.
title = Firefox Send
siteFeedback = Comentaris
importingFile = Importacion…
encryptingFile = Chiframent…
decryptingFile = Deschiframent…
downloadCount =
{ $num ->
[one] 1 telecargament
*[other] { $num } telecargaments
}
timespanHours =
{ $num ->
[one] 1 ora
*[other] { $num } oras
}
copiedUrl = Copiat !
unlockInputPlaceholder = Senhal
unlockButtonLabel = Desverrolhar
downloadButtonLabel = Telecargar
downloadFinish = Telecargament acabat
fileSizeProgress = ({ $partialSize } sus { $totalSize })
sendYourFilesLink = Ensajar Firefox Send
errorPageHeader = I a quicòm que truca.
fileTooBig = Aqueste fichièr es tròp gròs per lenviar. Sa talha deu èsser inferiora a { $size }.
linkExpiredAlt = Lo ligam a expirat
notSupportedHeader = Vòstre navegador es pas compatible.
notSupportedLink = Perqué mon navegador es pas compatible ?
notSupportedOutdatedDetail = Aquesta version de Firefox es pas compatibla amb la tecnologia web amb la quala fonciona Firefox Send. Vos cal metre a jorn lo navegador.
updateFirefox = Metre a jorn Firefox
deletePopupCancel = Anullar
deleteButtonHover = Suprimir
footerLinkLegal = Mencions legalas
footerLinkPrivacy = Vida privada
footerLinkCookies = Cookies
passwordTryAgain = Senhal incorrècte. Tornatz ensajar.
javascriptRequired = Firefox Send requesís JavaScript
whyJavascript = Perque Firefox Send requesís JavaScript ?
enableJavascript = Volgatz activar lo JavaScript e ensajatz tornamai.
# A short representation of a countdown timer containing the number of hours and minutes remaining as digits, example "13h 47m"
expiresHoursMinutes = { $hours } h { $minutes } min
# A short representation of a countdown timer containing the number of minutes remaining as digits, example "56m"
expiresMinutes = { $minutes } min
# A short status message shown when the user enters a long password
maxPasswordLength = Talha maximala del senhal : { $length }
# A short status message shown when there was an error setting the password
passwordSetError = Aqueste senhal a pas pogut èsser definit
## Send version 2 strings
# Firefox Send, Send, Firefox, Mozilla are proper names and should not be localized
-send-brand = Firefox Send
-send-short-brand = Send
-firefox = Firefox
-mozilla = Mozilla
introTitle = Partatge simple e privat de fichièrs
introDescription = { -send-brand } vos permet de partejar de fichièr amb un chiframent del cap a la fin e un ligam quexpira automaticament. Atal podètz gardar privat çò que partejatz e vos assegurar que demorarà pas en linha per totjorn.
notifyUploadEncryptDone = Vòstre fichièr es chifrat e prèst per mandadís
# downloadCount is from the downloadCount string and timespan is a timespanMinutes string. ex. 'Expires after 2 downloads or 25 minutes'
archiveExpiryInfo = Expira aprèp { $downloadCount } o { $timespan }
timespanMinutes =
{ $num ->
[one] 1 minuta
*[other] { $num } minutas
}
timespanDays =
{ $num ->
[one] 1 jorn
*[other] { $num } jorns
}
timespanWeeks =
{ $num ->
[one] 1 setmana
*[other] { $num } setmanas
}
fileCount =
{ $num ->
[one] 1 fichièr
*[other] { $num } fichièrs
}
# byte abbreviation
bytes = o
# kibibyte abbreviation
kb = Ko
# mebibyte abbreviation
mb = Mo
# gibibyte abbreviation
gb = Go
# localized number and byte abbreviation. example "2.5MB"
fileSize = { $num } { $units }
# $size is the size of the file, displayed using the fileSize message as format (e.g. "2.5MB")
totalSize = Talha totala : { $size }
# the next line after the colon contains a file name
copyLinkDescription = Copiatz lo ligam per partejar vòstre fichièr :
copyLinkButton = Copiar lo ligam
downloadTitle = Telecargar los fichièrs
downloadDescription = Aqueste fichièr foguèt partejat via { -send-brand } amb chiframent del cap a la fin e un ligam quexpira automaticament.
trySendDescription = Ensajatz { -send-brand } per un partiment de fichièrs simple e segur.
# count will always be > 10
tooManyFiles =
{ $count ->
[one] Òm pòt pas quenviar 1 fichièr al còp.
*[other] Òm pòt pas quenviar { $count } fichièrs al còp.
}
# count will always be > 10
tooManyArchives =
{ $count ->
[one] Pas quun archiu es autorizat.
*[other] Pas que { $count } archius son autorizats.
}
expiredTitle = Aqueste ligam a expirat.
notSupportedDescription = { -send-brand } foncionarà pas amb aqueste navegador. { -send-short-brand } fonciona melhor amb la darrièra version de { -firefox } e foncionarà amb la version mai recenta de la màger part dels navegadors.
downloadFirefox = Telecargar { -firefox }
legalTitle = Avís de confidencialitat de { -send-short-brand }
legalDateStamp = Version 1.0 del 12 de març de 2019
# A short representation of a countdown timer containing the number of days, hours, and minutes remaining as digits, example "2d 11h 56m"
expiresDaysHoursMinutes = { $days } j { $hours } h { $minutes } min
addFilesButton = Seleccionatz los fichièrs de mandar
uploadButton = Enviar
# the first part of the string 'Drag and drop files or click to send up to 1GB'
dragAndDropFiles = Lissatz-depausatz de fichièrs
# the second part of the string 'Drag and drop files or click to send up to 1GB'
# $size is the size of the file, displayed using the fileSize message as format (e.g. "2.5MB")
orClickWithSize = o clicatz per enviar fins a { $size }
addPassword = Protegir amb un senhal
emailPlaceholder = Picatz vòstra adreça electronica
# $size is the size of the file, displayed using the fileSize message as format (e.g. "2.5MB")
signInSizeBump = Connectatz-vos per enviar fins a { $size }
signInOnlyButton = Connexion
accountBenefitTitle = Creatz un compte { -firefox } o connectatz-vos
# $size is the size of the file, displayed using the fileSize message as format (e.g. "2.5MB")
accountBenefitLargeFiles = Partejatz de fichièrs fins a { $size }
accountBenefitDownloadCount = Partejatz de fichièrs amb mai de personas
accountBenefitTimeLimit =
{ $count ->
[one] Mantenètz los ligams actius fins a 1 jorn
*[other] Mantenètz los ligams actius fins a { $count } jorns
}
accountBenefitSync = Gerissètz los fichièrs partejats de qualque siá periferic estant
accountBenefitMoz = Aprenètz-ne mai suls autres servicis { -mozilla }
signOut = Desconnexion
okButton = D'acòrd
downloadingTitle = Telecargament
noStreamsWarning = Pòt arribar quaqueste navegador pòsca pas deschifrar un fichièr tan gròs.
noStreamsOptionCopy = Copiatz lo ligam per lo dobrir dins un autre navegador
noStreamsOptionFirefox = Ensajatz nòstre navegador preferit
noStreamsOptionDownload = Contunhar amb aqueste navegador
downloadFirefoxPromo = Lo nòu { -firefox } vos provesís { -send-short-brand }.
# the next line after the colon contains a file name
shareLinkDescription = Partejatz lo ligam cap a vòstre fichièr :
shareLinkButton = Partejar lo ligam
# $name is the name of the file
shareMessage = Telecargar « { $name } » amb { -send-brand } : un biais simple e segur de partejar de fichièrs.
trailheadPromo = Existís un biais de protegir vòstra vida privada. Rejonhètz Firefox.
learnMore = Ne saber mai.

View File

@@ -151,3 +151,5 @@ shareLinkDescription = ਆਪਣੀ ਫਾਇਲ ਲਈ ਲਿੰਕ ਸਾਂ
shareLinkButton = ਲਿੰਕ ਸਾਂਝਾ ਕਰੋ
# $name is the name of the file
shareMessage = { -send-brand } ਨਾਲ "{ $name }" ਡਾਊਨਲੋਡ ਕਰੋ: ਸੌਖਾ, ਸੁਰੱਖਿਅਤ ਫਾਇਲ ਸਾਂਝਾ ਕਰਨਾ
trailheadPromo = ਤੁਹਾਡੀ ਪਰਦੇਦਾਰੀ ਦੀ ਸੁਰੱਖਿਆ ਦਾ ਢੰਗ ਹੈ। ਫਾਇਰਫਾਕਸ ਨਾਲ ਜੁੜੋ।
learnMore = ਹੋਰ ਸਿੱਖੋ

View File

@@ -54,7 +54,7 @@ passwordSetError = Essa senha não pôde ser definida
-firefox = Firefox
-mozilla = Mozilla
introTitle = Compartilhamento de arquivos fácil e privativo
introDescription = O { -send-brand } permite compartilhar arquivos com criptografia ponto a ponto e um link que expira automaticamente. Assim você pode manter o que compartilha privativo e ter certeza que suas coisas não ficarão online para sempre.
introDescription = O { -send-brand } permite compartilhar arquivos com criptografia de ponta a ponta e um link que expira automaticamente. Assim você pode manter o que compartilha privativo e ter certeza que suas coisas não ficarão online para sempre.
notifyUploadEncryptDone = Seu arquivo foi criptografado e está pronto para ser enviado
# downloadCount is from the downloadCount string and timespan is a timespanMinutes string. ex. 'Expires after 2 downloads or 25 minutes'
archiveExpiryInfo = Expirar após { $downloadCount } ou { $timespan }
@@ -94,7 +94,7 @@ totalSize = Tamanho total: { $size }
copyLinkDescription = Copie o link para compartilhar seu arquivo:
copyLinkButton = Copiar link
downloadTitle = Baixar arquivos
downloadDescription = Este arquivo foi compartilhado via { -send-brand } com criptografia ponto a ponto e um link que expira automaticamente.
downloadDescription = Este arquivo foi compartilhado via { -send-brand } com criptografia de ponta a ponta e um link que expira automaticamente.
trySendDescription = Experimente o { -send-brand } para compartilhamento de arquivos simples e seguro.
# count will always be > 10
tooManyFiles =
@@ -127,17 +127,17 @@ emailPlaceholder = Informe seu e-mail
# $size is the size of the file, displayed using the fileSize message as format (e.g. "2.5MB")
signInSizeBump = Entre na sua conta para enviar até { $size }
signInOnlyButton = Entrar
accountBenefitTitle = Crie uma Conta { -firefox } ou entre na sua conta
accountBenefitTitle = Crie uma Conta { -firefox } ou entre se já tiver
# $size is the size of the file, displayed using the fileSize message as format (e.g. "2.5MB")
accountBenefitLargeFiles = Compartilhe arquivos até { $size }
accountBenefitDownloadCount = Compartilhe arquivos com mais pessoas
accountBenefitLargeFiles = Compartilhe arquivos até { $size }.
accountBenefitDownloadCount = Compartilhe arquivos com mais pessoas.
accountBenefitTimeLimit =
{ $count ->
[one] Mantenha links ativos por até 1 dia
*[other] Mantenha links ativos por até { $count } dias
[one] Mantenha links ativos por até 1 dia.
*[other] Mantenha links ativos por até { $count } dias.
}
accountBenefitSync = Gerencie arquivos compartilhados a partir de qualquer dispositivo
accountBenefitMoz = Conheça outros serviços da { -mozilla }
accountBenefitSync = Gerencie arquivos compartilhados a partir de qualquer dispositivo.
accountBenefitMoz = Conheça outros serviços da { -mozilla }.
signOut = Sair
okButton = OK
downloadingTitle = Baixando

View File

@@ -89,6 +89,7 @@ totalSize = మొత్తం పరిమాణం: { $size }
copyLinkDescription = మీ ఫైలును భాగస్వామ్యం చేయడానికి ఈ లంకెను నకలు చేయండి:
copyLinkButton = లంకెను నకలుతీయి
downloadTitle = ఫైళ్లను దింపుకోండి
expiredTitle = ఈ లంకె గడువు ముగిసింది.
downloadFirefox = { -firefox } ను దింపుకోండి
legalTitle = { -send-short-brand } గోప్యతా నోటీసు
legalDateStamp = వెర్షన్ 1.0, మార్చి 12, 2019 నాటిది
@@ -96,6 +97,11 @@ legalDateStamp = వెర్షన్ 1.0, మార్చి 12, 2019 నా
expiresDaysHoursMinutes = { $days }d { $hours }h { $minutes }m
addFilesButton = ఎక్కించడానికి ఫైళ్ళను ఎంచుకోండి
uploadButton = ఎక్కించు
# the first part of the string 'Drag and drop files or click to send up to 1GB'
dragAndDropFiles = ఫైళ్ళను లాగండి మరియు వదలండి
# the second part of the string 'Drag and drop files or click to send up to 1GB'
# $size is the size of the file, displayed using the fileSize message as format (e.g. "2.5MB")
orClickWithSize = లేదా { $size } వరకు పంపడానికి నొక్కండి
addPassword = సంకేతపదంతో రక్షించండి
emailPlaceholder = ఈ ఈమెయిలును ఇవ్వండి
# $size is the size of the file, displayed using the fileSize message as format (e.g. "2.5MB")
@@ -105,6 +111,7 @@ accountBenefitTitle = ఒక { -firefox } ఖాతాని సృష్టి
# $size is the size of the file, displayed using the fileSize message as format (e.g. "2.5MB")
accountBenefitLargeFiles = { $size } పరిమాణం ఫైళ్ళ వరకు పంచుకోండి
accountBenefitDownloadCount = ఫైళ్లను ఎక్కువ మందితో పంచుకోండి
accountBenefitSync = ఏదైనా పరికరం నుండి పంచుకున్న ఫైళ్ళను నిర్వహించండి
signOut = నిష్క్రమించు
okButton = సరే
downloadingTitle = దింపుకుంటోంది

View File

@@ -87,7 +87,7 @@ totalSize = Tổng kích thước: { $size }
# the next line after the colon contains a file name
copyLinkDescription = Sao chép liên kết để chia sẻ tập tin của bạn:
copyLinkButton = Sao chép liên kết
downloadTitle = Tải tập tin
downloadTitle = Tải xuống tập tin
downloadDescription = Tập tin này đã được chia sẻ qua { -send-brand } với mã hóa đầu cuối và liên kết tự động hết hạn.
trySendDescription = Hãy thử { -send-brand } để chia sẻ tập tin đơn giản, an toàn.
# count will always be > 10

View File

@@ -19,8 +19,8 @@ exec(cmd)
const locales = Object.keys(summary)
.filter(locale => {
const loc = summary[locale];
const hasMissing = loc.hasOwnProperty('missing');
const hasErrors = loc.hasOwnProperty('errors');
const hasMissing = Object.prototype.hasOwnProperty.call(loc, 'missing');
const hasErrors = Object.prototype.hasOwnProperty.call(loc, 'errors');
return !hasMissing && !hasErrors;
})
.sort();

View File

@@ -33,7 +33,7 @@ function filterErrors(details) {
.sort()
.map(locale => {
const data = details[locale]
.filter(item => item.hasOwnProperty('error'))
.filter(item => Object.prototype.hasOwnProperty.call(item, 'error'))
.map(({ error }) => error);
return { locale, data };
})

View File

@@ -0,0 +1,13 @@
#!/bin/bash
echo "checking package-lock.json for changes"
IFS=' '
read -ra G_PARAMS <<< "$HUSKY_GIT_PARAMS"
PREV=${G_PARAMS[0]}
NEXT=${G_PARAMS[1]}
if [ "$PREV" != "$NEXT" ]; then
DIFF=$(git diff $PREV $NEXT package-lock.json)
if [ "$DIFF" != "" ]; then
npm install
fi
fi

View File

@@ -3,7 +3,7 @@ const routes = require('../routes');
const pages = require('../routes/pages');
const tests = require('../../test/frontend/routes');
const express = require('express');
const expressWs = require('express-ws');
const expressWs = require('@dannycoates/express-ws');
const morgan = require('morgan');
const config = require('../config');

View File

@@ -1,13 +1,13 @@
const express = require('express');
const path = require('path');
const Raven = require('raven');
const Sentry = require('@sentry/node');
const config = require('../config');
const routes = require('../routes');
const pages = require('../routes/pages');
const expressWs = require('express-ws');
const expressWs = require('@dannycoates/express-ws');
if (config.sentry_dsn) {
Raven.config(config.sentry_dsn).install();
Sentry.init({ dsn: config.sentry_dsn });
}
const app = express();

View File

@@ -2,7 +2,7 @@ const assets = require('../../common/assets');
const routes = require('../routes');
const pages = require('../routes/pages');
const tests = require('../../test/frontend/routes');
const expressWs = require('express-ws');
const expressWs = require('@dannycoates/express-ws');
module.exports = function(app, devServer) {
assets.setMiddleware(devServer.middleware);

View File

@@ -8,12 +8,10 @@ if (config.sentry_id) {
//eslint-disable-next-line node/no-missing-require
const version = require('../dist/version.json');
sentry = `
var RAVEN_CONFIG = {
var SENTRY_CONFIG = {
dsn: '${config.sentry_id}',
release: '${version.version}',
tags: {
commit: '${version.commit}'
},
dataCallback: function (data) {
beforeSend: function (data) {
var hash = window.location.hash;
if (hash) {
return JSON.parse(JSON.stringify(data).replace(new RegExp(hash.slice(1), 'g'), ''));
@@ -21,7 +19,6 @@ var RAVEN_CONFIG = {
return data;
}
}
var SENTRY_ID = '${config.sentry_id}';
`;
}

View File

@@ -67,7 +67,10 @@ module.exports = function(app) {
}
app.use(function(req, res, next) {
res.set('Pragma', 'no-cache');
res.set('Cache-Control', 'no-cache');
res.set(
'Cache-Control',
'private, no-cache, no-store, must-revalidate, max-age=0'
);
next();
});
app.use(bodyParser.json());

View File

@@ -3,12 +3,11 @@ const storage = require('../storage');
const config = require('../config');
const mozlog = require('../log');
const Limiter = require('../limiter');
const wsStream = require('websocket-stream/stream');
const fxa = require('../fxa');
const { statUploadEvent } = require('../amplitude');
const { encryptedSize } = require('../../app/utils');
const { Duplex } = require('stream');
const { Transform } = require('stream');
const log = mozlog('send.upload');
@@ -76,25 +75,19 @@ module.exports = function(ws, req) {
})
);
const limiter = new Limiter(encryptedSize(maxFileSize));
const flowControl = new Duplex({
read() {
ws.resume();
},
write(chunk, encoding, callback) {
const eof = new Transform({
transform: function(chunk, encoding, callback) {
if (chunk.length === 1 && chunk[0] === 0) {
this.push(null);
} else {
if (!this.push(chunk)) {
ws.pause();
}
this.push(chunk);
}
callback();
}
});
const wsStream = ws.constructor.createWebSocketStream(ws);
fileStream = wsStream(ws, { binary: true })
.pipe(flowControl)
.pipe(limiter); // limiter needs to be the last in the chain
fileStream = wsStream.pipe(eof).pipe(limiter); // limiter needs to be the last in the chain
await storage.set(newId, fileStream, meta, timeLimit);
@@ -126,8 +119,8 @@ module.exports = function(ws, req) {
error: e === 'limit' ? 413 : 500
})
);
ws.close();
}
}
ws.close();
});
};

View File

@@ -22,7 +22,7 @@ class GCSStorage {
.pipe(
this.bucket.file(id).createWriteStream({
validation: false,
resumable: false
resumable: true
})
)
.on('error', reject)

View File

@@ -19,5 +19,7 @@ rules:
mocha/no-pending-tests: error
mocha/no-return-and-callback: warn
mocha/no-skipped-tests: error
mocha/no-setup-in-describe: off
mocha/no-hooks-for-single-case: off
no-console: off # ¯\_(ツ)_/¯

View File

@@ -6,7 +6,7 @@ module.exports = {
const webpack = require('webpack');
const middleware = require('webpack-dev-middleware');
const express = require('express');
const expressWs = require('express-ws');
const expressWs = require('@dannycoates/express-ws');
const assets = require('../common/assets');
const routes = require('../server/routes');
const tests = require('./frontend/routes');
@@ -18,8 +18,8 @@ module.exports = {
app.use(wpm);
assets.setMiddleware(wpm);
expressWs(app, null, { perMessageDeflate: false });
app.ws('/api/ws', require('../server/routes/ws'));
routes(app);
app.ws('/api/ws', require('../server/routes/ws'));
tests(app);
wpm.waitUntilValid(() => {
server = app.listen(8000, resolve);

View File

@@ -67,7 +67,7 @@ const serviceWorker = {
},
{
// loads all assets from assets/ for use by common/assets.js
test: require.resolve('./build/generate_asset_map.js'),
test: require.resolve('./common/generate_asset_map.js'),
use: ['babel-loader', 'val-loader']
}
]
@@ -114,6 +114,7 @@ const web = {
exclude: [
path.resolve(__dirname, 'node_modules/crc'),
path.resolve(__dirname, 'node_modules/@fluent'),
path.resolve(__dirname, 'node_modules/@sentry'),
path.resolve(__dirname, 'node_modules/tslib'),
path.resolve(__dirname, 'node_modules/webcrypto-core')
],
@@ -175,7 +176,7 @@ const web = {
},
{
// loads all assets from assets/ for use by common/assets.js
test: require.resolve('./build/generate_asset_map.js'),
test: require.resolve('./common/generate_asset_map.js'),
use: ['babel-loader', 'val-loader']
}
]