Compare commits

...

39 Commits

Author SHA1 Message Date
Danny Coates
030d65d7af v1.0.3 2017-08-02 16:59:41 -07:00
Danny Coates
aa113fd903 updated production locales 2017-08-02 16:58:26 -07:00
Danny Coates
caeba94e04 format 2017-08-02 16:51:18 -07:00
Emin Mastizada
a7de951115 Pontoon: Update Azerbaijani (az) localization of Test Pilot: Firefox Send
Localization authors:
- Emin Mastizada <emin@mastizada.com>
2017-08-02 23:50:59 +00:00
Emin Mastizada
e17d1f7235 Pontoon: Update Azerbaijani (az) localization of Test Pilot: Firefox Send
Localization authors:
- Emin Mastizada <emin@mastizada.com>
2017-08-02 23:31:39 +00:00
Emin Mastizada
51ba253f95 Pontoon: Update Azerbaijani (az) localization of Test Pilot: Firefox Send
Localization authors:
- Emin Mastizada <emin@mastizada.com>
2017-08-02 23:10:44 +00:00
Danny Coates
fef26e083a removed extraneous captureException 2017-08-02 15:53:52 -07:00
Danny Coates
de826afb9b Merge pull request #402 from mozilla/filter-sentry
filter the hash from error reports
2017-08-02 15:50:09 -07:00
Danny Coates
5944b85e67 filter the hash from error reports 2017-08-02 15:04:34 -07:00
Danny Coates
d208a82089 Merge pull request #400 from mozilla/fix-dl-breaking-link
fix link that breaks download by opening in new tab
2017-08-02 14:05:55 -07:00
John Gruen
a64eced8be fix link that breaks download by opening in new tab 2017-08-02 22:38:17 +02:00
Danny Coates
ada45323e1 Merge pull request #369 from pdehaan/eslint-no-alert
Add ESLint no-alert shame rule
2017-08-02 12:34:27 -07:00
Danny Coates
ced8c24f47 Merge pull request #396 from mozilla/i395
add babel-polyfill
2017-08-02 12:31:43 -07:00
Danny Coates
5b9e9d5146 Merge pull request #394 from mozilla/i393
catch JSON.parse errors of storage metadata
2017-08-02 12:28:55 -07:00
Danny Coates
280a4f65e7 begrudgingly added babel-polyfill 2017-08-02 12:25:13 -07:00
Danny Coates
d2dd9f4b4d Merge pull request #367 from pdehaan/issue-364
Generate production locales using 'compare-locales'
2017-08-02 12:05:23 -07:00
Danny Coates
2897a39131 catch JSON.parse errors of storage metadata 2017-08-02 11:21:03 -07:00
Erica
626b9068a9 Merge pull request #392 from weihanglo/master
Adjust hover behavior on send-logo (#382)
Fixes: #382.
2017-08-02 13:59:22 -04:00
Weihang Lo
c3bb876a2c Adjust hover behavior on send-logo (#382) 2017-08-03 01:40:57 +08:00
Weihang Lo
48912dd4d4 Adjust hover behavior on send-logo (#382) 2017-08-03 00:38:56 +08:00
Марко Костић (Marko Kostić)
f1c894d14f Pontoon: Update Serbian (sr) localization of Test Pilot: Firefox Send
Localization authors:
- Марко Костић (Marko Kostić) <marko.m.kostic@gmail.com>
2017-08-02 15:31:30 +00:00
Марко Костић (Marko Kostić)
60d61fa52c Pontoon: Update Serbian (sr) localization of Test Pilot: Firefox Send
Localization authors:
- Марко Костић (Marko Kostić) <marko.m.kostic@gmail.com>
2017-08-02 15:11:02 +00:00
eljuno
68705f60db Pontoon: Update Indonesian (id) localization of Test Pilot: Firefox Send
Localization authors:
- Rahmat Subekti <rahmatsubekti@live.com>
- eljuno <eljunotrie_anggoro@yahoo.co.id>
- Kiki <kelimutu.rizki@gmail.com>
2017-08-02 12:31:54 +00:00
Fjoerfoks
92303988c0 Pontoon: Update Frisian (fy-NL) localization of Test Pilot: Firefox Send
Localization authors:
- Fjoerfoks <fryskefirefox@gmail.com>
2017-08-02 11:32:21 +00:00
Peter deHaan
49d578217c Merge pull request #380 from pdehaan/readme-pontoon
Add Pontoon URL to README
2017-08-01 22:24:48 -07:00
Peter deHaan
1abb3b7ebe Update README text w/ guidance from flod 2017-08-01 21:51:19 -07:00
Peter deHaan
4f3c2498a6 Add get-prod-locales and lint-locales scripts 2017-08-01 20:24:54 -07:00
Peter deHaan
33babe6f67 Add Pontoon URL to README 2017-08-01 17:01:11 -07:00
Tomáš Zelina
a51ee89939 Pontoon: Update Czech (cs) localization of Test Pilot: Firefox Send
Localization authors:
- Tomáš Zelina <zelitomas@gmail.com>
2017-08-01 20:31:18 +00:00
Danny Coates
bca79489c0 added 'v' to untagged version.json 2017-08-01 13:19:43 -07:00
avelper
ddfbb06e1a Pontoon: Update Spanish (es-ES) localization of Test Pilot: Firefox Send
Localization authors:
- avelper <avelper@mozilla-hispano.org>
- Jordi Cuevas <jordicuevas@gmail.com>
2017-08-01 15:31:44 +00:00
Ton
cd2c944d41 Pontoon: Update Dutch (nl) localization of Test Pilot: Firefox Send
Localization authors:
- Ton <tonnes.mb@gmail.com>
2017-08-01 12:11:07 +00:00
Peter deHaan
5f66496519 Add ESLint no-alert rule 2017-07-31 23:59:18 -07:00
Peter deHaan
318964251d Fix some linting errors 2017-07-31 15:25:29 -07:00
Peter deHaan
5effeb16d1 Generate production locales using 'compare-locales' 2017-07-31 14:34:28 -07:00
Danny Coates
c9c7c3182c v1.0.2 2017-07-31 11:58:03 -07:00
Danny Coates
18b95c497f Merge pull request #365 from mozilla/fix-chrome-footer
revert the IE fix to fix footer on chrome
2017-07-31 11:55:35 -07:00
Roberto Alvarado
80d0f73b06 Pontoon: Update Spanish (es-MX) localization of Test Pilot: Firefox Send
Localization authors:
- Roberto Alvarado <ralv888@gmail.com>
2017-07-31 18:35:08 +00:00
Danny Coates
522290dbef revert the IE fix to fix footer on chrome 2017-07-31 11:34:48 -07:00
33 changed files with 1015 additions and 410 deletions

View File

@@ -21,6 +21,7 @@ rules:
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"}]

1
.gitignore vendored
View File

@@ -4,6 +4,7 @@ public/upload.js
public/download.js
public/version.json
public/l20n.min.js
public/polyfill.min.js
static/*
!static/info.txt
test/frontend/bundle.js

View File

@@ -32,7 +32,7 @@ $ redis-server /usr/local/etc/redis.conf
## Localization
_Coming soon_ (see [#57](https://github.com/mozilla/send/issues/57))
Firefox Send localization is managed via [Pontoon](https://pontoon.mozilla.org/projects/test-pilot-firefox-send/), not direct pull requests to the repository. If you want to fix a typo, add a new language, or simply know more about localization, please get in touch with the [existing localization team](https://pontoon.mozilla.org/teams/) for your language, or Mozillas [l10n-drivers](https://wiki.mozilla.org/L10n:Mozilla_Team#Mozilla_Corporation) for guidance.
## Contributing

View File

@@ -31,6 +31,7 @@ deployment:
test:
override:
- npm run build:version
- npm run lint
- npm test
- nsp check

View File

@@ -1,13 +1,14 @@
window.Raven = require('raven-js');
window.Raven.config(window.dsn).install();
window.dsn = undefined;
if (navigator.doNotTrack !== '1' && window.RAVEN_CONFIG) {
window.Raven.config(window.SENTRY_ID, window.RAVEN_CONFIG).install();
}
const testPilotGA = require('testpilot-ga');
const { gcmCompliant, sendEvent } = require('./utils');
window.analytics = new testPilotGA({
an: 'Firefox Send',
ds: 'web',
tid: window.trackerId
tid: window.GOOGLE_ANALYTICS_ID
});
const isSender = !location.pathname.includes('/download');

View File

@@ -144,7 +144,7 @@ $(document).ready(function() {
$('#download-btn').attr('hidden', true);
$('#expired-img').removeAttr('hidden');
}
return;
throw err;
})
.then(([decrypted, fname]) => {
const endTime = Date.now();

View File

@@ -1,8 +1,6 @@
const EventEmitter = require('events');
const { arrayToHex } = require('./utils');
const Raven = window.Raven;
class FileSender extends EventEmitter {
constructor(file) {
super();
@@ -38,15 +36,14 @@ class FileSender extends EventEmitter {
const self = this;
self.emit('loading', true);
return Promise.all([
window.crypto.subtle
.generateKey(
{
name: 'AES-GCM',
length: 128
},
true,
['encrypt', 'decrypt']
),
window.crypto.subtle.generateKey(
{
name: 'AES-GCM',
length: 128
},
true,
['encrypt', 'decrypt']
),
new Promise((resolve, reject) => {
const reader = new FileReader();
reader.readAsArrayBuffer(this.file);
@@ -133,10 +130,6 @@ class FileSender extends EventEmitter {
);
xhr.send(fd);
});
})
.catch(err => {
Raven.captureException(err);
return Promise.reject(err);
});
}
}

View File

@@ -29,7 +29,12 @@ class Storage {
for (let i = 0; i < this.engine.length; i++) {
const k = this.engine.key(i);
if (isFile(k)) {
fs.push(JSON.parse(this.engine.getItem(k))); // parse or whatever else
try {
fs.push(JSON.parse(this.engine.getItem(k)));
} catch (err) {
// obviously you're not a golfer
this.engine.removeItem(k);
}
}
}
return fs.sort((file1, file2) => {

View File

@@ -1,12 +1,7 @@
/* global MAXFILESIZE EXPIRE_SECONDS */
require('./common');
const FileSender = require('./fileSender');
const {
notify,
findMetric,
sendEvent,
ONE_DAY_IN_MS
} = require('./utils');
const { notify, findMetric, sendEvent, ONE_DAY_IN_MS } = require('./utils');
const bytes = require('bytes');
const Storage = require('./storage');
const storage = new Storage(localStorage);

View File

@@ -99,12 +99,7 @@ function findMetric(href) {
}
function isFile(id) {
return ![
'referrer',
'totalDownloads',
'totalUploads',
'testpilot_ga__cid'
].includes(id);
return /^[0-9a-fA-F]{10}$/.test(id);
}
function sendEvent() {

147
package-lock.json generated
View File

@@ -1,6 +1,6 @@
{
"name": "firefox-send",
"version": "1.0.1",
"version": "1.0.3",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
@@ -263,6 +263,43 @@
"js-tokens": "3.0.2"
}
},
"babel-polyfill": {
"version": "6.23.0",
"resolved": "https://registry.npmjs.org/babel-polyfill/-/babel-polyfill-6.23.0.tgz",
"integrity": "sha1-g2TKYt+Or7gwSZ9pkXdGbDsDSZ0=",
"dev": true,
"requires": {
"babel-runtime": "6.25.0",
"core-js": "2.4.1",
"regenerator-runtime": "0.10.5"
},
"dependencies": {
"core-js": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/core-js/-/core-js-2.4.1.tgz",
"integrity": "sha1-TekR5mew6ukSTjQlS1OupvxhjT4=",
"dev": true
}
}
},
"babel-runtime": {
"version": "6.25.0",
"resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.25.0.tgz",
"integrity": "sha1-M7mOql1IK7AajRqmtDetKwGuxBw=",
"dev": true,
"requires": {
"core-js": "2.4.1",
"regenerator-runtime": "0.10.5"
},
"dependencies": {
"core-js": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/core-js/-/core-js-2.4.1.tgz",
"integrity": "sha1-TekR5mew6ukSTjQlS1OupvxhjT4=",
"dev": true
}
}
},
"balanced-match": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
@@ -271,12 +308,12 @@
"base64-js": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.2.1.tgz",
"integrity": "sha1-qRlH2h9KUW6jjltOwOw3c2deCIY="
"integrity": "sha512-dwVUVIXsBZXwTuwnXI9RK8sBmgq09NDHzyR9SAph9eqk76gKK2JSQmZARC2zRC81JC2QTtxD0ARU5qTS25gIGw=="
},
"bn.js": {
"version": "4.11.7",
"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.7.tgz",
"integrity": "sha1-3bBI5Q2UgnkAlME+s/z8gzznq0Y=",
"integrity": "sha512-LxFiV5mefv0ley0SzqkOPR1bC4EbpPx8LkOz5vMe/Yi15t5hzwgO/G+tc7wOtL4PZTYjwHu8JnEiSLumuSjSfA==",
"dev": true
},
"body-parser": {
@@ -451,7 +488,7 @@
"glob": {
"version": "7.1.2",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz",
"integrity": "sha1-wZyd+aAocC1nhhI4SmVSQExjbRU=",
"integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==",
"dev": true,
"requires": {
"fs.realpath": "1.0.0",
@@ -480,7 +517,7 @@
"string_decoder": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz",
"integrity": "sha1-D8Z9fBQYJd6UKC3VNr7GubzoYKs=",
"integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==",
"dev": true,
"requires": {
"safe-buffer": "5.1.1"
@@ -917,65 +954,75 @@
"resolved": "https://registry.npmjs.org/convict/-/convict-3.0.0.tgz",
"integrity": "sha1-JZ8wv7h+4JRIYEhiA1GdRntNUbU=",
"requires": {
"depd": "https://registry.npmjs.org/depd/-/depd-1.1.0.tgz",
"json5": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz",
"lodash.clonedeep": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz",
"minimist": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
"moment": "https://registry.npmjs.org/moment/-/moment-2.17.1.tgz",
"validator": "https://registry.npmjs.org/validator/-/validator-7.0.0.tgz",
"varify": "https://registry.npmjs.org/varify/-/varify-0.2.0.tgz"
"depd": "1.1.0",
"json5": "0.5.1",
"lodash.clonedeep": "4.5.0",
"minimist": "1.2.0",
"moment": "2.17.1",
"validator": "7.0.0",
"varify": "0.2.0"
},
"dependencies": {
"depd": {
"version": "https://registry.npmjs.org/depd/-/depd-1.1.0.tgz",
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/depd/-/depd-1.1.0.tgz",
"integrity": "sha1-4b2Cxqq2ztlluXuIsX7T5SjKGMM="
},
"json5": {
"version": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz",
"version": "0.5.1",
"resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz",
"integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE="
},
"lodash.clonedeep": {
"version": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz",
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz",
"integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8="
},
"minimist": {
"version": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
"integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ="
},
"moment": {
"version": "https://registry.npmjs.org/moment/-/moment-2.17.1.tgz",
"version": "2.17.1",
"resolved": "https://registry.npmjs.org/moment/-/moment-2.17.1.tgz",
"integrity": "sha1-/tlQYGPzaxDwZsi1mhRNf66+HYI="
},
"validator": {
"version": "https://registry.npmjs.org/validator/-/validator-7.0.0.tgz",
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/validator/-/validator-7.0.0.tgz",
"integrity": "sha1-x03rgGNRL6w1VHk45vCxUEooL9I="
},
"varify": {
"version": "https://registry.npmjs.org/varify/-/varify-0.2.0.tgz",
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/varify/-/varify-0.2.0.tgz",
"integrity": "sha1-GR2p/p3EzWjQ0USY1OKpEP9OZRY=",
"optional": true,
"requires": {
"redeyed": "https://registry.npmjs.org/redeyed/-/redeyed-1.0.1.tgz",
"through": "https://registry.npmjs.org/through/-/through-2.3.8.tgz"
"redeyed": "1.0.1",
"through": "2.3.8"
},
"dependencies": {
"redeyed": {
"version": "https://registry.npmjs.org/redeyed/-/redeyed-1.0.1.tgz",
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/redeyed/-/redeyed-1.0.1.tgz",
"integrity": "sha1-6WwZO0DAgWsArshCaY5hGF5VSYo=",
"optional": true,
"requires": {
"esprima": "https://registry.npmjs.org/esprima/-/esprima-3.0.0.tgz"
"esprima": "3.0.0"
},
"dependencies": {
"esprima": {
"version": "https://registry.npmjs.org/esprima/-/esprima-3.0.0.tgz",
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/esprima/-/esprima-3.0.0.tgz",
"integrity": "sha1-U88kes2ncxPlUcOqLnM0LT+099k=",
"optional": true
}
}
},
"through": {
"version": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
"version": "2.3.8",
"resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
"integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=",
"optional": true
}
@@ -1302,7 +1349,7 @@
"string_decoder": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz",
"integrity": "sha1-D8Z9fBQYJd6UKC3VNr7GubzoYKs=",
"integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==",
"dev": true,
"requires": {
"safe-buffer": "5.1.1"
@@ -1526,7 +1573,7 @@
"eslint-plugin-security": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/eslint-plugin-security/-/eslint-plugin-security-1.4.0.tgz",
"integrity": "sha1-1PMUSEqAsbYTuMiIboT1Lv4VJsI=",
"integrity": "sha512-xlS7P2PLMXeqfhyf3NpqbvbnW04kN8M9NtmhpR3XGyOvt/vNKS7XPXT5EDbwKW9vCjWH4PpfQvgD/+JgN0VJKA==",
"dev": true,
"requires": {
"safe-regex": "1.1.0"
@@ -1730,7 +1777,7 @@
"iconv-lite": {
"version": "0.4.18",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.18.tgz",
"integrity": "sha1-I9hlaxaq5nQqwpcy6o8DNqR4nPI=",
"integrity": "sha512-sr1ZQph3UwHTR0XftSbK85OvBbxe/abLGzEnPENCQwmHf7sck8Oyu4ob3LgBxWWxRoM+QszeUyl7jbqapu2TqA==",
"dev": true
}
}
@@ -2002,7 +2049,7 @@
"globals": {
"version": "9.18.0",
"resolved": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz",
"integrity": "sha1-qjiWs+abSH8X4x7SFD1pqOMMLYo=",
"integrity": "sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ==",
"dev": true
},
"globby": {
@@ -2022,7 +2069,7 @@
"glob": {
"version": "7.1.2",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz",
"integrity": "sha1-wZyd+aAocC1nhhI4SmVSQExjbRU=",
"integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==",
"dev": true,
"requires": {
"fs.realpath": "1.0.0",
@@ -2947,7 +2994,7 @@
"lru-cache": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.1.tgz",
"integrity": "sha1-Yi4y6CSItJJ5EUpPns9F581rulU=",
"integrity": "sha512-q4spe4KTfsAS1SUHLO0wz8Qiyf1+vMIAgpRYioFYDMNqKfHQbg+AVDH3i4fvpl71/P1L0dBl+fQi+P37UYf0ew==",
"dev": true,
"requires": {
"pseudomap": "1.0.2",
@@ -3093,7 +3140,7 @@
"minimatch": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
"integrity": "sha1-UWbihkV/AzBgZL5Ul+jbsMPTIIM=",
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
"requires": {
"brace-expansion": "1.1.8"
}
@@ -3226,7 +3273,7 @@
"string_decoder": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz",
"integrity": "sha1-D8Z9fBQYJd6UKC3VNr7GubzoYKs=",
"integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==",
"dev": true,
"requires": {
"safe-buffer": "5.1.1"
@@ -3787,7 +3834,7 @@
"promise": {
"version": "7.3.1",
"resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz",
"integrity": "sha1-BktyYCsY+Q8pGSuLG8QY/9Hr078=",
"integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==",
"requires": {
"asap": "2.0.6"
}
@@ -3919,7 +3966,7 @@
"randombytes": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.0.5.tgz",
"integrity": "sha1-3ACaJGuNCaF3tLegrne8Vw9LG3k=",
"integrity": "sha512-8T7Zn1AhMsQ/HI1SjcCfT/t4ii3eAqco3yOcSzS4mozsOz69lHLsoMXmF9nZgnFanYscnSlUSgs8uZyKzpE6kg==",
"dev": true,
"requires": {
"safe-buffer": "5.1.1"
@@ -4005,7 +4052,7 @@
"string_decoder": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz",
"integrity": "sha1-D8Z9fBQYJd6UKC3VNr7GubzoYKs=",
"integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==",
"dev": true,
"requires": {
"safe-buffer": "5.1.1"
@@ -4142,6 +4189,12 @@
"resolved": "https://registry.npmjs.org/referrer-policy/-/referrer-policy-1.1.0.tgz",
"integrity": "sha1-NXdOtzW/UPtsB46DM0tHI1AgfXk="
},
"regenerator-runtime": {
"version": "0.10.5",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.10.5.tgz",
"integrity": "sha1-M2w+/BIgrc7dosn6tntaeVWjNlg=",
"dev": true
},
"regex-cache": {
"version": "0.4.3",
"resolved": "https://registry.npmjs.org/regex-cache/-/regex-cache-0.4.3.tgz",
@@ -4246,7 +4299,7 @@
"glob": {
"version": "7.1.2",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz",
"integrity": "sha1-wZyd+aAocC1nhhI4SmVSQExjbRU=",
"integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==",
"dev": true,
"requires": {
"fs.realpath": "1.0.0",
@@ -4296,7 +4349,7 @@
"safe-buffer": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz",
"integrity": "sha1-iTMSr2myEj3vcfV4iQAWce6yyFM=",
"integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==",
"dev": true
},
"safe-regex": {
@@ -4444,7 +4497,7 @@
"glob": {
"version": "7.1.2",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz",
"integrity": "sha1-wZyd+aAocC1nhhI4SmVSQExjbRU=",
"integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==",
"dev": true,
"requires": {
"fs.realpath": "1.0.0",
@@ -4590,7 +4643,7 @@
"string_decoder": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz",
"integrity": "sha1-D8Z9fBQYJd6UKC3VNr7GubzoYKs=",
"integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==",
"dev": true,
"requires": {
"safe-buffer": "5.1.1"
@@ -4635,7 +4688,7 @@
"string_decoder": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz",
"integrity": "sha1-D8Z9fBQYJd6UKC3VNr7GubzoYKs=",
"integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==",
"dev": true,
"requires": {
"safe-buffer": "5.1.1"
@@ -4646,7 +4699,7 @@
"stream-http": {
"version": "2.7.2",
"resolved": "https://registry.npmjs.org/stream-http/-/stream-http-2.7.2.tgz",
"integrity": "sha1-QKBQ7I3DtTsz2ZCUFcAsC/Gr+60=",
"integrity": "sha512-c0yTD2rbQzXtSsFSVhtpvY/vS6u066PcXOX9kBB3mSO76RiUQzL340uJkGBWnlBg4/HZzqiUXtaVA7wcRcJgEw==",
"dev": true,
"requires": {
"builtin-status-codes": "3.0.0",
@@ -4674,7 +4727,7 @@
"string_decoder": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz",
"integrity": "sha1-D8Z9fBQYJd6UKC3VNr7GubzoYKs=",
"integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==",
"dev": true,
"requires": {
"safe-buffer": "5.1.1"
@@ -4710,7 +4763,7 @@
"string_decoder": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz",
"integrity": "sha1-D8Z9fBQYJd6UKC3VNr7GubzoYKs=",
"integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==",
"dev": true,
"requires": {
"safe-buffer": "5.1.1"
@@ -5007,7 +5060,7 @@
"readable-stream": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz",
"integrity": "sha1-No8lEtefnUb9/HE0mueHi7weuVw=",
"integrity": "sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ==",
"dev": true,
"requires": {
"core-util-is": "1.0.2",
@@ -5022,7 +5075,7 @@
"string_decoder": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz",
"integrity": "sha1-D8Z9fBQYJd6UKC3VNr7GubzoYKs=",
"integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==",
"dev": true,
"requires": {
"safe-buffer": "5.1.1"
@@ -5152,7 +5205,7 @@
"string_decoder": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz",
"integrity": "sha1-D8Z9fBQYJd6UKC3VNr7GubzoYKs=",
"integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==",
"dev": true,
"requires": {
"safe-buffer": "5.1.1"

View File

@@ -1,7 +1,7 @@
{
"name": "firefox-send",
"description": "File Sharing Experiment",
"version": "1.0.1",
"version": "1.0.3",
"author": "Mozilla (https://mozilla.org)",
"dependencies": {
"aws-sdk": "^2.89.0",
@@ -17,6 +17,7 @@
"redis": "^2.7.1"
},
"devDependencies": {
"babel-polyfill": "^6.23.0",
"browserify": "^14.4.0",
"eslint": "^4.3.0",
"eslint-plugin-mocha": "^4.11.0",
@@ -46,48 +47,55 @@
"license": "MPL-2.0",
"repository": "mozilla/send",
"availableLanguages": [
"en-US",
"zh-TW",
"zh-CN",
"az",
"cs",
"cy",
"de",
"dsb",
"en-US",
"es-ES",
"es-MX",
"fr",
"fy-NL",
"de",
"hsb",
"hu",
"it",
"ja",
"kab",
"ms",
"nb-NO",
"nl",
"nn-NO",
"pt-PT",
"pt-BR",
"pt-PT",
"ru",
"sk",
"sl",
"dsb",
"hsb",
"es-CL",
"es-ES",
"sr",
"sv-SE",
"tr",
"cy"
"zh-CN",
"zh-TW"
],
"scripts": {
"build": "npm-run-all build:*",
"build:upload": "browserify frontend/src/upload.js -g uglifyify -o public/upload.js",
"build:download": "browserify frontend/src/download.js -g uglifyify -o public/download.js",
"build:version": "node scripts/version",
"build:l10n": "cp node_modules/l20n/dist/web/l20n.min.js public",
"build:vendor": "cp node_modules/l20n/dist/web/l20n.min.js node_modules/babel-polyfill/dist/polyfill.min.js public",
"dev": "npm run build && npm start",
"format": "prettier '{frontend/src/,scripts/,server/,test/**/!(bundle)}*.js' 'public/*.css' --single-quote --write",
"get-prod-locales": "node scripts/get-prod-locales",
"get-prod-locales:write": "npm run get-prod-locales -- --write",
"lint": "npm-run-all lint:*",
"lint:css": "stylelint 'public/*.css'",
"lint:js": "eslint .",
"lint-locales": "node scripts/lint-locales",
"lint-locales:dev": "npm run lint-locales",
"lint-locales:prod": "npm run lint-locales -- --production",
"start": "node server/server",
"test": "npm-run-all test:*",
"test:unit": "mocha test/unit",
"test:server": "mocha test/server",
"test:browser": "browserify test/frontend/frontend.bundle.js -o test/frontend/bundle.js -d && node test/frontend/driver.js"
"test--browser": "browserify test/frontend/frontend.bundle.js -o test/frontend/bundle.js -d && node test/frontend/driver.js"
}
}

View File

@@ -0,0 +1,94 @@
// Firefox Send is a brand name and should not be localized.
title = Firefox Send
siteSubtitle = web eksperiment
siteFeedback = Geri dönüş
uploadPageHeader = Məxfi, Şifrələnmiş Fayl Paylaşma
uploadPageExplainer = Fayllarınızı təhlükəsiz, məxfi, şifrələnmiş və daima onlayn qalmaması üçün avtomatik silinən keçidlə göndərin.
uploadPageLearnMore = Ətraflı öyrən
uploadPageDropMessage = Yükləmək üçün faylınızı buraya daşıyın
uploadPageSizeMessage = Xidmətin daha yaxşı işləməsi üçün faylınız 1 GB-dan az olmalıdır
uploadPageBrowseButton = Kompüterinizdən fayl seçin
.title = Kompüterinizdən fayl seçin
uploadPageMultipleFilesAlert = Birdən çox fayl və ya qovluq yükləmə hələlik dəstəklənmir.
uploadPageBrowseButtonTitle = Fayl yüklə
uploadingPageHeader = Faylınız yüklənir
importingFile = İdxal edilir…
verifyingFile = Təsdiqlənir…
encryptingFile = Şifrələnir...
decryptingFile = Şifrə açılır...
notifyUploadDone = Yükləməniz hazırdır.
uploadingPageMessage = Faylınız yükləndikdən sonra vaxtı çıxma seçimlərini qura biləcəksiz.
uploadingPageCancel = Yükləməni ləğv et
.title = Yükləməni ləğv et
uploadCancelNotification = Yükləməniz ləğv edildi.
uploadingPageLargeFileMessage = Fayl böyükdür və yükləmək çox vaxt ala bilər. Səbirli olun!
uploadingFileNotification = Yükləmə bitdiyində xəbər ver.
uploadSuccessConfirmHeader = Göndərməyə hazır
uploadSvgAlt
.alt = Yüklə
uploadSuccessTimingHeader = Faylınızın keçidinin 1 endirmədən və ya 24 saatdan sonra vaxtı çıxacaq.
copyUrlFormLabelWithName = Faylınızı göndərmək üçün keçidi köçürün: { $filename }
// Note: Title text for button should be the same.
copyUrlFormButton = Buferə köçür
.title = Mübadilə buferinə köçür
copiedUrl = Köçürüldü!
// Note: Title text for button should be the same.
deleteFileButton = Faylı sil
.title = Faylı sil
// Note: Title text for button should be the same.
sendAnotherFileLink = Başqa fayl göndər
.title = Başqa fayl göndər
// Alternative text used on the download link/button (indicates an action).
downloadAltText
.alt = Endir
downloadFileName = { $filename } faylını endir
downloadFileSize = ({ $size })
// Firefox Send is a brand name and should not be localized.
downloadMessage = Yoldaşınız Firefox Send ilə sizə fayl göndərir, fayllarınızı təhlükəsiz, məxfi, şifrələnmiş və daima onlayn qalmaması üçün avtomatik silən fayl göndərmə xidməti.
// Text and title used on the download link/button (indicates an action).
downloadButtonLabel = Endir
.title = Endir
downloadNotification = Endirməniz tamamlandı.
downloadFinish = Endirmə Tamamlandı
// Firefox Send is a brand name and should not be localized. Title text for button should be the same.
sendYourFilesLink = Firefox Send Yoxla
.title = Firefox Send Yoxla
downloadingPageProgress = { $filename } faylı ({ $size }) endirilir
downloadingPageMessage = Lütfən faylı endirib şifrəsini açarkən vərəqi açıq buraxın.
errorAltText
.alt = Yükləmə xətası
errorPageHeader = Nəsə səhv getdi!
errorPageMessage = Faylı yüklərkən xəta baş verdi.
errorPageLink = Başqa fayl göndər
fileTooBig = Fayl yükləmək üçün çox böyükdür. Fayl { $size }-dan az olmalıdır.
linkExpiredAlt
.alt = Keçidin vaxtı çıxıb
expiredPageHeader = Keçidin vaxtı çıxıb və ya heç vaxt olmayıb!
notSupportedHeader = Səyyahınız dəstəklənmir.
// Firefox Send is a brand name and should not be localized.
notSupportedDetail = Heyf ki, bu səyyah Firefox Send-ə güc verən web texnologiyalarını dəstəkləmir. Fərqli bir səyyah yoxlamalısınız. Biz Firefox məsləhət görürük!
notSupportedOutdatedDetail = Heyf ki, Firefox səyyahının bu versiyası Firefox Send-ə güc verən web texnologiyalarını dəstəkləmir. Səyyahınızı yeniləməlisiniz.
updateFirefox = Firefox-u Yenilə
downloadFirefoxButtonSub = Pulsuz Endir
uploadedFile = Fayl
copyFileList = Keçidi Köçürt
// expiryFileList is used as a column header
expiryFileList = Vaxtı çıxma tarixi
deleteFileList = Sil
nevermindButton = Vacib deyil
legalHeader = Şərtlər və Məxfilik
legalNoticeTestPilot = Firefox Send Test Pilot eksperimentidir, Test Pilot <a>Xidmət Şərtləri</a> və <a>Məxfilik Bildirişi</a>-nə tabedir. Bu eksperiment və məlumat yığma haqqında <a>buradan</a> öyrənə bilərsiz.
legalNoticeMozilla = Firefox Send saytının istifadəsi həmçinin Mozilla-nın <a>Saytlar üçün Məxfilik Bildirişi</a> və <a>Sayt İstifadə Şərtləri</a>-nə tabedir.
deletePopupText = Fayl silinsin?
deletePopupYes = Bəli
deletePopupCancel = Ləğv et
deleteButtonHover
.title = Sil
copyUrlHover
.title = Keçidi Köçürt
footerLinkLegal = Hüquqi
// Test Pilot is a proper name and should not be localized.
footerLinkAbout = Test Pilot Haqqında
footerLinkPrivacy = Məxfilik
footerLinkTerms = Şərtlər
footerLinkCookies = Çərəzlər

View File

@@ -26,7 +26,7 @@ uploadingFileNotification = Upozornit, až bude nahrávání dokončeno.
uploadSuccessConfirmHeader = Připraveno k odeslání
uploadSvgAlt
.alt = Nahrát
uploadSuccessTimingHeader = Platnost odkazu na váš souboru vyprší pro jeho prvním stažení, nebo po 24 hodinách.
uploadSuccessTimingHeader = Platnost odkazu na váš souboru vyprší po jeho prvním stažení, nebo po 24 hodinách.
copyUrlFormLabelWithName = Zkopírujte a sdílejte odkaz na váš soubor: { $filename }
// Note: Title text for button should be the same.
copyUrlFormButton = Zkopírovat do schránky

View File

@@ -67,6 +67,8 @@ expiredPageHeader = ¡El enlace ha caducado o nunca existió!
notSupportedHeader = Tu navegador no está admitido.
// Firefox Send is a brand name and should not be localized.
notSupportedDetail = Lamentablemente, este navegador no admite la tecnología web que necesita Firefox Send. Tendrás que probar otro navegador. ¡Te recomendamos Firefox!
notSupportedOutdatedDetail = Lamentablemente, esta versión de Firefox no admite la tecnología web que impulsa Firefox Send. Tendrás que actualizar tu navegador.
updateFirefox = Actualizar Firefox
downloadFirefoxButtonSub = Descarga gratuita
uploadedFile = Archivo
copyFileList = Copiar URL

View File

@@ -63,3 +63,32 @@ errorPageLink = Enviar otro archivo
fileTooBig = Ese archivo es muy grande. Debería ocupar menos de { $size }.
linkExpiredAlt
.alt = Enlace caducado
expiredPageHeader = ¡Este enlace ha caducado o nunca existió en primer lugar!
notSupportedHeader = Tu navegador no está soportado.
// Firefox Send is a brand name and should not be localized.
notSupportedDetail = Lamentablemente, este navegador no admite la tecnología web que necesita Firefox Send. Tendrás que probar otro navegador. ¡Te recomendamos Firefox!
notSupportedOutdatedDetail = Lamentablemente esta versión de Firefox no soporta la tecnología web que potencia a Firefox Send. Deberás actualizar tu navegador.
updateFirefox = Actualizar Firefox
downloadFirefoxButtonSub = Descarga gratuita
uploadedFile = Archivo
copyFileList = Copiar URL
// expiryFileList is used as a column header
expiryFileList = Caduca en
deleteFileList = Eliminar
nevermindButton = Da igual
legalHeader = Términos y privacidad
legalNoticeTestPilot = Firefox Send sigue siendo un experimento de Test Pilot y está sujeto a las <a>Condiciones del servicio</a> y al <a>Aviso de privacidad</a> de Test Pilot. Puedes saber más acerca de este experimento y si recolección de datos <a>aquí</a>.
legalNoticeMozilla = El uso de la página de Firefox Send también está sujeto al <a>Aviso de privacidad sobre sitios web</a> y a los <a>Términos de uso sobre sitios web</a>.
deletePopupText = ¿Eliminar este archivo?
deletePopupYes = Sí
deletePopupCancel = Cancelar
deleteButtonHover
.title = Eliminar
copyUrlHover
.title = Copiar URL
footerLinkLegal = Legal
// Test Pilot is a proper name and should not be localized.
footerLinkAbout = Acerca de Test Pilot
footerLinkPrivacy = Privacidad
footerLinkTerms = Términos
footerLinkCookies = Cookies

View File

@@ -6,7 +6,7 @@ uploadPageHeader = Privee, fersifere bestânsdieling
uploadPageExplainer = Ferstjoer bestannen troch in feilich, privee en fersifere keppeling dy't automatysk ferrint, om foar te kommen dat jo guod net foar altyd online bliuwt.
uploadPageLearnMore = Mear ynfo
uploadPageDropMessage = Sleep jo bestân hjir hinne om opladen te starten
uploadPageSizeMessage = Foar de meast betrouber wurking, is it it bêste om jo bestân lytser as 1 GB te hâlden
uploadPageSizeMessage = Foar de meast betroubere wurking, is it it bêste om jo bestân lytser as 1 GB te hâlden
uploadPageBrowseButton = Selektearje in bestân op jo kompjûter
.title = Selektearje in bestân op jo kompjûter
uploadPageMultipleFilesAlert = Opladen fan mear bestannen tagelyk of in map wurdt op dit stuit net stipe.

View File

@@ -0,0 +1,78 @@
// Firefox Send is a brand name and should not be localized.
title = Firefox Send
siteSubtitle = eksperimen web
siteFeedback = Saran
uploadPageHeader = Pribadi, Berbagi Berkas Terenskripsi
uploadPageLearnMore = Pelajari lebih lanjut
uploadPageBrowseButtonTitle = Unggah berkas
uploadingPageHeader = Mengunggah Berkas Anda
importingFile = Mengimpor…
verifyingFile = Memverifikasi…
encryptingFile = Mengenkripsi...
decryptingFile = Mendekripsi...
notifyUploadDone = Unggahan Anda telah selesai.
uploadingPageMessage = Setelah berkas diunggah, Anda dapat mengatur pilihan kedaluwarsa.
uploadingPageCancel = Batal unggah
.title = Batal unggah
uploadCancelNotification = Unggahan Anda dibatalkan.
uploadingPageLargeFileMessage = Berkas ini berukuran besar dan mungkin perlu beberapa saat untuk mengunggahnya. Silakan tunggu!
uploadingFileNotification = Beri tahu saya ketika unggahan telah selesai.
uploadSuccessConfirmHeader = Siap untuk Dikirim
uploadSvgAlt
.alt = Unggah
uploadSuccessTimingHeader = Tautan ke berkas Anda akan berakhir setelah 1 unduhan atau dalam 24 jam.
copyUrlFormLabelWithName = Salin dan bagikan tautan untuk mengirim berkas Anda: { $filename }
// Note: Title text for button should be the same.
copyUrlFormButton = Salin ke papan klip
.title = Salin ke papan klip
copiedUrl = Tersalin!
// Note: Title text for button should be the same.
deleteFileButton = Hapus berkas
.title = Hapus berkas
// Note: Title text for button should be the same.
sendAnotherFileLink = Kirim berkas lain
.title = Kirim berkas lain
// Alternative text used on the download link/button (indicates an action).
downloadAltText
.alt = Unduh
downloadFileName = Unduh { $filename }
downloadFileSize = ({ $size })
downloadNotification = Unduhan Anda telah selesai.
downloadFinish = Unduhan Selesai
// Firefox Send is a brand name and should not be localized. Title text for button should be the same.
sendYourFilesLink = Coba Firefox Send
.title = Coba Firefox Send
downloadingPageProgress = Mengunduh { $filename } ({ $size })
downloadingPageMessage = Sila biarkan tab ini terbuka sementara kami memproses berkas Anda dan mendekripsinya.
errorAltText
.alt = Unggahan bermasalah
errorPageHeader = Terjadi kesalahan!
errorPageMessage = Terjadi kesalahan saat mengunggah berkas.
errorPageLink = Kirim berkas lain
fileTooBig = Berkas terlalu besar untuk diunggah. Harus kurang dari { $size }.
linkExpiredAlt
.alt = Tautan kedaluwarsa
expiredPageHeader = Tautan ini telah kedaluwarsa atau tidak pernah ada!
notSupportedHeader = Peramban Anda tidak mendukung.
updateFirefox = Perbarui Firefox
downloadFirefoxButtonSub = Unduh Gratis
uploadedFile = Berkas
copyFileList = Salin URL
// expiryFileList is used as a column header
expiryFileList = Kedaluwarsa Pada
deleteFileList = Hapus
nevermindButton = Abaikan
legalHeader = Syarat & Privasi
deletePopupText = Hapus berkas ini?
deletePopupYes = Ya
deletePopupCancel = Batal
deleteButtonHover
.title = Hapus
copyUrlHover
.title = Salin URL
footerLinkLegal = Legal
// Test Pilot is a proper name and should not be localized.
footerLinkAbout = Tentang Test Pilot
footerLinkPrivacy = Privasi
footerLinkTerms = Ketentuan
footerLinkCookies = Kuki

View File

@@ -0,0 +1,94 @@
// Firefox Send is a brand name and should not be localized.
title = Firefox Send
siteSubtitle = webexperiment
siteFeedback = Feedback
uploadPageHeader = Privé, versleuteld bestanden delen
uploadPageExplainer = Stuur bestanden via een veilige, private en versleutelde koppeling die automatisch verloopt, zodat u zeker weet dat uw zaken niet onbeperkt online blijven.
uploadPageLearnMore = Meer info
uploadPageDropMessage = Sleep uw bestand hiernaartoe om het te uploaden
uploadPageSizeMessage = Voor de meest betrouwbare werking kunt u uw bestand het beste onder de 1 GB houden
uploadPageBrowseButton = Selecteer een bestand op uw computer
.title = Selecteer een bestand op uw computer
uploadPageMultipleFilesAlert = Het uploaden van meerdere bestanden of een map wordt momenteel niet ondersteund.
uploadPageBrowseButtonTitle = bestand uploaden
uploadingPageHeader = Uw bestand wordt geüpload
importingFile = Importeren…
verifyingFile = Verifiëren…
encryptingFile = Versleutelen…
decryptingFile = Ontcijferen…
notifyUploadDone = Uw upload is voltooid.
uploadingPageMessage = Zodra uw bestand wordt geüpload, kunt u vervalopties instellen.
uploadingPageCancel = Uploaden annuleren
.title = Uploaden annuleren
uploadCancelNotification = Uw upload is geannuleerd.
uploadingPageLargeFileMessage = Dit bestand is groot en het uploaden kan even duren. Even geduld…
uploadingFileNotification = Mij waarschuwen zodra het uploaden is voltooid
uploadSuccessConfirmHeader = Gereed voor verzending
uploadSvgAlt
.alt = Uploaden
uploadSuccessTimingHeader = De koppeling naar uw bestand zal na 1 download of 24 uur verlopen.
copyUrlFormLabelWithName = Kopieer en deel de koppeling om uw bestand te verzenden: { $filename }
// Note: Title text for button should be the same.
copyUrlFormButton = Kopiëren naar klembord
.title = Kopiëren naar klembord
copiedUrl = Gekopieerd!
// Note: Title text for button should be the same.
deleteFileButton = Bestand verwijderen
.title = Bestand verwijderen
// Note: Title text for button should be the same.
sendAnotherFileLink = Nog een bestand verzenden
.title = Nog een bestand verzenden
// Alternative text used on the download link/button (indicates an action).
downloadAltText
.alt = Downloaden
downloadFileName = { $filename } downloaden
downloadFileSize = ({ $size })
// Firefox Send is a brand name and should not be localized.
downloadMessage = Uw vriend(in) stuurt u een bestand met Firefox Send, een dienst waarmee u bestanden kunt verzenden met een veilige, private en versleutelde koppeling die automatisch verloopt, zodat u zeker weet dat uw zaken niet onbeperkt online blijven.
// Text and title used on the download link/button (indicates an action).
downloadButtonLabel = Downloaden
.title = Downloaden
downloadNotification = Uw download is voltooid.
downloadFinish = Downloaden voltooid
// Firefox Send is a brand name and should not be localized. Title text for button should be the same.
sendYourFilesLink = Firefox Send proberen
.title = Firefox Send proberen
downloadingPageProgress = { $filename } ({ $size }) wordt gedownload
downloadingPageMessage = Laat dit tabblad geopend terwijl uw bestand wordt opgehaald en ontcijferd.
errorAltText
.alt = Uploadfout
errorPageHeader = Er is iets misgegaan!
errorPageMessage = Er is een fout opgetreden bij het uploaden van het bestand.
errorPageLink = Nog een bestand verzenden
fileTooBig = Dat bestand is te groot om te worden geüpload. Het moet kleiner zijn dan { $size }.
linkExpiredAlt
.alt = Koppeling verlopen
expiredPageHeader = Deze koppeling is verlopen of heeft überhaupt nooit bestaan!
notSupportedHeader = Uw browser wordt niet ondersteund.
// Firefox Send is a brand name and should not be localized.
notSupportedDetail = Helaas ondersteunt deze browser de webtechnologie die Firefox Send gebruikt niet. U dient een andere browser te proberen. Firefox wordt aanbevolen!
notSupportedOutdatedDetail = Helaas ondersteunt deze versie van Firefox de webtechnologie die Firefox Send gebruikt niet. U dient uw browser bij te werken.
updateFirefox = Firefox bijwerken
downloadFirefoxButtonSub = Gratis download
uploadedFile = Bestand
copyFileList = URL kopiëren
// expiryFileList is used as a column header
expiryFileList = Verloopt over
deleteFileList = Verwijderen
nevermindButton = Maakt niet uit
legalHeader = Voorwaarden en privacy
legalNoticeTestPilot = Firefox Send is momenteel een Test Pilot-experiment en onderhevig aan de <a>Servicevoorwaarden</a> en <a>Privacyverklaring</a> van Test Pilot. <a>Hier</a> vindt u meer info over dit experiment en de gegevensverzameling ervan.
legalNoticeMozilla = Gebruik van de Firefox Send-website is ook onderhevig aan de <a>Privacyverklaring voor websites</a> en <a>Servicevoorwaarden voor websites</a> van Mozilla.
deletePopupText = Dit bestand verwijderen?
deletePopupYes = Ja
deletePopupCancel = Annuleren
deleteButtonHover
.title = Verwijderen
copyUrlHover
.title = URL kopiëren
footerLinkLegal = Juridisch
// Test Pilot is a proper name and should not be localized.
footerLinkAbout = Over Test Pilot
footerLinkPrivacy = Privacy
footerLinkTerms = Voorwaarden
footerLinkCookies = Cookies

View File

@@ -0,0 +1,94 @@
// Firefox Send is a brand name and should not be localized.
title = Firefox Send
siteSubtitle = веб експеримент
siteFeedback = Повратне информације
uploadPageHeader = Приватно и шифровано дељење датотека
uploadPageExplainer = Шаљите датотеке преко безбедне, приватне и шифроване везе која самостално истиче да ваше ствари не би остале на нету заувек.
uploadPageLearnMore = Сазнајте више
uploadPageDropMessage = Превуците ваше датотеке овде да бисте кренули са отпремањем
uploadPageSizeMessage = За бољи рад предлажемо да датотека не буде већа од 1GB
uploadPageBrowseButton = Изаберите датотеку на рачунару
.title = Изаберите датотеку на рачунару
uploadPageMultipleFilesAlert = Отпремање фасцикли или више датотека тренутно није подржано.
uploadPageBrowseButtonTitle = Отпреми датотеку
uploadingPageHeader = Ваша датотека се отпрема
importingFile = Увозим…
verifyingFile = Потврђујем…
encryptingFile = Шифрујем…
decryptingFile = Дешифрујем…
notifyUploadDone = Ваше отпремање је завршено.
uploadingPageMessage = Након што се ваша датотека отпреми, моћи ћете да подесите опције истека.
uploadingPageCancel = Откажи отпремање
.title = Откажи отпремање
uploadCancelNotification = Ваше отпремање је отказано.
uploadingPageLargeFileMessage = Ово је велика датотека и отпремање може потрајати. Будите стрпљиви!
uploadingFileNotification = Обавести ме када се отпремање заврши.
uploadSuccessConfirmHeader = Спреман за слање
uploadSvgAlt
.alt = Отпреми
uploadSuccessTimingHeader = Веза ка вашој датотеци ће истећи након једног преузимања или након 24 сата.
copyUrlFormLabelWithName = Ископирајте и поделите везу да бисте послали вашу датотеку: { $filename }
// Note: Title text for button should be the same.
copyUrlFormButton = Копирај у оставу
.title = Копирај у оставу
copiedUrl = Ископирано!
// Note: Title text for button should be the same.
deleteFileButton = Обриши датотеку
.title = Обриши датотеку
// Note: Title text for button should be the same.
sendAnotherFileLink = Пошаљи другу датотеку
.title = Пошаљи другу датотеку
// Alternative text used on the download link/button (indicates an action).
downloadAltText
.alt = Преузми
downloadFileName = Преузимање датотеке { $filename }
downloadFileSize = ({ $size })
// Firefox Send is a brand name and should not be localized.
downloadMessage = Ваш пријатељ вам је послао датотеку преко услуге Firefox Send која вам омогућава да делите датотеке преко безбедне, приватне и шифроване везе која самостално истиче да ваше ствари не би остале на нету заувек.
// Text and title used on the download link/button (indicates an action).
downloadButtonLabel = Преузми
.title = Преузми
downloadNotification = Ваше преузимање је завршено.
downloadFinish = Преузимање је завршено.
// Firefox Send is a brand name and should not be localized. Title text for button should be the same.
sendYourFilesLink = Испробајте Firefox Send
.title = Испробајте Firefox Send
downloadingPageProgress = Преузимам датотеку { $filename } ({ $size })
downloadingPageMessage = Оставите овај језичак отвореним док не добавимо вашу датотеку и док је не дешифрујемо.
errorAltText
.alt = Грешка при отпремању
errorPageHeader = Нешто је пошло наопако!
errorPageMessage = Догодила се грешка приликом отпремања датотеке.
errorPageLink = Пошаљи другу датотеку
fileTooBig = Та датотека је превелика за отпремање. Треба да буде мања од { $size }.
linkExpiredAlt
.alt = Веза је истекла
expiredPageHeader = Веза је или истекла, или никада није ни постојала!
notSupportedHeader = Ваш прегледач није подржан.
// Firefox Send is a brand name and should not be localized.
notSupportedDetail = Нажалост, овај прегледач не подржава веб технологију која омогућава Firefox Send. Мораћете да пробате са другим прегледачем. Ми предлажемо Firefox!
notSupportedOutdatedDetail = Нажалост, ово издање Firefox-a не подржава веб технологију која омогућава Firefox Send. Мораћете да ажурирате ваш прегледач.
updateFirefox = Ажурирај Firefox
downloadFirefoxButtonSub = Бесплатно преузимање
uploadedFile = Датотека
copyFileList = URL за копирање
// expiryFileList is used as a column header
expiryFileList = Истиче за
deleteFileList = Брисање
nevermindButton = Занемари
legalHeader = Услови и приватност
legalNoticeTestPilot = Firefox Send је тренутно Тест Пилот експеримент и подложан је <a>условима коришћења</a> Тест Пилота и <a>обавештењем о приватности</a>. Можете сазнати више о овом експерименту и о његовом сакупљању података <a>овде</a>.
legalNoticeMozilla = Коришћење Firefox Send веб сајта подлеже Mozilla-ином <a>обавештењу о приватности на веб сајтовима</a> и <a>условима коришћења веб сајтова</a>.
deletePopupText = Обрисати ову датотеку?
deletePopupYes = Да
deletePopupCancel = Откажи
deleteButtonHover
.title = Обриши
copyUrlHover
.title = Ископирај URL
footerLinkLegal = Правни подаци
// Test Pilot is a proper name and should not be localized.
footerLinkAbout = О Тест Пилоту
footerLinkPrivacy = Приватност
footerLinkTerms = Услови
footerLinkCookies = Колачићи

View File

@@ -1,12 +1,8 @@
/*** index.html ***/
html {
background: url('resources/send_bg.svg');
font-family: -apple-system,
BlinkMacSystemFont,
'SF Pro Text',
Helvetica,
Arial,
sans-serif;
font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Text', Helvetica,
Arial, sans-serif;
font-weight: 200;
background-size: 110%;
background-repeat: no-repeat;
@@ -21,7 +17,6 @@ body {
flex-direction: column;
margin: 0;
min-height: 100vh;
height: 100%;
position: relative;
}
@@ -40,6 +35,14 @@ body {
align-items: center;
}
.send-logo h1 {
transition: color 50ms;
}
.send-logo h1:hover {
color: #0297f8;
}
.send-logo > a {
display: flex;
flex-direction: row;
@@ -68,7 +71,7 @@ body {
transition: color 50ms;
}
.send-logo:hover a {
.site-subtitle a:hover {
color: #0297f8;
}

6
scripts/.eslintrc.yml Normal file
View File

@@ -0,0 +1,6 @@
rules:
node/shebang: off
security/detect-child-process: off
no-console: off
no-process-exit: off

49
scripts/get-prod-locales.js Executable file
View File

@@ -0,0 +1,49 @@
#!/usr/bin/env node
const cp = require('child_process');
const { promisify } = require('util');
const fs = require('fs');
const pkg = require('../package.json');
const availableLanguages = pkg.availableLanguages.sort();
const exec = promisify(cp.exec);
const arrayDiff = (current, package) =>
current.filter(locale => !package.includes(locale));
const cmd = 'compare-locales l10n.toml . `ls public/locales` --data=json';
exec(cmd)
.then(({ stdout }) => JSON.parse(stdout))
.then(({ summary }) => {
const locales = Object.keys(summary)
.filter(locale => {
const loc = summary[locale];
const hasMissing = loc.hasOwnProperty('missing');
const hasErrors = loc.hasOwnProperty('errors');
return !hasMissing && !hasErrors;
})
.sort();
if (locales.join(',') !== availableLanguages.join(',')) {
const missingLanguages = arrayDiff(locales, availableLanguages);
console.log('current 100%:', JSON.stringify(locales));
console.log('package.json:', JSON.stringify(availableLanguages));
console.log('missing prod:', JSON.stringify(missingLanguages));
if (process.argv.includes('--write')) {
const pkgPath = require.resolve('../package.json');
pkg.availableLanguages = locales;
const str = JSON.stringify(pkg, null, 2) + '\n';
console.log('Updating /package.json availableLanguages');
fs.writeFileSync(pkgPath, str, 'utf-8');
}
} else {
console.log('Production locales are up to date!');
}
})
.catch(err => {
console.error(err);
process.exit(1);
});

51
scripts/lint-locales.js Normal file
View File

@@ -0,0 +1,51 @@
#!/usr/bin/env node
const cp = require('child_process');
const { promisify } = require('util');
const pkg = require('../package.json');
const conf = require('../server/config');
const exec = promisify(cp.exec);
const cmd = `compare-locales l10n.toml . ${getLocales()} --data=json`;
console.log(cmd);
exec(cmd)
.then(({ stdout }) => JSON.parse(stdout))
.then(({ details }) => filterErrors(details))
.then(results => {
if (results.length) {
results.forEach(({ locale, data }) => {
console.log(locale);
data.forEach(msg => console.log(`- ${msg}`));
console.log('');
});
process.exit(2);
}
})
.catch(err => {
console.error(err);
process.exit(1);
});
function filterErrors(details) {
return Object.keys(details)
.sort()
.map(locale => {
const data = details[locale]
.filter(item => item.hasOwnProperty('error'))
.map(({ error }) => error);
return { locale, data };
})
.filter(({ data }) => data.length);
}
function getLocales() {
// If we're in a "production" env (or passed the `--production` flag), only
// check the locales from the package.json file's `availableLanguages` array.
if (conf.env === 'production' || process.argv.includes('--production')) {
return pkg.availableLanguages.sort().join(' ');
}
// Lint all the locales.
return '`ls public/locales`';
}

View File

@@ -14,7 +14,7 @@ const filename = path.join(__dirname, '..', 'public', 'version.json');
const filedata = {
commit,
source: pkg.homepage,
version: process.env.CIRCLE_TAG || pkg.version
version: process.env.CIRCLE_TAG || `v${pkg.version}`
};
fs.writeFileSync(filename, JSON.stringify(filedata, null, 2) + '\n');

View File

@@ -10,6 +10,7 @@ const storage = require('./storage.js');
const Raven = require('raven');
const crypto = require('crypto');
const fs = require('fs');
const version = require('../public/version.json');
if (conf.sentry_dsn) {
Raven.config(conf.sentry_dsn).install();
@@ -106,8 +107,10 @@ app.get('/legal', (req, res) => {
app.get('/jsconfig.js', (req, res) => {
res.set('Content-Type', 'application/javascript');
res.render('jsconfig', {
trackerId: conf.analytics_id,
dsn: conf.sentry_id,
googleAnalyticsId: conf.analytics_id,
sentryId: conf.sentry_id,
version: version.version,
commit: version.commit,
maxFileSize: conf.max_file_size,
expireSeconds: conf.expire_seconds,
layout: false

View File

@@ -9,7 +9,7 @@ window.Raven = {
captureException: function(err) {
console.error(err, err.stack);
}
}
};
window.FakeFile = FakeFile;
window.FileSender = require('../../frontend/src/fileSender');

View File

@@ -15,83 +15,84 @@ let originalBlob;
describe('File Sender', function() {
before(function() {
server.respondImmediately = true;
server.respondWith(
'POST',
'/upload',
function(request) {
const reader = new FileReader();
reader.readAsArrayBuffer(request.requestBody.get('data'));
server.respondImmediately = true;
server.respondWith('POST', '/upload', function(request) {
const reader = new FileReader();
reader.readAsArrayBuffer(request.requestBody.get('data'));
reader.onload = function(event) {
file = this.result;
}
reader.onload = function(event) {
file = this.result;
};
const responseObj = JSON.parse(request.requestHeaders['X-File-Metadata']);
request.respond(
200,
{'Content-Type': 'application/json'},
JSON.stringify({url: 'some url',
id: responseObj.id,
delete: responseObj.delete})
)
}
)
})
const responseObj = JSON.parse(request.requestHeaders['X-File-Metadata']);
request.respond(
200,
{ 'Content-Type': 'application/json' },
JSON.stringify({
url: 'some url',
id: responseObj.id,
delete: responseObj.delete
})
);
});
});
it('Should get a loading event emission', function() {
const file = new FakeFile('hello_world.txt', ['This is some data.'])
const fs = new FileSender(file);
let testLoading = true;
const file = new FakeFile('hello_world.txt', ['This is some data.']);
const fs = new FileSender(file);
let testLoading = true;
fs.on('loading', isStillLoading => {
assert(!(!testLoading && isStillLoading));
testLoading = isStillLoading;
fs.on('loading', isStillLoading => {
assert(!(!testLoading && isStillLoading));
testLoading = isStillLoading;
});
return fs
.upload()
.then(info => {
assert(info);
assert(!testLoading);
})
return fs.upload()
.then(info => {
assert(info);
assert(!testLoading);
})
.catch(err => {
console.log(err, err.stack);
assert.fail();
});
})
.catch(err => {
console.log(err, err.stack);
assert.fail();
});
});
it('Should get a hashing event emission', function() {
const file = new FakeFile('hello_world.txt', ['This is some data.'])
const file = new FakeFile('hello_world.txt', ['This is some data.']);
const fs = new FileSender(file);
let testHashing = true;
fs.on('hashing', isStillHashing => {
assert(!(!testHashing && isStillHashing));
testHashing = isStillHashing;
})
assert(!(!testHashing && isStillHashing));
testHashing = isStillHashing;
});
return fs.upload()
.then(info => {
assert(info);
assert(!testHashing);
})
.catch(err => {
console.log(err, err.stack);
assert.fail();
});
})
return fs
.upload()
.then(info => {
assert(info);
assert(!testHashing);
})
.catch(err => {
console.log(err, err.stack);
assert.fail();
});
});
it('Should get a encrypting event emission', function() {
const file = new FakeFile('hello_world.txt', ['This is some data.'])
const file = new FakeFile('hello_world.txt', ['This is some data.']);
const fs = new FileSender(file);
let testEncrypting = true;
fs.on('encrypting', isStillEncrypting => {
assert(!(!testEncrypting && isStillEncrypting));
testEncrypting = isStillEncrypting;
})
});
return fs.upload()
return fs
.upload()
.then(info => {
assert(info);
assert(!testEncrypting);
@@ -100,67 +101,68 @@ describe('File Sender', function() {
console.log(err, err.stack);
assert.fail();
});
})
});
it('Should encrypt a file properly', function(done) {
const newFile = new FakeFile('hello_world.txt', ['This is some data.'])
const newFile = new FakeFile('hello_world.txt', ['This is some data.']);
const fs = new FileSender(newFile);
fs.upload().then(info => {
const key = info.secretKey;
secretKey = info.secretKey;
const IV = info.fileId;
encryptedIV = info.fileId;
const readRaw = new FileReader;
const readRaw = new FileReader();
readRaw.onload = function(event) {
const rawArray = new Uint8Array(this.result);
originalBlob = rawArray;
window.crypto.subtle.digest('SHA-256', rawArray).then(hash => {
fileHash = hash;
window.crypto.subtle.importKey(
'jwk',
{
kty: 'oct',
k: key,
alg: 'A128GCM',
ext: true,
},
{
name: 'AES-GCM'
},
true,
['encrypt', 'decrypt']
)
.then(cryptoKey => {
window.crypto.subtle.encrypt(
window.crypto.subtle
.importKey(
'jwk',
{
name: 'AES-GCM',
iv: hexToArray(IV),
additionalData: hash,
tagLength: 128
kty: 'oct',
k: key,
alg: 'A128GCM',
ext: true
},
cryptoKey,
rawArray
{
name: 'AES-GCM'
},
true,
['encrypt', 'decrypt']
)
.then(encrypted => {
assert(new Uint8Array(encrypted).toString() ===
new Uint8Array(file).toString());
done();
})
})
})
}
.then(cryptoKey => {
window.crypto.subtle
.encrypt(
{
name: 'AES-GCM',
iv: hexToArray(IV),
additionalData: hash,
tagLength: 128
},
cryptoKey,
rawArray
)
.then(encrypted => {
assert(
new Uint8Array(encrypted).toString() ===
new Uint8Array(file).toString()
);
done();
});
});
});
};
readRaw.readAsArrayBuffer(newFile);
})
})
});
});
});
describe('File Receiver', function() {
class FakeXHR {
constructor() {
this.response = file;
@@ -169,19 +171,19 @@ describe('File Receiver', function() {
static setup() {
FakeXHR.prototype.open = sinon.spy();
FakeXHR.prototype.send = function () {
FakeXHR.prototype.send = function() {
this.onload();
}
};
FakeXHR.prototype.originalXHR = window.XMLHttpRequest;
FakeXHR.prototype.getResponseHeader = function () {
FakeXHR.prototype.getResponseHeader = function() {
return JSON.stringify({
aad: arrayToHex(new Uint8Array(fileHash)),
filename: 'hello_world.txt',
id: encryptedIV
})
}
});
};
window.XMLHttpRequest = FakeXHR;
}
@@ -191,38 +193,47 @@ describe('File Receiver', function() {
window.XMLHttpRequest.prototype.originalXHR.restore();
}
}
const cb = function(done) {
if (file === undefined ||
encryptedIV === undefined ||
fileHash === undefined ||
secretKey === undefined) {
assert.fail('Please run file sending tests before trying to receive the files.');
if (
file === undefined ||
encryptedIV === undefined ||
fileHash === undefined ||
secretKey === undefined
) {
assert.fail(
'Please run file sending tests before trying to receive the files.'
);
done();
}
FakeXHR.setup();
done();
}
};
before(cb)
before(cb);
after(function() {
FakeXHR.restore();
})
});
it('Should decrypt properly', function() {
const fr = new FileReceiver();
location.hash = secretKey;
return fr.download().then(([decrypted, name]) => {
assert(name);
assert(new Uint8Array(decrypted).toString() ===
new Uint8Array(originalBlob).toString())
}).catch(err => {
console.log(err, err.stack);
assert.fail();
})
})
return fr
.download()
.then(([decrypted, name]) => {
assert(name);
assert(
new Uint8Array(decrypted).toString() ===
new Uint8Array(originalBlob).toString()
);
})
.catch(err => {
console.log(err, err.stack);
assert.fail();
});
});
it('Should emit decrypting events', function() {
const fr = new FileReceiver();
@@ -237,17 +248,20 @@ describe('File Receiver', function() {
fr.on('safe', isSafe => {
assert(isSafe);
})
});
return fr.download().then(([decrypted, name]) => {
assert(decrypted);
assert(name);
assert(!testDecrypting);
}).catch(err => {
console.log(err, err.stack);
assert.fail();
})
})
return fr
.download()
.then(([decrypted, name]) => {
assert(decrypted);
assert(name);
assert(!testDecrypting);
})
.catch(err => {
console.log(err, err.stack);
assert.fail();
});
});
it('Should emit hashing events', function() {
const fr = new FileReceiver();
@@ -262,99 +276,109 @@ describe('File Receiver', function() {
fr.on('safe', isSafe => {
assert(isSafe);
})
});
return fr.download().then(([decrypted, name]) => {
assert(decrypted);
assert(name);
assert(!testHashing);
}).catch(err => {
assert.fail();
})
})
return fr
.download()
.then(([decrypted, name]) => {
assert(decrypted);
assert(name);
assert(!testHashing);
})
.catch(err => {
assert.fail();
});
});
it('Should catch fraudulent checksums', function(done) {
// Use the secret key and file hash of the previous file to encrypt,
// which has a different hash than this one (different strings).
const newFile = new FakeFile('hello_world.txt',
['This is some data, with a changed hash.'])
const newFile = new FakeFile('hello_world.txt', [
'This is some data, with a changed hash.'
]);
const readRaw = new FileReader();
readRaw.onload = function(event) {
const plaintext = new Uint8Array(this.result);
window.crypto.subtle.importKey(
'jwk',
{
kty: 'oct',
k: secretKey,
alg: 'A128GCM',
ext: true
},
{
name: 'AES-GCM'
},
true,
['encrypt', 'decrypt']
)
.then(key => {
// The file hash used here is the hash of the fake
// file from the previous test; it's a phony checksum.
return window.crypto.subtle.encrypt(
window.crypto.subtle
.importKey(
'jwk',
{
name: 'AES-GCM',
iv: hexToArray(encryptedIV),
additionalData: fileHash,
tagLength: 128
kty: 'oct',
k: secretKey,
alg: 'A128GCM',
ext: true
},
key,
plaintext
{
name: 'AES-GCM'
},
true,
['encrypt', 'decrypt']
)
})
.then(encrypted => {
file = encrypted;
const fr = new FileReceiver();
location.hash = secretKey;
fr.on('unsafe', isUnsafe => {
assert(isUnsafe)
.then(key => {
// The file hash used here is the hash of the fake
// file from the previous test; it's a phony checksum.
return window.crypto.subtle.encrypt(
{
name: 'AES-GCM',
iv: hexToArray(encryptedIV),
additionalData: fileHash,
tagLength: 128
},
key,
plaintext
);
})
.then(encrypted => {
file = encrypted;
const fr = new FileReceiver();
location.hash = secretKey;
fr.on('safe', () => {
// This event should not be emitted.
assert.fail();
})
fr.on('unsafe', isUnsafe => {
assert(isUnsafe);
});
fr.download().then(() => {
assert.fail();
done();
}).catch(err => {
assert(1);
done();
})
})
}
fr.on('safe', () => {
// This event should not be emitted.
assert.fail();
});
fr
.download()
.then(() => {
assert.fail();
done();
})
.catch(err => {
assert(1);
done();
});
});
};
readRaw.readAsArrayBuffer(newFile);
})
});
it('Should not decrypt with an incorrect checksum', function() {
FakeXHR.prototype.getResponseHeader = function () {
FakeXHR.prototype.getResponseHeader = function() {
return JSON.stringify({
aad: 'some_bad_hashz',
filename: 'hello_world.txt',
id: encryptedIV
})
}
});
};
const fr = new FileReceiver();
location.hash = secretKey;
return fr.download().then(([decrypted, name]) => {
assert(decrypted);
assert(name);
assert.fail();
}).catch(err => {
assert(1);
})
})
})
return fr
.download()
.then(([decrypted, name]) => {
assert(decrypted);
assert(name);
assert.fail();
})
.catch(err => {
assert(1);
});
});
});

View File

@@ -4,7 +4,6 @@ const proxyquire = require('proxyquire');
const request = require('supertest');
const fs = require('fs');
const logStub = {};
logStub.info = sinon.stub();
logStub.error = sinon.stub();
@@ -38,17 +37,21 @@ describe('Server integration tests', function() {
storage.flushall();
storage.quit();
server.close();
})
});
function upload() {
return request(server).post('/upload')
.field('fname', 'test_upload.txt')
.set('X-File-Metadata', JSON.stringify({
aad: '11111',
id: '111111111111111111111111',
filename: 'test_upload.txt'
}))
.attach('file', './test/test_upload.txt')
return request(server)
.post('/upload')
.field('fname', 'test_upload.txt')
.set(
'X-File-Metadata',
JSON.stringify({
aad: '11111',
id: '111111111111111111111111',
filename: 'test_upload.txt'
})
)
.attach('file', './test/test_upload.txt');
}
it('Responds with a 200 when the service is up', function() {
@@ -56,115 +59,123 @@ describe('Server integration tests', function() {
});
it('Rejects with a 404 when a file id is not valid', function() {
return request(server).post('/upload/123')
.field('fname', 'test_upload.txt')
.set('X-File-Metadata', JSON.stringify({
'silly': 'text'
}))
.attach('file', './test/test_upload.txt')
.expect(404)
})
return request(server)
.post('/upload/123')
.field('fname', 'test_upload.txt')
.set(
'X-File-Metadata',
JSON.stringify({
silly: 'text'
})
)
.attach('file', './test/test_upload.txt')
.expect(404);
});
it('Accepts a file and stores it when properly uploaded', function(done) {
upload().then(res => {
assert(res.body.hasOwnProperty('delete'));
uuid = res.body.delete;
assert(res.body.hasOwnProperty('url'));
assert(res.body.hasOwnProperty('id'));
fileId = res.body.id;
fs.access('./static/' + fileId, fs.constants.F_OK, err => {
if (err) {
done(new Error('The file does not exist'));
} else {
done();
}
})
})
})
assert(res.body.hasOwnProperty('delete'));
uuid = res.body.delete;
assert(res.body.hasOwnProperty('url'));
assert(res.body.hasOwnProperty('id'));
fileId = res.body.id;
fs.access('./static/' + fileId, fs.constants.F_OK, err => {
if (err) {
done(new Error('The file does not exist'));
} else {
done();
}
});
});
});
it('Responds with a 200 if a file exists', function() {
return request(server).get('/exists/' + fileId)
.expect(200)
})
return request(server).get('/exists/' + fileId).expect(200);
});
it('Exists in the redis server', function() {
return storage.exists(fileId)
.then(() => assert(1))
.catch(err => assert.fail())
})
return storage
.exists(fileId)
.then(() => assert(1))
.catch(err => assert.fail());
});
it('Fails delete if the delete token does not match', function() {
return request(server).post('/delete/' + fileId)
.send({ delete_token: 11 })
.expect(404);
})
return request(server)
.post('/delete/' + fileId)
.send({ delete_token: 11 })
.expect(404);
});
it('Fails delete if the id is invalid', function() {
return request(server).post('/delete/1')
.expect(404);
})
return request(server).post('/delete/1').expect(404);
});
it('Successfully deletes if the id is valid and the delete token matches', function(done) {
request(server).post('/delete/' + fileId)
.send({ delete_token: uuid })
.expect(200)
.then(() => {
fs.access('./static/' + fileId, fs.constants.F_OK, err => {
if (err) {
done();
} else {
done(new Error('The file does not exist'));
}
})
})
})
it('Successfully deletes if the id is valid and the delete token matches', function(
done
) {
request(server)
.post('/delete/' + fileId)
.send({ delete_token: uuid })
.expect(200)
.then(() => {
fs.access('./static/' + fileId, fs.constants.F_OK, err => {
if (err) {
done();
} else {
done(new Error('The file does not exist'));
}
});
});
});
it('Responds with a 404 if a file does not exist', function() {
return request(server).get('/exists/notfound')
.expect(404)
})
return request(server).get('/exists/notfound').expect(404);
});
it('Uploads properly after a delete', function(done) {
upload().then(res => {
assert(res.body.hasOwnProperty('delete'));
uuid = res.body.delete;
assert(res.body.hasOwnProperty('url'));
assert(res.body.hasOwnProperty('id'));
fileId = res.body.id;
fs.access('./static/' + fileId, fs.constants.F_OK, err => {
if (err) {
done(new Error('The file does not exist'));
} else {
done();
}
})
})
})
assert(res.body.hasOwnProperty('delete'));
uuid = res.body.delete;
assert(res.body.hasOwnProperty('url'));
assert(res.body.hasOwnProperty('id'));
fileId = res.body.id;
fs.access('./static/' + fileId, fs.constants.F_OK, err => {
if (err) {
done(new Error('The file does not exist'));
} else {
done();
}
});
});
});
it('Responds with a 200 for the download page', function() {
return request(server).get('/download/' + fileId)
.expect(200);
})
return request(server).get('/download/' + fileId).expect(200);
});
it('Downloads a file properly', function() {
return request(server).get('/assets/download/' + fileId)
.then(res => {
assert(res.header.hasOwnProperty('content-disposition'));
assert(res.header.hasOwnProperty('content-type'))
assert(res.header.hasOwnProperty('content-length'))
assert(res.header.hasOwnProperty('x-file-metadata'))
assert.equal(res.header['content-disposition'], 'attachment; filename=test_upload.txt')
assert.equal(res.header['content-type'], 'application/octet-stream')
})
})
return request(server).get('/assets/download/' + fileId).then(res => {
assert(res.header.hasOwnProperty('content-disposition'));
assert(res.header.hasOwnProperty('content-type'));
assert(res.header.hasOwnProperty('content-length'));
assert(res.header.hasOwnProperty('x-file-metadata'));
assert.equal(
res.header['content-disposition'],
'attachment; filename=test_upload.txt'
);
assert.equal(res.header['content-type'], 'application/octet-stream');
});
});
it('The file is deleted after one download', function() {
assert(!fs.existsSync('./static/' + fileId));
})
});
it('No longer exists in the redis server', function() {
return storage.exists(fileId)
.then(() => assert.fail())
.catch(err => assert(1))
})
return storage
.exists(fileId)
.then(() => assert.fail())
.catch(err => assert(1));
});
});

View File

@@ -110,9 +110,9 @@ describe('Testing Set using aws', function() {
it('Should pass when the file is successfully uploaded', function() {
const buf = Buffer.alloc(10);
sinon.stub(crypto, 'randomBytes').returns(buf);
s3Stub.upload.returns({promise: () => Promise.resolve()});
s3Stub.upload.returns({ promise: () => Promise.resolve() });
return storage
.set('123', {on: sinon.stub()}, 'Filename.moz', {})
.set('123', { on: sinon.stub() }, 'Filename.moz', {})
.then(() => {
assert(expire.calledOnce);
assert(expire.calledWith('123', 86400));
@@ -121,9 +121,9 @@ describe('Testing Set using aws', function() {
});
it('Should fail if there was an error during uploading', function() {
s3Stub.upload.returns({promise: () => Promise.reject()});
s3Stub.upload.returns({ promise: () => Promise.reject() });
return storage
.set('123', {on: sinon.stub()}, 'Filename.moz', 'url.com')
.set('123', { on: sinon.stub() }, 'Filename.moz', 'url.com')
.then(_reply => assert.fail())
.catch(err => assert(1));
});

View File

@@ -35,5 +35,5 @@
</div>
</div>
<a class="send-new" data-l10n-id="sendYourFilesLink"></a>
<a class="send-new" data-l10n-id="sendYourFilesLink" target="_blank"></a>
</div>

View File

@@ -5,11 +5,24 @@ if (isIE && !isUnsupportedPage) {
window.location.replace('/unsupported/ie');
}
{{#if dsn}}
window.dsn = '{{{dsn}}}';
{{#if sentryId}}
var RAVEN_CONFIG = {
release: '{{{version}}}',
tags: {
commit: '{{{commit}}}'
},
dataCallback: function (data) {
var hash = window.location.hash;
if (hash) {
return JSON.parse(JSON.stringify(data).replace(new RegExp(hash.slice(1), 'g'), ''));
}
return data;
}
}
var SENTRY_ID = '{{{sentryId}}}';
{{/if}}
{{#if trackerId}}
window.trackerId = '{{{trackerId}}}';
{{#if googleAnalyticsId}}
var GOOGLE_ANALYTICS_ID = '{{{googleAnalyticsId}}}';
{{/if}}
var MAXFILESIZE = {{{maxFileSize}}};
var EXPIRE_SECONDS = {{{expireSeconds}}};

View File

@@ -12,6 +12,7 @@
<link rel="icon" type="image/png" href="/resources/favicon-32x32.png" sizes="32x32" />
<link rel="localization" href="/locales/{locale}/send.ftl">
<script src="/polyfill.min.js"></script>
<script defer src="/l20n.min.js"></script>
</head>
<body>