Просмотр исходного кода

Merge branch 'better-feature-detection'

El RIDO 6 лет назад
Родитель
Сommit
b496ae42fd
31 измененных файлов с 858 добавлено и 476 удалено
  1. 9 9
      .eslintrc
  2. 11 2
      cfg/conf.sample.php
  3. 3 4
      i18n/bg.json
  4. 3 4
      i18n/cs.json
  5. 3 4
      i18n/de.json
  6. 3 4
      i18n/es.json
  7. 3 4
      i18n/fr.json
  8. 3 4
      i18n/hu.json
  9. 3 4
      i18n/it.json
  10. 3 4
      i18n/nl.json
  11. 3 4
      i18n/no.json
  12. 3 4
      i18n/oc.json
  13. 3 4
      i18n/pl.json
  14. 3 4
      i18n/pt.json
  15. 3 4
      i18n/ru.json
  16. 3 4
      i18n/sl.json
  17. 3 4
      i18n/zh.json
  18. 1 0
      js/common.js
  19. 309 0
      js/legacy.js
  20. 150 274
      js/privatebin.js
  21. 207 18
      js/test/Alert.js
  22. 83 0
      js/test/Check.js
  23. 7 1
      js/test/CryptTool.js
  24. 6 6
      js/test/Helper.js
  25. 0 88
      js/test/InitialCheck.js
  26. 1 0
      lib/Controller.php
  27. 15 2
      lib/Request.php
  28. 0 1
      robots.txt
  29. 6 8
      tpl/bootstrap.php
  30. 7 7
      tpl/page.php
  31. 1 0
      tst/ViewTest.php

+ 9 - 9
.eslintrc

@@ -1,3 +1,4 @@
+---
 parserOptions:
 parserOptions:
   ecmaVersion: 2017
   ecmaVersion: 2017
 
 
@@ -11,17 +12,16 @@ env:
   es6: true
   es6: true
   jquery: true
   jquery: true
   node: true
   node: true
+  mocha: true
 
 
 globals:
 globals:
-  DOMPurify: false
-  after: true
-  before: true
-  cleanup: true
-  describe: false
-  it: false
-  jsc: false
-  jsdom: true
-  kjua: true
+  DOMPurify: readonly
+  cleanup: writable
+  describe: readonly
+  jsc: readonly
+  jsdom: writable
+  kjua: writable
+  WebCrypto: writable
 
 
 # http://eslint.org/docs/rules/
 # http://eslint.org/docs/rules/
 rules:
 rules:

+ 11 - 2
cfg/conf.sample.php

@@ -68,8 +68,17 @@ languageselection = false
 ; custom scripts from third-party domains to your templates, e.g. tracking
 ; custom scripts from third-party domains to your templates, e.g. tracking
 ; scripts or run your site behind certain DDoS-protection services.
 ; scripts or run your site behind certain DDoS-protection services.
 ; Check the documentation at https://content-security-policy.com/
 ; Check the documentation at https://content-security-policy.com/
-; Note: If you use a bootstrap theme, you can remove the allow-popups from the sandbox restrictions.
-; By default this disallows to load images from third-party servers, e.g. when they are embedded in pastes. If you wish to allow that, you can adjust the policy here. See https://github.com/PrivateBin/PrivateBin/wiki/FAQ#why-does-not-it-load-embedded-images for details.
+; Notes:
+; - If you use a bootstrap theme, you can remove the allow-popups from the
+;   sandbox restrictions.
+; - By default this disallows to load images from third-party servers, e.g. when
+;   they are embedded in pastes. If you wish to allow that, you can adjust the
+;   policy here. See https://github.com/PrivateBin/PrivateBin/wiki/FAQ#why-does-not-it-load-embedded-images
+;   for details.
+; - The 'unsafe-eval' is used in two cases; to check if the browser supports
+;   async functions and display an error if not and for Chrome to enable
+;   webassembly support (used for zlib compression). You can remove it if Chrome
+;   doesn't need to be supported and old browsers don't need to be warned.
 ; cspheader = "default-src 'none'; manifest-src 'self'; connect-src * blob:; script-src 'self' 'unsafe-eval'; style-src 'self'; font-src 'self'; img-src 'self' data: blob:; media-src blob:; object-src blob:; sandbox allow-same-origin allow-scripts allow-forms allow-popups allow-modals"
 ; cspheader = "default-src 'none'; manifest-src 'self'; connect-src * blob:; script-src 'self' 'unsafe-eval'; style-src 'self'; font-src 'self'; img-src 'self' data: blob:; media-src blob:; object-src blob:; sandbox allow-same-origin allow-scripts allow-forms allow-popups allow-modals"
 
 
 ; stay compatible with PrivateBin Alpha 0.19, less secure
 ; stay compatible with PrivateBin Alpha 0.19, less secure

+ 3 - 4
i18n/bg.json

@@ -35,8 +35,6 @@
         "Услугата %s се нуждае от JavaScript, за да работи.<br />Съжаляваме за неудобството.",
         "Услугата %s се нуждае от JavaScript, за да работи.<br />Съжаляваме за неудобството.",
     "%s requires a modern browser to work.":
     "%s requires a modern browser to work.":
         "%s се нуждае от съвременен браузър за да работи.",
         "%s се нуждае от съвременен браузър за да работи.",
-    "Still using Internet Explorer? Do yourself a favor, switch to a modern browser:":
-        "Все още използваш Internet Explorer? Направи си услуга и го смени с модерен браузър:",
     "New":
     "New":
         "Създаване",
         "Създаване",
     "Send":
     "Send":
@@ -155,11 +153,12 @@
     "Could not get paste data: %s":
     "Could not get paste data: %s":
         "Взимането на информацията беше неуспешно: %s",
         "Взимането на информацията беше неуспешно: %s",
     "QR code": "QR код",
     "QR code": "QR код",
-    "I love you too, bot…": "И аз те обичам, бот…",
     "This website is using an insecure HTTP connection! Please use it only for testing.":
     "This website is using an insecure HTTP connection! Please use it only for testing.":
         "Този сайт използва несигурна HTTP връзка. Моля използвайте само за проби.",
         "Този сайт използва несигурна HTTP връзка. Моля използвайте само за проби.",
     "For more information <a href=\"%s\">see this FAQ entry</a>.":
     "For more information <a href=\"%s\">see this FAQ entry</a>.":
         "<a href=\"%s\">Вижте тази страница</a> за повече информация.",
         "<a href=\"%s\">Вижте тази страница</a> за повече информация.",
     "Your browser may require an HTTPS connection to support the WebCrypto API. Try <a href=\"%s\">switching to HTTPS</a>.":
     "Your browser may require an HTTPS connection to support the WebCrypto API. Try <a href=\"%s\">switching to HTTPS</a>.":
-        "Браузъра ви може да се нуждае от HTTPS връзка за да използва WebCrypto API. Пробвай <a href=\"%s\">да минеш на HTTPS</a>."
+        "Браузъра ви може да се нуждае от HTTPS връзка за да използва WebCrypto API. Пробвай <a href=\"%s\">да минеш на HTTPS</a>.",
+    "Your browser doesn't support WebAssembly, used for zlib compression. You can create uncompressed documents, but can't read compressed ones.":
+        "Your browser doesn't support WebAssembly, used for zlib compression. You can create uncompressed documents, but can't read compressed ones."
 }
 }

+ 3 - 4
i18n/cs.json

@@ -35,8 +35,6 @@
         "JavaScript is required for %s to work.<br />Sorry for the inconvenience.",
         "JavaScript is required for %s to work.<br />Sorry for the inconvenience.",
     "%s requires a modern browser to work.":
     "%s requires a modern browser to work.":
         "%%s requires a modern browser to work.",
         "%%s requires a modern browser to work.",
-    "Still using Internet Explorer? Do yourself a favor, switch to a modern browser:":
-        "Still using Internet Explorer? Do yourself a favor, switch to a modern browser:",
     "New":
     "New":
         "Nový",
         "Nový",
     "Send":
     "Send":
@@ -155,11 +153,12 @@
     "Could not get paste data: %s":
     "Could not get paste data: %s":
         "Could not get paste data: %s",
         "Could not get paste data: %s",
     "QR code": "QR code",
     "QR code": "QR code",
-    "I love you too, bot…": "I love you too, bot…",
     "This website is using an insecure HTTP connection! Please use it only for testing.":
     "This website is using an insecure HTTP connection! Please use it only for testing.":
         "This website is using an insecure HTTP connection! Please use it only for testing.",
         "This website is using an insecure HTTP connection! Please use it only for testing.",
     "For more information <a href=\"%s\">see this FAQ entry</a>.":
     "For more information <a href=\"%s\">see this FAQ entry</a>.":
         "For more information <a href=\"%s\">see this FAQ entry</a>.",
         "For more information <a href=\"%s\">see this FAQ entry</a>.",
     "Your browser may require an HTTPS connection to support the WebCrypto API. Try <a href=\"%s\">switching to HTTPS</a>.":
     "Your browser may require an HTTPS connection to support the WebCrypto API. Try <a href=\"%s\">switching to HTTPS</a>.":
-        "Your browser may require an HTTPS connection to support the WebCrypto API. Try <a href=\"%s\">switching to HTTPS</a>."
+        "Your browser may require an HTTPS connection to support the WebCrypto API. Try <a href=\"%s\">switching to HTTPS</a>.",
+    "Your browser doesn't support WebAssembly, used for zlib compression. You can create uncompressed documents, but can't read compressed ones.":
+        "Your browser doesn't support WebAssembly, used for zlib compression. You can create uncompressed documents, but can't read compressed ones."
 }
 }

+ 3 - 4
i18n/de.json

@@ -35,8 +35,6 @@
         "JavaScript ist eine Voraussetzung, um %s zu nutzen.<br />Bitte entschuldige die Unannehmlichkeiten.",
         "JavaScript ist eine Voraussetzung, um %s zu nutzen.<br />Bitte entschuldige die Unannehmlichkeiten.",
     "%s requires a modern browser to work.":
     "%s requires a modern browser to work.":
         "%s setzt einen modernen Browser voraus, um funktionieren zu können.",
         "%s setzt einen modernen Browser voraus, um funktionieren zu können.",
-    "Still using Internet Explorer? Do yourself a favor, switch to a modern browser:":
-        "Du benutzt immer noch den Internet Explorer? Tu Dir einen Gefallen und wechsle zu einem moderneren Browser:",
     "New":
     "New":
         "Neu",
         "Neu",
     "Send":
     "Send":
@@ -155,11 +153,12 @@
     "Could not get paste data: %s":
     "Could not get paste data: %s":
         "Text konnte nicht geladen werden: %s",
         "Text konnte nicht geladen werden: %s",
     "QR code": "QR code",
     "QR code": "QR code",
-    "I love you too, bot…": "Ich mag Dich auch, bot…",
     "This website is using an insecure HTTP connection! Please use it only for testing.":
     "This website is using an insecure HTTP connection! Please use it only for testing.":
         "Diese Webseite verwendet eine unsichere HTTP Verbindung! Bitte benutze sie nur zum Testen.",
         "Diese Webseite verwendet eine unsichere HTTP Verbindung! Bitte benutze sie nur zum Testen.",
     "For more information <a href=\"%s\">see this FAQ entry</a>.":
     "For more information <a href=\"%s\">see this FAQ entry</a>.":
         "<a href=\"%s\">Besuche diesen FAQ Eintrag</a> für weitere Informationen dazu.",
         "<a href=\"%s\">Besuche diesen FAQ Eintrag</a> für weitere Informationen dazu.",
     "Your browser may require an HTTPS connection to support the WebCrypto API. Try <a href=\"%s\">switching to HTTPS</a>.":
     "Your browser may require an HTTPS connection to support the WebCrypto API. Try <a href=\"%s\">switching to HTTPS</a>.":
-        "Dein Browser benötigt möglicherweise eine HTTPS Verbindung um das WebCrypto API nutzen zu können. Versuche <a href=\"%s\">auf HTTPS zu wechseln</a>."
+        "Dein Browser benötigt möglicherweise eine HTTPS Verbindung um das WebCrypto API nutzen zu können. Versuche <a href=\"%s\">auf HTTPS zu wechseln</a>.",
+    "Your browser doesn't support WebAssembly, used for zlib compression. You can create uncompressed documents, but can't read compressed ones.":
+        "Dein Browser unterstützt WebAssembly nicht, welches für zlib Komprimierung benötigt wird. Du kannst unkomprimierte Dokumente erzeugen, aber keine komprimierten lesen."
 }
 }

+ 3 - 4
i18n/es.json

@@ -35,8 +35,6 @@
         "JavaScript es necesario para que %s funcione.<br />Sentimos los inconvenientes ocasionados.",
         "JavaScript es necesario para que %s funcione.<br />Sentimos los inconvenientes ocasionados.",
     "%s requires a modern browser to work.":
     "%s requires a modern browser to work.":
         "%s requiere un navegador moderno para funcionar.",
         "%s requiere un navegador moderno para funcionar.",
-    "Still using Internet Explorer? Do yourself a favor, switch to a modern browser:":
-        "¿Sigues usando Internet Explorer? Hazte un favor, cambia a un navegador moderno:",
     "New":
     "New":
         "Nuevo",
         "Nuevo",
     "Send":
     "Send":
@@ -155,11 +153,12 @@
     "Could not get paste data: %s":
     "Could not get paste data: %s":
         "No se pudieron obtener los datos: %s",
         "No se pudieron obtener los datos: %s",
     "QR code": "Código QR",
     "QR code": "Código QR",
-    "I love you too, bot…": "I love you too, bot…",
     "This website is using an insecure HTTP connection! Please use it only for testing.":
     "This website is using an insecure HTTP connection! Please use it only for testing.":
         "This website is using an insecure HTTP connection! Please use it only for testing.",
         "This website is using an insecure HTTP connection! Please use it only for testing.",
     "For more information <a href=\"%s\">see this FAQ entry</a>.":
     "For more information <a href=\"%s\">see this FAQ entry</a>.":
         "For more information <a href=\"%s\">see this FAQ entry</a>.",
         "For more information <a href=\"%s\">see this FAQ entry</a>.",
     "Your browser may require an HTTPS connection to support the WebCrypto API. Try <a href=\"%s\">switching to HTTPS</a>.":
     "Your browser may require an HTTPS connection to support the WebCrypto API. Try <a href=\"%s\">switching to HTTPS</a>.":
-        "Your browser may require an HTTPS connection to support the WebCrypto API. Try <a href=\"%s\">switching to HTTPS</a>."
+        "Your browser may require an HTTPS connection to support the WebCrypto API. Try <a href=\"%s\">switching to HTTPS</a>.",
+    "Your browser doesn't support WebAssembly, used for zlib compression. You can create uncompressed documents, but can't read compressed ones.":
+        "Your browser doesn't support WebAssembly, used for zlib compression. You can create uncompressed documents, but can't read compressed ones."
 }
 }

+ 3 - 4
i18n/fr.json

@@ -35,8 +35,6 @@
         "JavaScript est requis pour faire fonctionner %s. <br />Désolé pour cet inconvénient.",
         "JavaScript est requis pour faire fonctionner %s. <br />Désolé pour cet inconvénient.",
     "%s requires a modern browser to work.":
     "%s requires a modern browser to work.":
         "%s nécessite un navigateur moderne pour fonctionner.",
         "%s nécessite un navigateur moderne pour fonctionner.",
-    "Still using Internet Explorer? Do yourself a favor, switch to a modern browser:":
-        "Encore sur Internet Explorer ? Faites-vous une faveur, passez à un navigateur moderne :",
     "New":
     "New":
         "Nouveau",
         "Nouveau",
     "Send":
     "Send":
@@ -164,11 +162,12 @@
     "Could not get paste data: %s":
     "Could not get paste data: %s":
         "Impossible d'obtenir les données du paste: %s",
         "Impossible d'obtenir les données du paste: %s",
     "QR code": "QR code",
     "QR code": "QR code",
-    "I love you too, bot…": "Je t’aime aussi, bot…",
     "This website is using an insecure HTTP connection! Please use it only for testing.":
     "This website is using an insecure HTTP connection! Please use it only for testing.":
         "Ce site web utilise une connexion HTTP non sécurisée ! Veuillez l’utiliser uniquement pour des tests.",
         "Ce site web utilise une connexion HTTP non sécurisée ! Veuillez l’utiliser uniquement pour des tests.",
     "For more information <a href=\"%s\">see this FAQ entry</a>.":
     "For more information <a href=\"%s\">see this FAQ entry</a>.":
         "Pour plus d'informations <a href=\"%s\">consultez cette rubrique de la FAQ</a>.",
         "Pour plus d'informations <a href=\"%s\">consultez cette rubrique de la FAQ</a>.",
     "Your browser may require an HTTPS connection to support the WebCrypto API. Try <a href=\"%s\">switching to HTTPS</a>.":
     "Your browser may require an HTTPS connection to support the WebCrypto API. Try <a href=\"%s\">switching to HTTPS</a>.":
-        "Votre navigateur peut nécessiter une connexion HTTPS pour prendre en charge l’API WebCrypto. Essayez <a href=\"%s\">de passer en HTTPS</a>."
+        "Votre navigateur peut nécessiter une connexion HTTPS pour prendre en charge l’API WebCrypto. Essayez <a href=\"%s\">de passer en HTTPS</a>.",
+    "Your browser doesn't support WebAssembly, used for zlib compression. You can create uncompressed documents, but can't read compressed ones.":
+        "Your browser doesn't support WebAssembly, used for zlib compression. You can create uncompressed documents, but can't read compressed ones."
 }
 }

+ 3 - 4
i18n/hu.json

@@ -35,8 +35,6 @@
         "JavaScript szükséges a %s működéséhez. Elnézést a fennakadásért.",
         "JavaScript szükséges a %s működéséhez. Elnézést a fennakadásért.",
     "%s requires a modern browser to work.":
     "%s requires a modern browser to work.":
         "A %s működéséhez a jelenleginél újabb böngészőre van szükség.",
         "A %s működéséhez a jelenleginél újabb böngészőre van szükség.",
-    "Still using Internet Explorer? Do yourself a favor, switch to a modern browser:":
-        "Még mindig Internet Explorert használsz? Ideje váltani:",
     "New":
     "New":
         "Új",
         "Új",
     "Send":
     "Send":
@@ -155,11 +153,12 @@
     "Could not get paste data: %s":
     "Could not get paste data: %s":
         "Could not get paste data: %s",
         "Could not get paste data: %s",
     "QR code": "QR code",
     "QR code": "QR code",
-    "I love you too, bot…": "I love you too, bot…",
     "This website is using an insecure HTTP connection! Please use it only for testing.":
     "This website is using an insecure HTTP connection! Please use it only for testing.":
         "This website is using an insecure HTTP connection! Please use it only for testing.",
         "This website is using an insecure HTTP connection! Please use it only for testing.",
     "For more information <a href=\"%s\">see this FAQ entry</a>.":
     "For more information <a href=\"%s\">see this FAQ entry</a>.":
         "For more information <a href=\"%s\">see this FAQ entry</a>.",
         "For more information <a href=\"%s\">see this FAQ entry</a>.",
     "Your browser may require an HTTPS connection to support the WebCrypto API. Try <a href=\"%s\">switching to HTTPS</a>.":
     "Your browser may require an HTTPS connection to support the WebCrypto API. Try <a href=\"%s\">switching to HTTPS</a>.":
-        "Your browser may require an HTTPS connection to support the WebCrypto API. Try <a href=\"%s\">switching to HTTPS</a>."
+        "Your browser may require an HTTPS connection to support the WebCrypto API. Try <a href=\"%s\">switching to HTTPS</a>.",
+    "Your browser doesn't support WebAssembly, used for zlib compression. You can create uncompressed documents, but can't read compressed ones.":
+        "Your browser doesn't support WebAssembly, used for zlib compression. You can create uncompressed documents, but can't read compressed ones."
 }
 }

+ 3 - 4
i18n/it.json

@@ -35,8 +35,6 @@
         "%s funziona solo con JavaScript attivo.<br />Ci dispiace per l'inconveniente.",
         "%s funziona solo con JavaScript attivo.<br />Ci dispiace per l'inconveniente.",
     "%s requires a modern browser to work.":
     "%s requires a modern browser to work.":
         "%s richiede un browser moderno e aggiornato per funzionare.",
         "%s richiede un browser moderno e aggiornato per funzionare.",
-    "Still using Internet Explorer? Do yourself a favor, switch to a modern browser:":
-        "Usi ancora Internet Explorer? Ti consigliamo di passare ad un browser più sicuro:",
     "New":
     "New":
         "Nuovo",
         "Nuovo",
     "Send":
     "Send":
@@ -155,11 +153,12 @@
     "Could not get paste data: %s":
     "Could not get paste data: %s":
         "Could not get paste data: %s",
         "Could not get paste data: %s",
     "QR code": "QR code",
     "QR code": "QR code",
-    "I love you too, bot…": "I love you too, bot…",
     "This website is using an insecure HTTP connection! Please use it only for testing.":
     "This website is using an insecure HTTP connection! Please use it only for testing.":
         "This website is using an insecure HTTP connection! Please use it only for testing.",
         "This website is using an insecure HTTP connection! Please use it only for testing.",
     "For more information <a href=\"%s\">see this FAQ entry</a>.":
     "For more information <a href=\"%s\">see this FAQ entry</a>.":
         "For more information <a href=\"%s\">see this FAQ entry</a>.",
         "For more information <a href=\"%s\">see this FAQ entry</a>.",
     "Your browser may require an HTTPS connection to support the WebCrypto API. Try <a href=\"%s\">switching to HTTPS</a>.":
     "Your browser may require an HTTPS connection to support the WebCrypto API. Try <a href=\"%s\">switching to HTTPS</a>.":
-        "Your browser may require an HTTPS connection to support the WebCrypto API. Try <a href=\"%s\">switching to HTTPS</a>."
+        "Your browser may require an HTTPS connection to support the WebCrypto API. Try <a href=\"%s\">switching to HTTPS</a>.",
+    "Your browser doesn't support WebAssembly, used for zlib compression. You can create uncompressed documents, but can't read compressed ones.":
+        "Your browser doesn't support WebAssembly, used for zlib compression. You can create uncompressed documents, but can't read compressed ones."
 }
 }

+ 3 - 4
i18n/nl.json

@@ -35,8 +35,6 @@
         "JavaScript vereist om %s te laten werken.<br />Sorry voor het ongemak.",
         "JavaScript vereist om %s te laten werken.<br />Sorry voor het ongemak.",
     "%s requires a modern browser to work.":
     "%s requires a modern browser to work.":
         "%s vereist een moderne browser om te kunnen werken ",
         "%s vereist een moderne browser om te kunnen werken ",
-    "Still using Internet Explorer? Do yourself a favor, switch to a modern browser:":
-        "Gebruik je nog steeds Internet explorer? Doe jezelf een plezier en maak gebruik van een moderne browser:",
     "New":
     "New":
         "Nieuw",
         "Nieuw",
     "Send":
     "Send":
@@ -155,11 +153,12 @@
     "Could not get paste data: %s":
     "Could not get paste data: %s":
         "Could not get paste data: %s",
         "Could not get paste data: %s",
     "QR code": "QR code",
     "QR code": "QR code",
-    "I love you too, bot…": "I love you too, bot…",
     "This website is using an insecure HTTP connection! Please use it only for testing.":
     "This website is using an insecure HTTP connection! Please use it only for testing.":
         "This website is using an insecure HTTP connection! Please use it only for testing.",
         "This website is using an insecure HTTP connection! Please use it only for testing.",
     "For more information <a href=\"%s\">see this FAQ entry</a>.":
     "For more information <a href=\"%s\">see this FAQ entry</a>.":
         "For more information <a href=\"%s\">see this FAQ entry</a>.",
         "For more information <a href=\"%s\">see this FAQ entry</a>.",
     "Your browser may require an HTTPS connection to support the WebCrypto API. Try <a href=\"%s\">switching to HTTPS</a>.":
     "Your browser may require an HTTPS connection to support the WebCrypto API. Try <a href=\"%s\">switching to HTTPS</a>.":
-        "Your browser may require an HTTPS connection to support the WebCrypto API. Try <a href=\"%s\">switching to HTTPS</a>."
+        "Your browser may require an HTTPS connection to support the WebCrypto API. Try <a href=\"%s\">switching to HTTPS</a>.",
+    "Your browser doesn't support WebAssembly, used for zlib compression. You can create uncompressed documents, but can't read compressed ones.":
+        "Your browser doesn't support WebAssembly, used for zlib compression. You can create uncompressed documents, but can't read compressed ones."
 }
 }

+ 3 - 4
i18n/no.json

@@ -35,8 +35,6 @@
         "Javascript kreves for at %s skal fungere<br />Beklager.",
         "Javascript kreves for at %s skal fungere<br />Beklager.",
     "%s requires a modern browser to work.":
     "%s requires a modern browser to work.":
         "%s krever en moderne nettleser for å fungere.",
         "%s krever en moderne nettleser for å fungere.",
-    "Still using Internet Explorer? Do yourself a favor, switch to a modern browser:":
-        "Fortsatt bruker av Internet Explorer? Gjør deg selv en tjeneste og bytt til en moderne nettleser:",
     "New":
     "New":
         "Ny",
         "Ny",
     "Send":
     "Send":
@@ -155,11 +153,12 @@
     "Could not get paste data: %s":
     "Could not get paste data: %s":
         "Kunne ikke hente utklippsdata: %s",
         "Kunne ikke hente utklippsdata: %s",
     "QR code": "QR kode",
     "QR code": "QR kode",
-    "I love you too, bot…": "Jeg elsker deg også, bot…",
     "This website is using an insecure HTTP connection! Please use it only for testing.":
     "This website is using an insecure HTTP connection! Please use it only for testing.":
         "Denne websiden bruker usikker HTTP tilkobling! Bruk den kun for testing.",
         "Denne websiden bruker usikker HTTP tilkobling! Bruk den kun for testing.",
     "For more information <a href=\"%s\">see this FAQ entry</a>.":
     "For more information <a href=\"%s\">see this FAQ entry</a>.":
         "For mer informasjon <a href=\"%s\">se ofte stilte spørsmål</a>.",
         "For mer informasjon <a href=\"%s\">se ofte stilte spørsmål</a>.",
     "Your browser may require an HTTPS connection to support the WebCrypto API. Try <a href=\"%s\">switching to HTTPS</a>.":
     "Your browser may require an HTTPS connection to support the WebCrypto API. Try <a href=\"%s\">switching to HTTPS</a>.":
-        "Din nettleser har behov for HTTPS tilkobling for å støtte WebCrypto biblioteket. Prøv å <a href=\"%s\">bytt til HTTPS</a>."
+        "Din nettleser har behov for HTTPS tilkobling for å støtte WebCrypto biblioteket. Prøv å <a href=\"%s\">bytt til HTTPS</a>.",
+    "Your browser doesn't support WebAssembly, used for zlib compression. You can create uncompressed documents, but can't read compressed ones.":
+        "Your browser doesn't support WebAssembly, used for zlib compression. You can create uncompressed documents, but can't read compressed ones."
 }
 }

+ 3 - 4
i18n/oc.json

@@ -35,8 +35,6 @@
         "JavaScript es requesit per far foncionar %s. <br />O planhèm per l’inconvenient.",
         "JavaScript es requesit per far foncionar %s. <br />O planhèm per l’inconvenient.",
     "%s requires a modern browser to work.":
     "%s requires a modern browser to work.":
         "%s necessita un navigator modèrn per foncionar.",
         "%s necessita un navigator modèrn per foncionar.",
-    "Still using Internet Explorer? Do yourself a favor, switch to a modern browser:":
-        "Encora sus Internet Explorer ? Fasètz-vos una favor, passatz a un navigator modèrn :",
     "New":
     "New":
         "Nòu",
         "Nòu",
     "Send":
     "Send":
@@ -164,11 +162,12 @@
     "Could not get paste data: %s":
     "Could not get paste data: %s":
         "Recuperacion impossibla de las donadas copiadas : %s",
         "Recuperacion impossibla de las donadas copiadas : %s",
     "QR code": "Còdi QR",
     "QR code": "Còdi QR",
-    "I love you too, bot…": "T’aimi tanben, robòt…",
     "This website is using an insecure HTTP connection! Please use it only for testing.":
     "This website is using an insecure HTTP connection! Please use it only for testing.":
         "Aqueste site utiliza una connexion HTTP pas segura ! Mercés de l’utilizar pas que per d’ensages.",
         "Aqueste site utiliza una connexion HTTP pas segura ! Mercés de l’utilizar pas que per d’ensages.",
     "For more information <a href=\"%s\">see this FAQ entry</a>.":
     "For more information <a href=\"%s\">see this FAQ entry</a>.":
         "Per mai d’informacions <a href=\"%s\">vejatz aqueste article de FAQ</a>.",
         "Per mai d’informacions <a href=\"%s\">vejatz aqueste article de FAQ</a>.",
     "Your browser may require an HTTPS connection to support the WebCrypto API. Try <a href=\"%s\">switching to HTTPS</a>.":
     "Your browser may require an HTTPS connection to support the WebCrypto API. Try <a href=\"%s\">switching to HTTPS</a>.":
-        "Se pòt que vòstre navigator faga besonh d’una connexion HTTPS per èsser compatible amb l’API WebCrypto. Ensajatz de <a href=\"%s\">passar al HTTPS</a>."
+        "Se pòt que vòstre navigator faga besonh d’una connexion HTTPS per èsser compatible amb l’API WebCrypto. Ensajatz de <a href=\"%s\">passar al HTTPS</a>.",
+    "Your browser doesn't support WebAssembly, used for zlib compression. You can create uncompressed documents, but can't read compressed ones.":
+        "Your browser doesn't support WebAssembly, used for zlib compression. You can create uncompressed documents, but can't read compressed ones."
 }
 }

+ 3 - 4
i18n/pl.json

@@ -35,8 +35,6 @@
         "Do działania %sa jest wymagany JavaScript. Przepraszamy za tę niedogodność.",
         "Do działania %sa jest wymagany JavaScript. Przepraszamy za tę niedogodność.",
     "%s requires a modern browser to work.":
     "%s requires a modern browser to work.":
         "%s wymaga do działania nowoczesnej przeglądarki.",
         "%s wymaga do działania nowoczesnej przeglądarki.",
-    "Still using Internet Explorer? Do yourself a favor, switch to a modern browser:":
-        "Cały czas używasz Internet Explorera? Zrób sobie przysługę, przesiądź się na nowoczesną przeglądarkę:",
     "New":
     "New":
         "Nowa",
         "Nowa",
     "Send":
     "Send":
@@ -155,11 +153,12 @@
     "Could not get paste data: %s":
     "Could not get paste data: %s":
         "Nie można było pobrać danych wklejki: %s",
         "Nie można było pobrać danych wklejki: %s",
     "QR code": "Kod QR",
     "QR code": "Kod QR",
-    "I love you too, bot…": "I love you too, bot…",
     "This website is using an insecure HTTP connection! Please use it only for testing.":
     "This website is using an insecure HTTP connection! Please use it only for testing.":
         "This website is using an insecure HTTP connection! Please use it only for testing.",
         "This website is using an insecure HTTP connection! Please use it only for testing.",
     "For more information <a href=\"%s\">see this FAQ entry</a>.":
     "For more information <a href=\"%s\">see this FAQ entry</a>.":
         "For more information <a href=\"%s\">see this FAQ entry</a>.",
         "For more information <a href=\"%s\">see this FAQ entry</a>.",
     "Your browser may require an HTTPS connection to support the WebCrypto API. Try <a href=\"%s\">switching to HTTPS</a>.":
     "Your browser may require an HTTPS connection to support the WebCrypto API. Try <a href=\"%s\">switching to HTTPS</a>.":
-        "Your browser may require an HTTPS connection to support the WebCrypto API. Try <a href=\"%s\">switching to HTTPS</a>."
+        "Your browser may require an HTTPS connection to support the WebCrypto API. Try <a href=\"%s\">switching to HTTPS</a>.",
+    "Your browser doesn't support WebAssembly, used for zlib compression. You can create uncompressed documents, but can't read compressed ones.":
+        "Your browser doesn't support WebAssembly, used for zlib compression. You can create uncompressed documents, but can't read compressed ones."
 }
 }

+ 3 - 4
i18n/pt.json

@@ -35,8 +35,6 @@
         "JavaScript é necessário para que %s funcione.<br />Pedimos desculpas pela inconveniência.",
         "JavaScript é necessário para que %s funcione.<br />Pedimos desculpas pela inconveniência.",
     "%s requires a modern browser to work.":
     "%s requires a modern browser to work.":
         "%s requer um navegador moderno para funcionar.",
         "%s requer um navegador moderno para funcionar.",
-    "Still using Internet Explorer? Do yourself a favor, switch to a modern browser:":
-        "Ainda usando Internet Explorer? Faça-se um favor, mude para um navegador moderno:",
     "New":
     "New":
         "Novo",
         "Novo",
     "Send":
     "Send":
@@ -155,11 +153,12 @@
     "Could not get paste data: %s":
     "Could not get paste data: %s":
         "Could not get paste data: %s",
         "Could not get paste data: %s",
     "QR code": "QR code",
     "QR code": "QR code",
-    "I love you too, bot…": "I love you too, bot…",
     "This website is using an insecure HTTP connection! Please use it only for testing.":
     "This website is using an insecure HTTP connection! Please use it only for testing.":
         "This website is using an insecure HTTP connection! Please use it only for testing.",
         "This website is using an insecure HTTP connection! Please use it only for testing.",
     "For more information <a href=\"%s\">see this FAQ entry</a>.":
     "For more information <a href=\"%s\">see this FAQ entry</a>.":
         "For more information <a href=\"%s\">see this FAQ entry</a>.",
         "For more information <a href=\"%s\">see this FAQ entry</a>.",
     "Your browser may require an HTTPS connection to support the WebCrypto API. Try <a href=\"%s\">switching to HTTPS</a>.":
     "Your browser may require an HTTPS connection to support the WebCrypto API. Try <a href=\"%s\">switching to HTTPS</a>.":
-        "Your browser may require an HTTPS connection to support the WebCrypto API. Try <a href=\"%s\">switching to HTTPS</a>."
+        "Your browser may require an HTTPS connection to support the WebCrypto API. Try <a href=\"%s\">switching to HTTPS</a>.",
+    "Your browser doesn't support WebAssembly, used for zlib compression. You can create uncompressed documents, but can't read compressed ones.":
+        "Your browser doesn't support WebAssembly, used for zlib compression. You can create uncompressed documents, but can't read compressed ones."
 }
 }

+ 3 - 4
i18n/ru.json

@@ -35,8 +35,6 @@
         "Для работы %s требуется включенный JavaScript.<br />Приносим извинения за неудобства.",
         "Для работы %s требуется включенный JavaScript.<br />Приносим извинения за неудобства.",
     "%s requires a modern browser to work.":
     "%s requires a modern browser to work.":
         "Для работы %s требуется более современный браузер.",
         "Для работы %s требуется более современный браузер.",
-    "Still using Internet Explorer? Do yourself a favor, switch to a modern browser:":
-        "До сих пор используете Internet Explorer? Пожалейте себя, перейдите на более современный браузер:",
     "New":
     "New":
         "Новая запись",
         "Новая запись",
     "Send":
     "Send":
@@ -165,11 +163,12 @@
     "Could not get paste data: %s":
     "Could not get paste data: %s":
         "Не удалось получить данные записи: %s",
         "Не удалось получить данные записи: %s",
     "QR code": "QR код",
     "QR code": "QR код",
-    "I love you too, bot…": "Я тоже люблю тебя, бот…",
     "This website is using an insecure HTTP connection! Please use it only for testing.":
     "This website is using an insecure HTTP connection! Please use it only for testing.":
         "Данный сайт использует незащищенное HTTP подключение! Пожалуйста используйте его только для тестирования.",
         "Данный сайт использует незащищенное HTTP подключение! Пожалуйста используйте его только для тестирования.",
     "For more information <a href=\"%s\">see this FAQ entry</a>.":
     "For more information <a href=\"%s\">see this FAQ entry</a>.":
         "Для продробностей <a href=\"%s\">прочтите информацию в FAQ</a>.",
         "Для продробностей <a href=\"%s\">прочтите информацию в FAQ</a>.",
     "Your browser may require an HTTPS connection to support the WebCrypto API. Try <a href=\"%s\">switching to HTTPS</a>.":
     "Your browser may require an HTTPS connection to support the WebCrypto API. Try <a href=\"%s\">switching to HTTPS</a>.":
-        "Ваш браузер требует использования HTTPS подключения для поддержки WebCrypto API. Попробуйте <a href=\"%s\">переключиться на HTTPS</a>."
+        "Ваш браузер требует использования HTTPS подключения для поддержки WebCrypto API. Попробуйте <a href=\"%s\">переключиться на HTTPS</a>.",
+    "Your browser doesn't support WebAssembly, used for zlib compression. You can create uncompressed documents, but can't read compressed ones.":
+        "Your browser doesn't support WebAssembly, used for zlib compression. You can create uncompressed documents, but can't read compressed ones."
 }
 }

+ 3 - 4
i18n/sl.json

@@ -35,8 +35,6 @@
         "Da %s deluje, moraš vklopiti JavaScript.<br />Oprosti za povročene nevšečnosti.",
         "Da %s deluje, moraš vklopiti JavaScript.<br />Oprosti za povročene nevšečnosti.",
     "%s requires a modern browser to work.":
     "%s requires a modern browser to work.":
         "%s za svoje delovanje potrebuje moderen brskalnik.",
         "%s za svoje delovanje potrebuje moderen brskalnik.",
-    "Still using Internet Explorer? Do yourself a favor, switch to a modern browser:":
-        "Še vedno uporabljaš Internet Explorer? Naredi si uslugo, preklopi na moderen brskalnik:",
     "New":
     "New":
         "Nov prilepek",
         "Nov prilepek",
     "Send":
     "Send":
@@ -164,11 +162,12 @@
     "Could not get paste data: %s":
     "Could not get paste data: %s":
         "Could not get paste data: %s",
         "Could not get paste data: %s",
     "QR code": "QR code",
     "QR code": "QR code",
-    "I love you too, bot…": "I love you too, bot…",
     "This website is using an insecure HTTP connection! Please use it only for testing.":
     "This website is using an insecure HTTP connection! Please use it only for testing.":
         "This website is using an insecure HTTP connection! Please use it only for testing.",
         "This website is using an insecure HTTP connection! Please use it only for testing.",
     "For more information <a href=\"%s\">see this FAQ entry</a>.":
     "For more information <a href=\"%s\">see this FAQ entry</a>.":
         "For more information <a href=\"%s\">see this FAQ entry</a>.",
         "For more information <a href=\"%s\">see this FAQ entry</a>.",
     "Your browser may require an HTTPS connection to support the WebCrypto API. Try <a href=\"%s\">switching to HTTPS</a>.":
     "Your browser may require an HTTPS connection to support the WebCrypto API. Try <a href=\"%s\">switching to HTTPS</a>.":
-        "Your browser may require an HTTPS connection to support the WebCrypto API. Try <a href=\"%s\">switching to HTTPS</a>."
+        "Your browser may require an HTTPS connection to support the WebCrypto API. Try <a href=\"%s\">switching to HTTPS</a>.",
+    "Your browser doesn't support WebAssembly, used for zlib compression. You can create uncompressed documents, but can't read compressed ones.":
+        "Your browser doesn't support WebAssembly, used for zlib compression. You can create uncompressed documents, but can't read compressed ones."
 }
 }

+ 3 - 4
i18n/zh.json

@@ -35,8 +35,6 @@
         "%s需要JavaScript来进行加解密。<br />给你带来的不便敬请谅解。",
         "%s需要JavaScript来进行加解密。<br />给你带来的不便敬请谅解。",
     "%s requires a modern browser to work.":
     "%s requires a modern browser to work.":
         "%s需要在现代浏览器上工作。",
         "%s需要在现代浏览器上工作。",
-    "Still using Internet Explorer? Do yourself a favor, switch to a modern browser:":
-        "还在使用Internet Explorer?对自己好点,换一个现代浏览器:",
     "New":
     "New":
         "新建",
         "新建",
     "Send":
     "Send":
@@ -155,11 +153,12 @@
     "Could not get paste data: %s":
     "Could not get paste data: %s":
         "无法获取粘贴数据:%s",
         "无法获取粘贴数据:%s",
     "QR code": "二维码",
     "QR code": "二维码",
-    "I love you too, bot…": "I love you too, bot…",
     "This website is using an insecure HTTP connection! Please use it only for testing.":
     "This website is using an insecure HTTP connection! Please use it only for testing.":
         "This website is using an insecure HTTP connection! Please use it only for testing.",
         "This website is using an insecure HTTP connection! Please use it only for testing.",
     "For more information <a href=\"%s\">see this FAQ entry</a>.":
     "For more information <a href=\"%s\">see this FAQ entry</a>.":
         "For more information <a href=\"%s\">see this FAQ entry</a>.",
         "For more information <a href=\"%s\">see this FAQ entry</a>.",
     "Your browser may require an HTTPS connection to support the WebCrypto API. Try <a href=\"%s\">switching to HTTPS</a>.":
     "Your browser may require an HTTPS connection to support the WebCrypto API. Try <a href=\"%s\">switching to HTTPS</a>.":
-        "Your browser may require an HTTPS connection to support the WebCrypto API. Try <a href=\"%s\">switching to HTTPS</a>."
+        "Your browser may require an HTTPS connection to support the WebCrypto API. Try <a href=\"%s\">switching to HTTPS</a>.",
+    "Your browser doesn't support WebAssembly, used for zlib compression. You can create uncompressed documents, but can't read compressed ones.":
+        "Your browser doesn't support WebAssembly, used for zlib compression. You can create uncompressed documents, but can't read compressed ones."
 }
 }

+ 1 - 0
js/common.js

@@ -19,6 +19,7 @@ global.prettyPrintOne = window.PR.prettyPrintOne;
 global.showdown = require('./showdown-1.9.1');
 global.showdown = require('./showdown-1.9.1');
 global.DOMPurify = require('./purify-2.0.1');
 global.DOMPurify = require('./purify-2.0.1');
 global.baseX = require('./base-x-3.0.5.1').baseX;
 global.baseX = require('./base-x-3.0.5.1').baseX;
+global.Legacy = require('./legacy').Legacy;
 require('./bootstrap-3.3.7');
 require('./bootstrap-3.3.7');
 require('./privatebin');
 require('./privatebin');
 
 

+ 309 - 0
js/legacy.js

@@ -0,0 +1,309 @@
+/**
+ * PrivateBin
+ *
+ * a zero-knowledge paste bin
+ *
+ * @see       {@link https://github.com/PrivateBin/PrivateBin}
+ * @copyright 2012 Sébastien SAUVAGE ({@link http://sebsauvage.net})
+ * @license   {@link https://www.opensource.org/licenses/zlib-license.php The zlib/libpng License}
+ * @version   1.3
+ * @name      Legacy
+ * @namespace
+ *
+ * IMPORTANT NOTICE FOR DEVELOPERS:
+ * The logic in this file is intended to run in legacy browsers. Avoid any use of:
+ * - jQuery (doesn't work in older browsers)
+ * - ES5 or newer in general
+ * - const/let, use the traditional var declarations instead
+ * - async/await or Promises, use traditional callbacks
+ * - shorthand function notation "() => output", use the full "function() {return output;}" style
+ * - IE doesn't support:
+ *   - URL(), use the traditional window.location object
+ *   - endsWith(), use indexof()
+ * - yes, this logic needs to support IE 6, to at least display the error message
+ */
+
+'use strict';
+(function() {
+    /**
+     * compatibility check
+     *
+     * @name   Check
+     * @class
+     */
+    var Check = (function () {
+        var me = {};
+
+        /**
+         * Status of the initial check, true means it passed
+         *
+         * @private
+         * @prop   {bool}
+         */
+        var status = false;
+
+        /**
+         * Initialization check did run
+         *
+         * @private
+         * @prop   {bool}
+         */
+        var init = false;
+
+        /**
+         * blacklist of UserAgents (parts) known to belong to a bot
+         *
+         * @private
+         * @enum   {Array}
+         * @readonly
+         */
+        var badBotUA = [
+            'Bot',
+            'bot'
+        ];
+
+        /**
+         * whitelist of top level domains to consider a secure context,
+         * regardless of protocol
+         *
+         * @private
+         * @enum   {Array}
+         * @readonly
+         */
+        var tld = [
+            '.onion',
+            '.i2p'
+        ];
+
+        /**
+         * whitelist of hostnames to consider a secure context,
+         * regardless of protocol
+         *
+         * @private
+         * @enum   {Array}
+         * @readonly
+         */
+        // whitelists of TLDs & local hostnames
+        var hostname = [
+            'localhost',
+            '127.0.0.1',
+            '[::1]'
+        ];
+
+        /**
+         * check if the context is secure
+         *
+         * @private
+         * @name   Check.isSecureContext
+         * @function
+         * @return {bool}
+         */
+        function isSecureContext()
+        {
+            // use .isSecureContext if available
+            if (window.isSecureContext === true || window.isSecureContext === false) {
+                return window.isSecureContext;
+            }
+
+            // HTTP is obviously insecure
+            if (window.location.protocol !== 'http:') {
+                return true;
+            }
+
+            // filter out actually secure connections over HTTP
+            for (var i = 0; i < tld.length; i++) {
+                if (
+                    window.location.hostname.indexOf(
+                        tld[i],
+                        window.location.hostname.length - tld[i].length
+                    ) !== -1
+                ) {
+                    return true;
+                }
+            }
+
+            // whitelist localhost for development
+            for (var j = 0; j < hostname.length; j++) {
+                if (window.location.hostname === hostname[j]) {
+                    return true;
+                }
+            }
+
+            // totally INSECURE http protocol!
+            return false;
+        }
+
+        /**
+         * checks whether this is a bot we dislike
+         *
+         * @private
+         * @name   Check.isBadBot
+         * @function
+         * @return {bool}
+         */
+        function isBadBot() {
+            // check whether a bot user agent part can be found in the current
+            // user agent
+            for (var i = 0; i < badBotUA.length; i++) {
+                if (navigator.userAgent.indexOf(badBotUA[i]) !== -1) {
+                    return true;
+                }
+            }
+            return false;
+        }
+
+        /**
+         * checks whether this is an unsupported browser, via feature detection
+         *
+         * @private
+         * @name   Check.isOldBrowser
+         * @function
+         * @return {bool}
+         */
+        function isOldBrowser() {
+            // webcrypto support
+            if (!(
+                'crypto' in window &&
+                'getRandomValues' in window.crypto &&
+                'subtle' in window.crypto &&
+                'encrypt' in window.crypto.subtle &&
+                'decrypt' in window.crypto.subtle &&
+                'Uint8Array' in window &&
+                'Uint32Array' in window
+            )) {
+                return true;
+            }
+
+            // async & ES6 support
+            try {
+                eval('async () => {}');
+            } catch (e) {
+                if (e instanceof SyntaxError) {
+                    return true;
+                } else {
+                    throw e; // throws CSP error
+                }
+            }
+
+            return false;
+        }
+
+        /**
+         * shows an error message
+         *
+         * @private
+         * @name   Check.showError
+         * @param  {string} message
+         * @function
+         */
+        function showError(message)
+        {
+            var element = document.getElementById('errormessage');
+            if (message.indexOf('<a') === -1) {
+                element.appendChild(
+                    document.createTextNode(message)
+                );
+            } else {
+                element.innerHTML = message;
+            }
+            removeHiddenFromId('errormessage');
+        }
+
+        /**
+         * removes "hidden" CSS class from element with given ID
+         *
+         * @private
+         * @name   Check.removeHiddenFromId
+         * @param  {string} id
+         * @function
+         */
+        function removeHiddenFromId(id)
+        {
+            var element = document.getElementById(id);
+            if (element) {
+                element.className = element.className.replace(/\bhidden\b/g, '');
+            }
+        }
+
+        /**
+         * returns if the check has concluded
+         *
+         * @name   Check.getInit
+         * @function
+         * @return {bool}
+         */
+        me.getInit = function()
+        {
+            return init;
+        };
+        
+        /**
+         * returns the current status of the check
+         *
+         * @name   Check.getStatus
+         * @function
+         * @return {bool}
+         */
+        me.getStatus = function()
+        {
+            return status;
+        };
+        
+        /**
+         * init on application start, returns an all-clear signal
+         *
+         * @name   Check.init
+         * @function
+         */
+        me.init = function()
+        {
+            // prevent bots from viewing a paste and potentially deleting data
+            // when burn-after-reading is set
+            if (isBadBot()) {
+                showError('I love you too, bot…');
+                init = true;
+                return;
+            }
+
+            if (isOldBrowser()) {
+                // some browsers (Chrome based ones) would have webcrypto support if using HTTPS
+                if (!isSecureContext()) {
+                    removeHiddenFromId('insecurecontextnotice');
+                }
+                removeHiddenFromId('oldnotice');
+                init = true;
+                return;
+            }
+
+            if (!isSecureContext()) {
+                removeHiddenFromId('httpnotice');
+            }
+            init = true;
+
+            // only if everything passed, we set the status to true
+            status = true;
+        };
+
+        return me;
+    })();
+
+    // main application start, called when DOM is fully loaded
+    if (document.readyState === 'complete' || (!document.attachEvent && document.readyState === 'interactive')) {
+        Check.init();
+    } else {
+        if (document.addEventListener) {
+            // first choice is DOMContentLoaded event
+            document.addEventListener('DOMContentLoaded', Check.init, false);
+            // backup is window load event
+            window.addEventListener('load', Check.init, false);
+        } else {
+            // must be IE
+            document.attachEvent('onreadystatechange', Check.init);
+            window.attachEvent('onload', Check.init);
+        }
+    }
+
+    this.Legacy = {
+        Check: Check
+    };
+}).call(this);

+ 150 - 274
js/privatebin.js

@@ -9,20 +9,12 @@
  * @version   1.3
  * @version   1.3
  * @name      PrivateBin
  * @name      PrivateBin
  * @namespace
  * @namespace
+ *
+ * global Base64, DOMPurify, FileReader, RawDeflate, history, navigator, prettyPrint, prettyPrintOne, showdown, kjua
  */
  */
 
 
-/** global: Base64 */
-/** global: DOMPurify */
-/** global: FileReader */
-/** global: RawDeflate */
-/** global: history */
-/** global: navigator */
-/** global: prettyPrint */
-/** global: prettyPrintOne */
-/** global: showdown */
-/** global: kjua */
-
 jQuery.fn.draghover = function() {
 jQuery.fn.draghover = function() {
+    'use strict';
     return this.each(function() {
     return this.each(function() {
         let collection = $(),
         let collection = $(),
             self = $(this);
             self = $(this);
@@ -566,12 +558,11 @@ jQuery.PrivateBin = (function($, RawDeflate) {
 
 
             // if $element is given, apply text to element
             // if $element is given, apply text to element
             if ($element !== null) {
             if ($element !== null) {
-                // get last text node of element
-                let content = $element.contents();
-                if (content.length > 1) {
-                    content[content.length - 1].nodeValue = ' ' + output;
-                } else {
+                // avoid HTML entity encoding if translation contains link
+                if (output.indexOf('<a') === -1) {
                     $element.text(output);
                     $element.text(output);
+                } else {
+                    $element.html(output);
                 }
                 }
             }
             }
 
 
@@ -781,15 +772,20 @@ jQuery.PrivateBin = (function($, RawDeflate) {
          * @private
          * @private
          * @param  {string} message
          * @param  {string} message
          * @param  {string} mode
          * @param  {string} mode
+         * @param  {object} zlib
+         * @throws {string}
          * @return {ArrayBuffer} data
          * @return {ArrayBuffer} data
          */
          */
-        async function compress(message, mode)
+        async function compress(message, mode, zlib)
         {
         {
             message = stringToArraybuffer(
             message = stringToArraybuffer(
                 utf16To8(message)
                 utf16To8(message)
             );
             );
             if (mode === 'zlib') {
             if (mode === 'zlib') {
-                return z.deflate(message).buffer;
+                if (typeof zlib === 'undefined') {
+                    throw 'Error compressing paste, due to missing WebAssembly support.'
+                }
+                return zlib.deflate(message).buffer;
             }
             }
             return message;
             return message;
         }
         }
@@ -803,13 +799,18 @@ jQuery.PrivateBin = (function($, RawDeflate) {
          * @private
          * @private
          * @param  {ArrayBuffer} data
          * @param  {ArrayBuffer} data
          * @param  {string} mode
          * @param  {string} mode
+         * @param  {object} zlib
+         * @throws {string}
          * @return {string} message
          * @return {string} message
          */
          */
-        async function decompress(data, mode)
+        async function decompress(data, mode, zlib)
         {
         {
             if (mode === 'zlib' || mode === 'none') {
             if (mode === 'zlib' || mode === 'none') {
                 if (mode === 'zlib') {
                 if (mode === 'zlib') {
-                    data = z.inflate(
+                    if (typeof zlib === 'undefined') {
+                        throw 'Error decompressing paste, due to missing WebAssembly support.'
+                    }
+                    data = zlib.inflate(
                         new Uint8Array(data)
                         new Uint8Array(data)
                     ).buffer;
                     ).buffer;
                 }
                 }
@@ -883,7 +884,7 @@ jQuery.PrivateBin = (function($, RawDeflate) {
                         stringToArraybuffer(
                         stringToArraybuffer(
                             utf16To8(password)
                             utf16To8(password)
                         )
                         )
-                    );
+                    ).catch(Alert.showError);
                     password = Array.prototype.map.call(
                     password = Array.prototype.map.call(
                         new Uint8Array(passwordBuffer),
                         new Uint8Array(passwordBuffer),
                         x => ('00' + x.toString(16)).slice(-2)
                         x => ('00' + x.toString(16)).slice(-2)
@@ -903,7 +904,7 @@ jQuery.PrivateBin = (function($, RawDeflate) {
                 {name: 'PBKDF2'}, // we use PBKDF2 for key derivation
                 {name: 'PBKDF2'}, // we use PBKDF2 for key derivation
                 false, // the key may not be exported
                 false, // the key may not be exported
                 ['deriveKey'] // we may only use it for key derivation
                 ['deriveKey'] // we may only use it for key derivation
-            );
+            ).catch(Alert.showError);
 
 
             // derive a stronger key for use with AES
             // derive a stronger key for use with AES
             return window.crypto.subtle.deriveKey(
             return window.crypto.subtle.deriveKey(
@@ -920,7 +921,7 @@ jQuery.PrivateBin = (function($, RawDeflate) {
                 },
                 },
                 false, // the key may not be exported
                 false, // the key may not be exported
                 ['encrypt', 'decrypt'] // we may only use it for en- and decryption
                 ['encrypt', 'decrypt'] // we may only use it for en- and decryption
-            );
+            ).catch(Alert.showError);
         }
         }
 
 
         /**
         /**
@@ -957,18 +958,24 @@ jQuery.PrivateBin = (function($, RawDeflate) {
          */
          */
         me.cipher = async function(key, password, message, adata)
         me.cipher = async function(key, password, message, adata)
         {
         {
+            let zlib = (await z);
             // AES in Galois Counter Mode, keysize 256 bit,
             // AES in Galois Counter Mode, keysize 256 bit,
             // authentication tag 128 bit, 10000 iterations in key derivation
             // authentication tag 128 bit, 10000 iterations in key derivation
-            const spec = [
-                getRandomBytes(16),                     // initialization vector
-                getRandomBytes(8),                      // salt
-                100000,                                 // iterations
-                256,                                    // key size
-                128,                                    // tag size
-                'aes',                                  // algorithm
-                'gcm',                                  // algorithm mode
-                $('body').data('compression') || 'zlib' // compression
-            ], encodedSpec = [];
+            const compression = (
+                    typeof zlib === 'undefined' ?
+                    'none' : // client lacks support for WASM
+                    ($('body').data('compression') || 'zlib')
+                ),
+                spec = [
+                    getRandomBytes(16), // initialization vector
+                    getRandomBytes(8),  // salt
+                    100000,             // iterations
+                    256,                // key size
+                    128,                // tag size
+                    'aes',              // algorithm
+                    'gcm',              // algorithm mode
+                    compression         // compression
+                ], encodedSpec = [];
             for (let i = 0; i < spec.length; ++i) {
             for (let i = 0; i < spec.length; ++i) {
                 encodedSpec[i] = i < 2 ? btoa(spec[i]) : spec[i];
                 encodedSpec[i] = i < 2 ? btoa(spec[i]) : spec[i];
             }
             }
@@ -987,8 +994,8 @@ jQuery.PrivateBin = (function($, RawDeflate) {
                         await window.crypto.subtle.encrypt(
                         await window.crypto.subtle.encrypt(
                             cryptoSettings(JSON.stringify(adata), spec),
                             cryptoSettings(JSON.stringify(adata), spec),
                             await deriveKey(key, password, spec),
                             await deriveKey(key, password, spec),
-                            await compress(message, spec[7])
-                        )
+                            await compress(message, compression, zlib)
+                        ).catch(Alert.showError)
                     )
                     )
                 ),
                 ),
                 adata
                 adata
@@ -1008,7 +1015,8 @@ jQuery.PrivateBin = (function($, RawDeflate) {
          */
          */
         me.decipher = async function(key, password, data)
         me.decipher = async function(key, password, data)
         {
         {
-            let adataString, spec, cipherMessage;
+            let adataString, spec, cipherMessage, plaintext;
+            let zlib = (await z);
             if (data instanceof Array) {
             if (data instanceof Array) {
                 // version 2
                 // version 2
                 adataString = JSON.stringify(data[1]);
                 adataString = JSON.stringify(data[1]);
@@ -1035,20 +1043,29 @@ jQuery.PrivateBin = (function($, RawDeflate) {
             }
             }
             spec[0] = atob(spec[0]);
             spec[0] = atob(spec[0]);
             spec[1] = atob(spec[1]);
             spec[1] = atob(spec[1]);
+            if (spec[7] === 'zlib') {
+                if (typeof zlib === 'undefined') {
+                    throw 'Error decompressing paste, due to missing WebAssembly support.'
+                }
+            }
             try {
             try {
-                return await decompress(
-                    await window.crypto.subtle.decrypt(
-                        cryptoSettings(adataString, spec),
-                        await deriveKey(key, password, spec),
-                        stringToArraybuffer(
-                            atob(cipherMessage)
-                        )
-                    ),
-                    spec[7]
+                plaintext = await window.crypto.subtle.decrypt(
+                    cryptoSettings(adataString, spec),
+                    await deriveKey(key, password, spec),
+                    stringToArraybuffer(
+                        atob(cipherMessage)
+                    )
                 );
                 );
             } catch(err) {
             } catch(err) {
+                console.error(err);
                 return '';
                 return '';
             }
             }
+            try {
+                return await decompress(plaintext, spec[7], zlib);
+            } catch(err) {
+                Alert.showError(err);
+                return err;
+            }
         };
         };
 
 
         /**
         /**
@@ -1194,7 +1211,6 @@ jQuery.PrivateBin = (function($, RawDeflate) {
         me.getPasteId = function()
         me.getPasteId = function()
         {
         {
             const idRegEx = /^[a-z0-9]{16}$/;
             const idRegEx = /^[a-z0-9]{16}$/;
-            const idRegExFind = /[a-z0-9]{16}/;
 
 
             // return cached value
             // return cached value
             if (id !== null) {
             if (id !== null) {
@@ -1483,7 +1499,7 @@ jQuery.PrivateBin = (function($, RawDeflate) {
         const alertType = [
         const alertType = [
             'loading', // not in bootstrap CSS, but using a plausible value here
             'loading', // not in bootstrap CSS, but using a plausible value here
             'info',    // status icon
             'info',    // status icon
-            'warning', // not used yet
+            'warning', // warning icon
             'danger'   // error icon
             'danger'   // error icon
         ];
         ];
 
 
@@ -1527,28 +1543,35 @@ jQuery.PrivateBin = (function($, RawDeflate) {
                     icon = null; // icons not supported in this case
                     icon = null; // icons not supported in this case
                 }
                 }
             }
             }
+            let $translationTarget = $element;
 
 
-            // handle icon
-            if (icon !== null && // icon was passed
-                icon !== currentIcon[id] // and it differs from current icon
-            ) {
-                let $glyphIcon = $element.find(':first');
-
-                // remove (previous) icon
-                $glyphIcon.removeClass(currentIcon[id]);
+            // handle icon, if template uses one
+            const $glyphIcon = $element.find(':first');
+            if ($glyphIcon.length) {
+                // if there is an icon, we need to provide an inner element
+                // to translate the message into, instead of the parent
+                $translationTarget = $('<span>');
+                $element.html(' ').prepend($glyphIcon).append($translationTarget);
 
 
-                // any other thing as a string (e.g. 'null') (only) removes the icon
-                if (typeof icon === 'string') {
-                    // set new icon
-                    currentIcon[id] = 'glyphicon-' + icon;
-                    $glyphIcon.addClass(currentIcon[id]);
+                if (icon !== null && // icon was passed
+                    icon !== currentIcon[id] // and it differs from current icon
+                ) {
+                    // remove (previous) icon
+                    $glyphIcon.removeClass(currentIcon[id]);
+
+                    // any other thing as a string (e.g. 'null') (only) removes the icon
+                    if (typeof icon === 'string') {
+                        // set new icon
+                        currentIcon[id] = 'glyphicon-' + icon;
+                        $glyphIcon.addClass(currentIcon[id]);
+                    }
                 }
                 }
             }
             }
 
 
             // show text
             // show text
             if (args !== null) {
             if (args !== null) {
                 // add jQuery object to it as first parameter
                 // add jQuery object to it as first parameter
-                args.unshift($element);
+                args.unshift($translationTarget);
                 // pass it to I18n
                 // pass it to I18n
                 I18n._.apply(this, args);
                 I18n._.apply(this, args);
             }
             }
@@ -1573,6 +1596,25 @@ jQuery.PrivateBin = (function($, RawDeflate) {
             handleNotification(1, $statusMessage, message, icon);
             handleNotification(1, $statusMessage, message, icon);
         };
         };
 
 
+        /**
+         * display a warning message
+         *
+         * This automatically passes the text to I18n for translation.
+         *
+         * @name   Alert.showWarning
+         * @function
+         * @param  {string|array} message     string, use an array for %s/%d options
+         * @param  {string|null}  icon        optional, the icon to show, default:
+         *                                    leave previous icon
+         */
+        me.showWarning = function(message, icon)
+        {
+            $errorMessage.find(':first')
+                         .removeClass(currentIcon[3])
+                         .addClass(currentIcon[2]);
+            handleNotification(2, $errorMessage, message, icon);
+        };
+
         /**
         /**
          * display an error message
          * display an error message
          *
          *
@@ -1699,7 +1741,7 @@ jQuery.PrivateBin = (function($, RawDeflate) {
             currentIcon = [
             currentIcon = [
                 'glyphicon-time', // loading icon
                 'glyphicon-time', // loading icon
                 'glyphicon-info-sign', // status icon
                 'glyphicon-info-sign', // status icon
-                '', // reserved for warning, not used yet
+                'glyphicon-warning-sign', // warning icon
                 'glyphicon-alert' // error icon
                 'glyphicon-alert' // error icon
             ];
             ];
         };
         };
@@ -1768,14 +1810,14 @@ jQuery.PrivateBin = (function($, RawDeflate) {
                             return;
                             return;
                         }
                         }
                     }
                     }
-                    Alert.showError(
-                        I18n._('Cannot parse response from URL shortener.')
-                    );
+                    Alert.showError('Cannot parse response from URL shortener.');
                 }
                 }
             })
             })
             .fail(function(data, textStatus, errorThrown) {
             .fail(function(data, textStatus, errorThrown) {
                 console.error(textStatus, errorThrown);
                 console.error(textStatus, errorThrown);
-                // we don't know why it failed, could be CORS of the external server not setup properly, in which case we follow old behavior to open it in new tab
+                // we don't know why it failed, could be CORS of the external
+                // server not setup properly, in which case we follow old
+                // behavior to open it in new tab
                 window.open(
                 window.open(
                     `${$shortenButton.data('shortener')}${encodeURIComponent($pasteUrl.attr('href'))}`,
                     `${$shortenButton.data('shortener')}${encodeURIComponent($pasteUrl.attr('href'))}`,
                     '_blank',
                     '_blank',
@@ -2731,9 +2773,7 @@ jQuery.PrivateBin = (function($, RawDeflate) {
                 // revert loading status…
                 // revert loading status…
                 me.hideAttachment();
                 me.hideAttachment();
                 me.hideAttachmentPreview();
                 me.hideAttachmentPreview();
-                Alert.showError(
-                    I18n._('Your browser does not support uploading encrypted files. Please use a newer browser.')
-                );
+                Alert.showWarning('Your browser does not support uploading encrypted files. Please use a newer browser.');
                 return;
                 return;
             }
             }
 
 
@@ -2965,11 +3005,11 @@ jQuery.PrivateBin = (function($, RawDeflate) {
         me.init = function()
         me.init = function()
         {
         {
             $attachment = $('#attachment');
             $attachment = $('#attachment');
-            if($attachment.length){
+            $dragAndDropFileName = $('#dragAndDropFileName');
+            $dropzone = $('#dropzone');
+            if($attachment.length) {
                 $attachmentLink = $('#attachment a');
                 $attachmentLink = $('#attachment a');
                 $attachmentPreview = $('#attachmentPreview');
                 $attachmentPreview = $('#attachmentPreview');
-                $dragAndDropFileName = $('#dragAndDropFileName');
-                $dropzone = $('#dropzone');
 
 
                 $fileInput = $('#file');
                 $fileInput = $('#file');
                 addDragDropHandler();
                 addDragDropHandler();
@@ -4194,8 +4234,6 @@ jQuery.PrivateBin = (function($, RawDeflate) {
     const PasteEncrypter = (function () {
     const PasteEncrypter = (function () {
         const me = {};
         const me = {};
 
 
-        let requirementsChecked = false;
-
         /**
         /**
          * called after successful paste upload
          * called after successful paste upload
          *
          *
@@ -4428,16 +4466,13 @@ jQuery.PrivateBin = (function($, RawDeflate) {
                             });
                             });
                             cipherMessage['attachment'] = await fileReading;
                             cipherMessage['attachment'] = await fileReading;
                         } else {
                         } else {
-                            Alert.showError(
-                                I18n._('Cannot process attachment data.')
-                            );
-                            throw new TypeError('Cannot process attachment data.');
+                            const error = 'Cannot process attachment data.';
+                            Alert.showError(error);
+                            throw new TypeError(error);
                         }
                         }
                     } catch (error) {
                     } catch (error) {
                         console.error(error);
                         console.error(error);
-                        Alert.showError(
-                            I18n._('Cannot retrieve attachment.')
-                        );
+                        Alert.showError('Cannot retrieve attachment.');
                         throw error;
                         throw error;
                     }
                     }
                 }
                 }
@@ -4494,7 +4529,7 @@ jQuery.PrivateBin = (function($, RawDeflate) {
 
 
             // if all tries failed, we can only return an error
             // if all tries failed, we can only return an error
             if (plaindata.length === 0) {
             if (plaindata.length === 0) {
-                throw 'failed to decipher data';
+                return false;
             }
             }
 
 
             return plaindata;
             return plaindata;
@@ -4523,13 +4558,14 @@ jQuery.PrivateBin = (function($, RawDeflate) {
                 if (password.length === 0) {
                 if (password.length === 0) {
                     throw 'waiting on user to provide a password';
                     throw 'waiting on user to provide a password';
                 } else {
                 } else {
-                    displayDecryptionError('failed to decipher paste text: Incorrect password?');
-                    throw 'waiting on user to provide correct password';
+                    Alert.hideLoading();
+                    // reset password, so it can be re-entered
+                    Prompt.reset();
+                    TopNav.showRetryButton();
+                    throw 'Could not decrypt data. Did you enter a wrong password? Retry with the button at the top.';
                 }
                 }
             }
             }
 
 
-            let format = '',
-                text   = '';
             if (paste.v > 1) {
             if (paste.v > 1) {
                 // version 2 paste
                 // version 2 paste
                 const pasteMessage = JSON.parse(pastePlain);
                 const pasteMessage = JSON.parse(pastePlain);
@@ -4616,29 +4652,6 @@ jQuery.PrivateBin = (function($, RawDeflate) {
             });
             });
         }
         }
 
 
-        /**
-         * displays and logs decryption errors
-         *
-         * @name   PasteDecrypter.displayDecryptionError
-         * @private
-         * @function
-         * @param  {string} message
-         */
-        function displayDecryptionError(message)
-        {
-            Alert.hideLoading();
-
-            // log detailed error, but display generic translation
-            console.error(message);
-            Alert.showError(
-                I18n._('Could not decrypt data. Did you enter a wrong password? Retry with the button at the top.')
-            );
-
-            // reset password, so it can be re-entered
-            Prompt.reset();
-            TopNav.showRetryButton();
-        }
-
         /**
         /**
          * show decrypted text in the display area, including discussion (if open)
          * show decrypted text in the display area, including discussion (if open)
          *
          *
@@ -4690,170 +4703,13 @@ jQuery.PrivateBin = (function($, RawDeflate) {
                 .catch((err) => {
                 .catch((err) => {
                     // wait for the user to type in the password,
                     // wait for the user to type in the password,
                     // then PasteDecrypter.run will be called again
                     // then PasteDecrypter.run will be called again
-                    console.error(err);
+                    Alert.showError(err);
                 });
                 });
         };
         };
 
 
         return me;
         return me;
     })();
     })();
 
 
-
-    /**
-     * initial (security) check
-     *
-     * @name   InitialCheck
-     * @param  {object} window
-     * @param  {object} document
-     * @class
-     */
-    const InitialCheck = (function () {
-        const me = {};
-
-        /**
-         * blacklist of UserAgents (parts) known to belong to a bot
-         *
-         * @private
-         * @enum   {Array}
-         * @readonly
-         */
-        const badBotUA = [
-            'Bot',
-            'bot'
-        ];
-
-        /**
-         * check if the connection is insecure
-         *
-         * @private
-         * @name   InitialCheck.isInsecureConnection
-         * @function
-         * @return {bool}
-         */
-        function isInsecureConnection()
-        {
-            // use .isSecureContext if available
-            if (window.isSecureContext === true || window.isSecureContext === false) {
-                return !window.isSecureContext;
-            }
-
-            const url = new URL(window.location);
-
-            // HTTP is obviously insecure
-            if (url.protocol !== 'http:') {
-                return false;
-            }
-
-            // filter out actually secure connections over HTTP
-            for (const tld of ['.onion', '.i2p']) {
-                if (url.hostname.endsWith(tld)) {
-                    return false;
-                }
-            }
-
-            // whitelist localhost for development
-            for (const hostname of ['localhost', '127.0.0.1', '[::1]']) {
-                if (url.hostname === hostname) {
-                    return false;
-                }
-            }
-
-            // totally INSECURE http protocol!
-            return true;
-        }
-
-        /**
-         * checks whether this is a bot we dislike
-         *
-         * @private
-         * @name   InitialCheck.isBadBot
-         * @function
-         * @return {bool}
-         */
-        function isBadBot() {
-            // check whether a bot user agent part can be found in the current
-            // user agent
-            for (const UAfragment of badBotUA) {
-                if (navigator.userAgent.indexOf(UAfragment) >= 0) {
-                    return true;
-                }
-            }
-            return false;
-        }
-
-        /**
-         * checks whether this is an unsupported browser, via feature detection
-         *
-         * @private
-         * @name   InitialCheck.isOldBrowser
-         * @function
-         * @return {bool}
-         */
-        function isOldBrowser() {
-            // webcrypto support
-            if (typeof window.crypto !== 'object') {
-                return true;
-            }
-
-            if (typeof WebAssembly !== 'object' && typeof WebAssembly.instantiate !== 'function') {
-                return true;
-            }
-            try {
-                // [\0, 'a', 's', 'm', (uint_32) 1] - smallest valid wasm module
-                const module = new WebAssembly.Module(Uint8Array.of(0x0, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00));
-                if (
-                    !(
-                        module instanceof WebAssembly.Module &&
-                        new WebAssembly.Instance(module) instanceof WebAssembly.Instance
-                    )
-                ) {
-                    return true;
-                }
-            } catch (e) {
-                return true;
-            }
-
-             // not checking for async/await, ES6, Promise or Uint8Array support,
-             // as most browsers introduced these earlier then webassembly and webcrypto:
-             // https://github.com/PrivateBin/PrivateBin/pull/431#issuecomment-493129359
-
-            return false;
-        }
-
-        /**
-         * init on application start, returns an all-clear signal
-         *
-         * @name   InitialCheck.init
-         * @function
-         * @return {bool}
-         */
-        me.init = function()
-        {
-            // prevent bots from viewing a paste and potentially deleting data
-            // when burn-after-reading is set
-            if (isBadBot()) {
-                Alert.showError('I love you too, bot…');
-                return false;
-            }
-
-            if (isOldBrowser()) {
-                // some browsers (Chrome based ones) would have webcrypto support if using HTTPS
-                if (isInsecureConnection()) {
-                    Alert.showError(['Your browser may require an HTTPS connection to support the WebCrypto API. Try <a href="%s">switching to HTTPS</a>.', 'https' + window.location.href.slice(4)]);
-                }
-                $('#oldnotice').removeClass('hidden');
-                return false;
-            }
-
-            if (isInsecureConnection()) {
-                $('#httpnotice').removeClass('hidden');
-            }
-
-            return true;
-        }
-
-        return me;
-    })();
-
     /**
     /**
      * (controller) main PrivateBin logic
      * (controller) main PrivateBin logic
      *
      *
@@ -4926,9 +4782,7 @@ jQuery.PrivateBin = (function($, RawDeflate) {
 
 
                 // missing decryption key (or paste ID) in URL?
                 // missing decryption key (or paste ID) in URL?
                 if (window.location.hash.length === 0) {
                 if (window.location.hash.length === 0) {
-                    Alert.showError(
-                        I18n._('Cannot decrypt paste: Decryption key missing in URL (Did you use a redirector or an URL shortener which strips part of the URL?)')
-                    );
+                    Alert.showError('Cannot decrypt paste: Decryption key missing in URL (Did you use a redirector or an URL shortener which strips part of the URL?)');
                     return;
                     return;
                 }
                 }
             }
             }
@@ -5030,13 +4884,29 @@ jQuery.PrivateBin = (function($, RawDeflate) {
             DiscussionViewer.prepareNewDiscussion();
             DiscussionViewer.prepareNewDiscussion();
         };
         };
 
 
+        /**
+         * try initializing zlib or display a warning if it fails,
+         * extracted from main init to allow unit testing
+         *
+         * @name   Controller.initZ
+         * @function
+         */
+        me.initZ = function()
+        {
+            z = zlib.catch(function () {
+                if ($('body').data('compression') !== 'none') {
+                    Alert.showWarning('Your browser doesn\'t support WebAssembly, used for zlib compression. You can create uncompressed documents, but can\'t read compressed ones.');
+                }
+            });
+        }
+
         /**
         /**
          * application start
          * application start
          *
          *
          * @name   Controller.init
          * @name   Controller.init
          * @function
          * @function
          */
          */
-        me.init = async function()
+        me.init = function()
         {
         {
             // first load translations
             // first load translations
             I18n.loadTranslations();
             I18n.loadTranslations();
@@ -5054,11 +4924,18 @@ jQuery.PrivateBin = (function($, RawDeflate) {
             Prompt.init();
             Prompt.init();
             TopNav.init();
             TopNav.init();
             UiHelper.init();
             UiHelper.init();
-            z = (await zlib);
-            if (!InitialCheck.init()) {
+
+            // check for legacy browsers before going any further
+            if (!Legacy.Check.getInit()) {
+                // Legacy check didn't complete, wait and try again
+                setTimeout(init, 500);
+                return;
+            }
+            if (!Legacy.Check.getStatus()) {
                 // something major is wrong, stop right away
                 // something major is wrong, stop right away
                 return;
                 return;
             }
             }
+            me.initZ();
 
 
             // check whether existing paste needs to be shown
             // check whether existing paste needs to be shown
             try {
             try {
@@ -5098,7 +4975,6 @@ jQuery.PrivateBin = (function($, RawDeflate) {
         ServerInteraction: ServerInteraction,
         ServerInteraction: ServerInteraction,
         PasteEncrypter: PasteEncrypter,
         PasteEncrypter: PasteEncrypter,
         PasteDecrypter: PasteDecrypter,
         PasteDecrypter: PasteDecrypter,
-        InitialCheck: InitialCheck,
         Controller: Controller
         Controller: Controller
     };
     };
 })(jQuery, RawDeflate);
 })(jQuery, RawDeflate);

+ 207 - 18
js/test/Alert.js

@@ -4,16 +4,55 @@ var common = require('../common');
 describe('Alert', function () {
 describe('Alert', function () {
     describe('showStatus', function () {
     describe('showStatus', function () {
         jsc.property(
         jsc.property(
-            'shows a status message',
+            'shows a status message (basic)',
             jsc.array(common.jscAlnumString()),
             jsc.array(common.jscAlnumString()),
             jsc.array(common.jscAlnumString()),
             jsc.array(common.jscAlnumString()),
             function (icon, message) {
             function (icon, message) {
                 icon = icon.join('');
                 icon = icon.join('');
                 message = message.join('');
                 message = message.join('');
-                var expected = '<div id="status" role="alert" ' +
+                const expected = '<div id="status">' + message + '</div>';
+                $('body').html(
+                    '<div id="status"></div>'
+                );
+                $.PrivateBin.Alert.init();
+                $.PrivateBin.Alert.showStatus(message, icon);
+                const result = $('body').html();
+                return expected === result;
+            }
+        );
+
+        jsc.property(
+            'shows a status message (bootstrap)',
+            jsc.array(common.jscAlnumString()),
+            function (message) {
+                message = message.join('');
+                const expected = '<div id="status" role="alert" ' +
+                    'class="statusmessage alert alert-info"><span ' +
+                    'class="glyphicon glyphicon-info-sign" ' +
+                    'aria-hidden="true"></span> <span>' + message + '</span></div>';
+                $('body').html(
+                    '<div id="status" role="alert" class="statusmessage ' +
+                    'alert alert-info hidden"><span class="glyphicon ' +
+                    'glyphicon-info-sign" aria-hidden="true"></span> </div>'
+                );
+                $.PrivateBin.Alert.init();
+                $.PrivateBin.Alert.showStatus(message);
+                const result = $('body').html();
+                return expected === result;
+            }
+        );
+
+        jsc.property(
+            'shows a status message (bootstrap, custom icon)',
+            jsc.array(common.jscAlnumString()),
+            jsc.array(common.jscAlnumString()),
+            function (icon, message) {
+                icon = icon.join('');
+                message = message.join('');
+                const expected = '<div id="status" role="alert" ' +
                     'class="statusmessage alert alert-info"><span ' +
                     'class="statusmessage alert alert-info"><span ' +
                     'class="glyphicon glyphicon-' + icon +
                     'class="glyphicon glyphicon-' + icon +
-                    '" aria-hidden="true"></span> ' + message + '</div>';
+                    '" aria-hidden="true"></span> <span>' + message + '</span></div>';
                 $('body').html(
                 $('body').html(
                     '<div id="status" role="alert" class="statusmessage ' +
                     '<div id="status" role="alert" class="statusmessage ' +
                     'alert alert-info hidden"><span class="glyphicon ' +
                     'alert alert-info hidden"><span class="glyphicon ' +
@@ -21,7 +60,76 @@ describe('Alert', function () {
                 );
                 );
                 $.PrivateBin.Alert.init();
                 $.PrivateBin.Alert.init();
                 $.PrivateBin.Alert.showStatus(message, icon);
                 $.PrivateBin.Alert.showStatus(message, icon);
-                var result = $('body').html();
+                const result = $('body').html();
+                return expected === result;
+            }
+        );
+    });
+
+    describe('showWarning', function () {
+        before(function () {
+            cleanup();
+        });
+
+        jsc.property(
+            'shows a warning message (basic)',
+            jsc.array(common.jscAlnumString()),
+            jsc.array(common.jscAlnumString()),
+            function (icon, message) {
+                icon = icon.join('');
+                message = message.join('');
+                const expected = '<div id="errormessage">' + message + '</div>';
+                $('body').html(
+                    '<div id="errormessage"></div>'
+                );
+                $.PrivateBin.Alert.init();
+                $.PrivateBin.Alert.showWarning(message, icon);
+                const result = $('body').html();
+                return expected === result;
+            }
+        );
+
+        jsc.property(
+            'shows a warning message (bootstrap)',
+            jsc.array(common.jscAlnumString()),
+            jsc.array(common.jscAlnumString()),
+            function (message) {
+                message = message.join('');
+                const expected = '<div id="errormessage" role="alert" ' +
+                    'class="statusmessage alert alert-danger"><span ' +
+                    'class="glyphicon glyphicon-warning-sign" ' +
+                    'aria-hidden="true"></span> <span>' + message + '</span></div>';
+                $('body').html(
+                    '<div id="errormessage" role="alert" class="statusmessage ' +
+                    'alert alert-danger hidden"><span class="glyphicon ' +
+                    'glyphicon-alert" aria-hidden="true"></span> </div>'
+                );
+                $.PrivateBin.Alert.init();
+                $.PrivateBin.Alert.showWarning(message);
+                const result = $('body').html();
+                return expected === result;
+            }
+        );
+
+        jsc.property(
+            'shows a warning message (bootstrap, custom icon)',
+            jsc.array(common.jscAlnumString()),
+            jsc.array(common.jscAlnumString()),
+            function (icon, message) {
+                icon = icon.join('');
+                message = message.join('');
+                const expected = '<div id="errormessage" role="alert" ' +
+                    'class="statusmessage alert alert-danger"><span ' +
+                    'class="glyphicon glyphicon-' + icon +
+                    '" aria-hidden="true"></span> <span>' + message + '</span></div>';
+                $('body').html(
+                    '<div id="errormessage" role="alert" class="statusmessage ' +
+                    'alert alert-danger hidden"><span class="glyphicon ' +
+                    'glyphicon-alert" aria-hidden="true"></span> </div>'
+                );
+                $.PrivateBin.Alert.init();
+                $.PrivateBin.Alert.showWarning(message, icon);
+                const result = $('body').html();
                 return expected === result;
                 return expected === result;
             }
             }
         );
         );
@@ -33,16 +141,56 @@ describe('Alert', function () {
         });
         });
 
 
         jsc.property(
         jsc.property(
-            'shows an error message',
+            'shows an error message (basic)',
+            jsc.array(common.jscAlnumString()),
+            jsc.array(common.jscAlnumString()),
+            function (icon, message) {
+                icon = icon.join('');
+                message = message.join('');
+                const expected = '<div id="errormessage">' + message + '</div>';
+                $('body').html(
+                    '<div id="errormessage"></div>'
+                );
+                $.PrivateBin.Alert.init();
+                $.PrivateBin.Alert.showError(message, icon);
+                const result = $('body').html();
+                return expected === result;
+            }
+        );
+
+        jsc.property(
+            'shows an error message (bootstrap)',
+            jsc.array(common.jscAlnumString()),
+            jsc.array(common.jscAlnumString()),
+            function (icon, message) {
+                message = message.join('');
+                const expected = '<div id="errormessage" role="alert" ' +
+                    'class="statusmessage alert alert-danger"><span ' +
+                    'class="glyphicon glyphicon-alert" ' +
+                    'aria-hidden="true"></span> <span>' + message + '</span></div>';
+                $('body').html(
+                    '<div id="errormessage" role="alert" class="statusmessage ' +
+                    'alert alert-danger hidden"><span class="glyphicon ' +
+                    'glyphicon-alert" aria-hidden="true"></span> </div>'
+                );
+                $.PrivateBin.Alert.init();
+                $.PrivateBin.Alert.showError(message);
+                const result = $('body').html();
+                return expected === result;
+            }
+        );
+
+        jsc.property(
+            'shows an error message (bootstrap, custom icon)',
             jsc.array(common.jscAlnumString()),
             jsc.array(common.jscAlnumString()),
             jsc.array(common.jscAlnumString()),
             jsc.array(common.jscAlnumString()),
             function (icon, message) {
             function (icon, message) {
                 icon = icon.join('');
                 icon = icon.join('');
                 message = message.join('');
                 message = message.join('');
-                var expected = '<div id="errormessage" role="alert" ' +
+                const expected = '<div id="errormessage" role="alert" ' +
                     'class="statusmessage alert alert-danger"><span ' +
                     'class="statusmessage alert alert-danger"><span ' +
                     'class="glyphicon glyphicon-' + icon +
                     'class="glyphicon glyphicon-' + icon +
-                    '" aria-hidden="true"></span> ' + message + '</div>';
+                    '" aria-hidden="true"></span> <span>' + message + '</span></div>';
                 $('body').html(
                 $('body').html(
                     '<div id="errormessage" role="alert" class="statusmessage ' +
                     '<div id="errormessage" role="alert" class="statusmessage ' +
                     'alert alert-danger hidden"><span class="glyphicon ' +
                     'alert alert-danger hidden"><span class="glyphicon ' +
@@ -50,7 +198,7 @@ describe('Alert', function () {
                 );
                 );
                 $.PrivateBin.Alert.init();
                 $.PrivateBin.Alert.init();
                 $.PrivateBin.Alert.showError(message, icon);
                 $.PrivateBin.Alert.showError(message, icon);
-                var result = $('body').html();
+                const result = $('body').html();
                 return expected === result;
                 return expected === result;
             }
             }
         );
         );
@@ -62,17 +210,36 @@ describe('Alert', function () {
         });
         });
 
 
         jsc.property(
         jsc.property(
-            'shows remaining time',
+            'shows remaining time (basic)',
+            jsc.array(common.jscAlnumString()),
+            jsc.array(common.jscAlnumString()),
+            'integer',
+            function (message, string, number) {
+                message = message.join('');
+                string = string.join('');
+                const expected = '<div id="remainingtime" class="">' + string + message + number + '</div>';
+                $('body').html(
+                    '<div id="remainingtime" class="hidden"></div>'
+                );
+                $.PrivateBin.Alert.init();
+                $.PrivateBin.Alert.showRemaining(['%s' + message + '%d', string, number]);
+                const result = $('body').html();
+                return expected === result;
+            }
+        );
+
+        jsc.property(
+            'shows remaining time (bootstrap)',
             jsc.array(common.jscAlnumString()),
             jsc.array(common.jscAlnumString()),
             jsc.array(common.jscAlnumString()),
             jsc.array(common.jscAlnumString()),
             'integer',
             'integer',
             function (message, string, number) {
             function (message, string, number) {
                 message = message.join('');
                 message = message.join('');
                 string = string.join('');
                 string = string.join('');
-                var expected = '<div id="remainingtime" role="alert" ' +
+                const expected = '<div id="remainingtime" role="alert" ' +
                     'class="alert alert-info"><span ' +
                     'class="alert alert-info"><span ' +
                     'class="glyphicon glyphicon-fire" aria-hidden="true">' +
                     'class="glyphicon glyphicon-fire" aria-hidden="true">' +
-                    '</span> ' + string + message + number + '</div>';
+                    '</span> <span>' + string + message + number + '</span></div>';
                 $('body').html(
                 $('body').html(
                     '<div id="remainingtime" role="alert" class="hidden ' +
                     '<div id="remainingtime" role="alert" class="hidden ' +
                     'alert alert-info"><span class="glyphicon ' +
                     'alert alert-info"><span class="glyphicon ' +
@@ -80,7 +247,7 @@ describe('Alert', function () {
                 );
                 );
                 $.PrivateBin.Alert.init();
                 $.PrivateBin.Alert.init();
                 $.PrivateBin.Alert.showRemaining(['%s' + message + '%d', string, number]);
                 $.PrivateBin.Alert.showRemaining(['%s' + message + '%d', string, number]);
-                var result = $('body').html();
+                const result = $('body').html();
                 return expected === result;
                 return expected === result;
             }
             }
         );
         );
@@ -92,20 +259,42 @@ describe('Alert', function () {
         });
         });
 
 
         jsc.property(
         jsc.property(
-            'shows a loading message',
+            'shows a loading message (basic)',
+            jsc.array(common.jscAlnumString()),
+            jsc.array(common.jscAlnumString()),
+            function (message, icon) {
+                message = message.join('');
+                icon = icon.join('');
+                const defaultMessage = 'Loading…';
+                if (message.length === 0) {
+                    message = defaultMessage;
+                }
+                const expected = '<div id="loadingindicator" class="">' + message + '</div>';
+                $('body').html(
+                    '<div id="loadingindicator" class="hidden">' + defaultMessage + '</div>'
+                );
+                $.PrivateBin.Alert.init();
+                $.PrivateBin.Alert.showLoading(message, icon);
+                const result = $('body').html();
+                return expected === result;
+            }
+        );
+
+        jsc.property(
+            'shows a loading message (bootstrap)',
             jsc.array(common.jscAlnumString()),
             jsc.array(common.jscAlnumString()),
             jsc.array(common.jscAlnumString()),
             jsc.array(common.jscAlnumString()),
             function (message, icon) {
             function (message, icon) {
                 message = message.join('');
                 message = message.join('');
                 icon = icon.join('');
                 icon = icon.join('');
-                var defaultMessage = 'Loading…';
+                const defaultMessage = 'Loading…';
                 if (message.length === 0) {
                 if (message.length === 0) {
                     message = defaultMessage;
                     message = defaultMessage;
                 }
                 }
-                var expected = '<ul class="nav navbar-nav"><li ' +
+                const expected = '<ul class="nav navbar-nav"><li ' +
                     'id="loadingindicator" class="navbar-text"><span ' +
                     'id="loadingindicator" class="navbar-text"><span ' +
                     'class="glyphicon glyphicon-' + icon +
                     'class="glyphicon glyphicon-' + icon +
-                    '" aria-hidden="true"></span> ' + message + '</li></ul>';
+                    '" aria-hidden="true"></span> <span>' + message + '</span></li></ul>';
                 $('body').html(
                 $('body').html(
                     '<ul class="nav navbar-nav"><li id="loadingindicator" ' +
                     '<ul class="nav navbar-nav"><li id="loadingindicator" ' +
                     'class="navbar-text hidden"><span class="glyphicon ' +
                     'class="navbar-text hidden"><span class="glyphicon ' +
@@ -114,7 +303,7 @@ describe('Alert', function () {
                 );
                 );
                 $.PrivateBin.Alert.init();
                 $.PrivateBin.Alert.init();
                 $.PrivateBin.Alert.showLoading(message, icon);
                 $.PrivateBin.Alert.showLoading(message, icon);
-                var result = $('body').html();
+                const result = $('body').html();
                 return expected === result;
                 return expected === result;
             }
             }
         );
         );
@@ -182,7 +371,7 @@ describe('Alert', function () {
             jsc.array(common.jscAlnumString()),
             jsc.array(common.jscAlnumString()),
             function (trigger, message) {
             function (trigger, message) {
                 message = message.join('');
                 message = message.join('');
-                var handlerCalled = false,
+                let handlerCalled = false,
                     defaultMessage = 'Loading…',
                     defaultMessage = 'Loading…',
                     functions = [
                     functions = [
                         $.PrivateBin.Alert.showStatus,
                         $.PrivateBin.Alert.showStatus,

+ 83 - 0
js/test/Check.js

@@ -0,0 +1,83 @@
+'use strict';
+var common = require('../common');
+/* global Legacy, WebCrypto */
+
+describe('Check', function () {
+    describe('init', function () {
+        this.timeout(30000);
+        before(function () {
+            cleanup();
+        });
+
+        it('returns false and shows error, if a bot UA is detected', function () {
+            jsc.assert(jsc.forall(
+                'string',
+                jsc.elements(['Bot', 'bot']),
+                'string',
+                function (prefix, botBit, suffix) {
+                    const clean = jsdom(
+                        '<html><body><div id="errormessage" class="hidden"></div>' +
+                        '</body></html>', {
+                            'userAgent': prefix + botBit + suffix
+                        }
+                    );
+                    Legacy.Check.init();
+                    const result1 = Legacy.Check.getInit() && !Legacy.Check.getStatus(),
+                          result2 = (document.getElementById('errormessage').className !== 'hidden');
+                    clean();
+                    return result1 && result2;
+                }
+            ),
+            {tests: 10});
+        });
+
+        jsc.property(
+            'shows error, if no webcrypto is detected',
+            'bool',
+            jsc.elements(['localhost', '127.0.0.1', '[::1]', '']),
+            jsc.nearray(common.jscA2zString()),
+            jsc.elements(['.onion', '.i2p', '']),
+            function (secureProtocol, localhost, domain, tld) {
+                const isDomain = localhost === '',
+                      isSecureContext = secureProtocol || !isDomain || tld.length > 0,
+                      clean = jsdom(
+                          '<html><body><div id="errormessage" class="hidden"></div>' +
+                          '<div id="oldnotice" class="hidden"></div>' +
+                          '<div id="insecurecontextnotice" class="hidden"></div></body></html>',
+                          {
+                              'url': (secureProtocol ? 'https' : 'http' ) + '://' +
+                                     (isDomain ? domain.join('') + tld : localhost) + '/'
+                          }
+                      );
+                Legacy.Check.init();
+                const result1 = Legacy.Check.getInit() && !Legacy.Check.getStatus(),
+                      result2 = isSecureContext === (document.getElementById('insecurecontextnotice').className === 'hidden'),
+                      result3 = (document.getElementById('oldnotice').className !== 'hidden');
+                clean();
+                return result1 && result2 && result3;
+            }
+        );
+
+        jsc.property(
+            'shows error, if HTTP only site is detected',
+            'bool',
+            jsc.nearray(common.jscA2zString()),
+            function (secureProtocol, domain) {
+                const clean = jsdom(
+                          '<html><body><div id="httpnotice" class="hidden"></div>' +
+                          '</body></html>',
+                          {
+                              'url': (secureProtocol ? 'https' : 'http' ) + '://' + domain.join('') + '/'
+                          }
+                      );
+                window.crypto = new WebCrypto();
+                Legacy.Check.init();
+                const result1 = Legacy.Check.getInit() && Legacy.Check.getStatus(),
+                      result2 = secureProtocol === (document.getElementById('httpnotice').className === 'hidden');
+                clean();
+                return result1 && result2;
+            }
+        );
+    });
+});
+

+ 7 - 1
js/test/CryptTool.js

@@ -18,13 +18,15 @@ describe('CryptTool', function () {
                     // pause to let async functions conclude
                     // pause to let async functions conclude
                     await new Promise(resolve => setTimeout(resolve, 300));
                     await new Promise(resolve => setTimeout(resolve, 300));
                     let clean = jsdom();
                     let clean = jsdom();
+                    // ensure zlib is getting loaded
+                    $.PrivateBin.Controller.initZ();
                     window.crypto = new WebCrypto();
                     window.crypto = new WebCrypto();
                     message = message.trim();
                     message = message.trim();
                     let cipherMessage = await $.PrivateBin.CryptTool.cipher(
                     let cipherMessage = await $.PrivateBin.CryptTool.cipher(
                             key, password, message, []
                             key, password, message, []
                         ),
                         ),
                         plaintext = await $.PrivateBin.CryptTool.decipher(
                         plaintext = await $.PrivateBin.CryptTool.decipher(
-                                key, password, cipherMessage
+                            key, password, cipherMessage
                         );
                         );
                     clean();
                     clean();
                     return message === plaintext;
                     return message === plaintext;
@@ -179,6 +181,8 @@ describe('CryptTool', function () {
             let message = fs.readFileSync('test/compression-sample.txt', 'utf8'),
             let message = fs.readFileSync('test/compression-sample.txt', 'utf8'),
                 clean = jsdom();
                 clean = jsdom();
             window.crypto = new WebCrypto();
             window.crypto = new WebCrypto();
+            // ensure zlib is getting loaded
+            $.PrivateBin.Controller.initZ();
             let cipherMessage = await $.PrivateBin.CryptTool.cipher(
             let cipherMessage = await $.PrivateBin.CryptTool.cipher(
                     'foo', 'bar', message, []
                     'foo', 'bar', message, []
                 ),
                 ),
@@ -222,6 +226,8 @@ isWhile : interp (while expr sBody) (MemElem mem) =
 conseq_or_bottom inv (interp (nth_iterate sBody n) (MemElem mem))
 conseq_or_bottom inv (interp (nth_iterate sBody n) (MemElem mem))
 `;
 `;
                     let clean = jsdom();
                     let clean = jsdom();
+                    // ensure zlib is getting loaded
+                    $.PrivateBin.Controller.initZ();
                     window.crypto = new WebCrypto();
                     window.crypto = new WebCrypto();
                     let cipherMessage = await $.PrivateBin.CryptTool.cipher(
                     let cipherMessage = await $.PrivateBin.CryptTool.cipher(
                             key, password, message, []
                             key, password, message, []

+ 6 - 6
js/test/Helper.js

@@ -183,9 +183,9 @@ describe('Helper', function () {
             'string',
             'string',
             'string',
             'string',
             function (prefix, uint, middle, string, postfix) {
             function (prefix, uint, middle, string, postfix) {
-                prefix  =  prefix.replace(/%(s|d)/g, '%%');
-                middle  =  middle.replace(/%(s|d)/g, '%%');
-                postfix = postfix.replace(/%(s|d)/g, '%%');
+                prefix  =  prefix.replace(/%(s|d)/g, '');
+                middle  =  middle.replace(/%(s|d)/g, '');
+                postfix = postfix.replace(/%(s|d)/g, '');
                 var params = [prefix + '%d' + middle + '%s' + postfix, uint, string],
                 var params = [prefix + '%d' + middle + '%s' + postfix, uint, string],
                     result = prefix + uint + middle + string + postfix;
                     result = prefix + uint + middle + string + postfix;
                 return result === $.PrivateBin.Helper.sprintf.apply(this, params);
                 return result === $.PrivateBin.Helper.sprintf.apply(this, params);
@@ -199,9 +199,9 @@ describe('Helper', function () {
             'string',
             'string',
             'string',
             'string',
             function (prefix, uint, middle, string, postfix) {
             function (prefix, uint, middle, string, postfix) {
-                prefix  =  prefix.replace(/%(s|d)/g, '%%');
-                middle  =  middle.replace(/%(s|d)/g, '%%');
-                postfix = postfix.replace(/%(s|d)/g, '%%');
+                prefix  =  prefix.replace(/%(s|d)/g, '');
+                middle  =  middle.replace(/%(s|d)/g, '');
+                postfix = postfix.replace(/%(s|d)/g, '');
                 var params = [prefix + '%s' + middle + '%d' + postfix, string, uint],
                 var params = [prefix + '%s' + middle + '%d' + postfix, string, uint],
                     result = prefix + string + middle + uint + postfix;
                     result = prefix + string + middle + uint + postfix;
                 return result === $.PrivateBin.Helper.sprintf.apply(this, params);
                 return result === $.PrivateBin.Helper.sprintf.apply(this, params);

+ 0 - 88
js/test/InitialCheck.js

@@ -1,88 +0,0 @@
-'use strict';
-var common = require('../common');
-
-describe('InitialCheck', function () {
-    describe('init', function () {
-        this.timeout(30000);
-        before(function () {
-            cleanup();
-        });
-
-        it('returns false and shows error, if a bot UA is detected', function () {
-            jsc.assert(jsc.forall(
-                'string',
-                jsc.elements(['Bot', 'bot']),
-                'string',
-                function (prefix, botBit, suffix) {
-                    const clean = jsdom('', {
-                        'userAgent': prefix + botBit + suffix
-                    });
-                    $('body').html(
-                        '<html><body><div id="errormessage" class="hidden"></div>' +
-                        '</body></html>'
-                    );
-                    $.PrivateBin.Alert.init();
-                    window.crypto = null;
-                    const result1 = !$.PrivateBin.InitialCheck.init(),
-                          result2 = !$('#errormessage').hasClass('hidden');
-                    clean();
-                    return result1 && result2;
-                }
-            ),
-            {tests: 10});
-        });
-
-        jsc.property(
-            'shows error, if no webcrypto is detected',
-            'bool',
-            jsc.elements(['localhost', '127.0.0.1', '[::1]', '']),
-            jsc.nearray(common.jscA2zString()),
-            jsc.elements(['.onion', '.i2p', '']),
-            function (secureProtocol, localhost, domain, tld) {
-                const isDomain = localhost === '',
-                      isSecureContext = secureProtocol || !isDomain || tld.length > 0,
-                      clean = jsdom('', {
-                          'url': (secureProtocol ? 'https' : 'http' ) + '://' +
-                                 (isDomain ? domain.join('') + tld : localhost) + '/'
-                      });
-                $('body').html(
-                    '<html><body><div id="errormessage" class="hidden"></div>'+
-                    '<div id="oldnotice" class="hidden"></div></body></html>'
-                );
-                $.PrivateBin.Alert.init();
-                const result1 = !$.PrivateBin.InitialCheck.init(),
-                      result2 = isSecureContext === $('#errormessage').hasClass('hidden'),
-                      result3 = !$('#oldnotice').hasClass('hidden');
-                clean();
-                return result1 && result2 && result3;
-            }
-        );
-
-        jsc.property(
-            'shows error, if HTTP only site is detected',
-            'bool',
-            jsc.elements(['localhost', '127.0.0.1', '[::1]', '']),
-            jsc.nearray(common.jscA2zString()),
-            jsc.elements(['.onion', '.i2p', '']),
-            function (secureProtocol, localhost, domain, tld) {
-                const isDomain = localhost === '',
-                      isSecureContext = secureProtocol || !isDomain || tld.length > 0,
-                      clean = jsdom('', {
-                          'url': (secureProtocol ? 'https' : 'http' ) + '://' +
-                               (isDomain ? domain.join('') + tld : localhost) + '/'
-                      });
-                $('body').html(
-                    '<html><body><div id="httpnotice" class="hidden"></div>'+
-                    '</body></html>'
-                );
-                $.PrivateBin.Alert.init();
-                window.crypto = null;
-                const result1 = $.PrivateBin.InitialCheck.init(),
-                      result2 = isSecureContext === $('#httpnotice').hasClass('hidden');
-                clean();
-                return result1 && result2;
-            }
-        );
-    });
-});
-

+ 1 - 0
lib/Controller.php

@@ -388,6 +388,7 @@ class Controller
         $page->assign('URLSHORTENER', $this->_conf->getKey('urlshortener'));
         $page->assign('URLSHORTENER', $this->_conf->getKey('urlshortener'));
         $page->assign('QRCODE', $this->_conf->getKey('qrcode'));
         $page->assign('QRCODE', $this->_conf->getKey('qrcode'));
         $page->assign('HTTPWARNING', $this->_conf->getKey('httpwarning'));
         $page->assign('HTTPWARNING', $this->_conf->getKey('httpwarning'));
+        $page->assign('HTTPSLINK', 'https://' . $this->_request->getHost() . $this->_request->getRequestUri());
         $page->assign('COMPRESSION', $this->_conf->getKey('compression'));
         $page->assign('COMPRESSION', $this->_conf->getKey('compression'));
         $page->draw($this->_conf->getKey('template'));
         $page->draw($this->_conf->getKey('template'));
     }
     }

+ 15 - 2
lib/Request.php

@@ -193,6 +193,19 @@ class Request
             $this->_params[$param] : $default;
             $this->_params[$param] : $default;
     }
     }
 
 
+    /**
+     * Get host as requested by the client
+     *
+     * @access public
+     * @return string
+     */
+    public function getHost()
+    {
+        return array_key_exists('HTTP_HOST', $_SERVER) ?
+            htmlspecialchars($_SERVER['HTTP_HOST']) :
+            'localhost';
+    }
+
     /**
     /**
      * Get request URI
      * Get request URI
      *
      *
@@ -202,8 +215,8 @@ class Request
     public function getRequestUri()
     public function getRequestUri()
     {
     {
         return array_key_exists('REQUEST_URI', $_SERVER) ?
         return array_key_exists('REQUEST_URI', $_SERVER) ?
-            htmlspecialchars(
-                parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH)
+        htmlspecialchars(
+            parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH)
             ) : '/';
             ) : '/';
     }
     }
 
 

+ 0 - 1
robots.txt

@@ -5,5 +5,4 @@
 # directory.
 # directory.
 
 
 User-agent: *
 User-agent: *
-Allow: /index.php
 Disallow: /
 Disallow: /

+ 6 - 8
tpl/bootstrap.php

@@ -71,10 +71,8 @@ if ($MARKDOWN):
 endif;
 endif;
 ?>
 ?>
 		<script type="text/javascript" data-cfasync="false" src="js/purify-2.0.1.js" integrity="sha512-ddI36MdUoXp/o7yhQtr9/qj4G3oFwCRga4jCGaoUYtORg0PPmFKVKG4Ess3fIknYzxwwKMlrIL9o4NwuPTCc1Q==" crossorigin="anonymous"></script>
 		<script type="text/javascript" data-cfasync="false" src="js/purify-2.0.1.js" integrity="sha512-ddI36MdUoXp/o7yhQtr9/qj4G3oFwCRga4jCGaoUYtORg0PPmFKVKG4Ess3fIknYzxwwKMlrIL9o4NwuPTCc1Q==" crossorigin="anonymous"></script>
-		<script type="text/javascript" data-cfasync="false" src="js/privatebin.js?<?php echo rawurlencode($VERSION); ?>" integrity="sha512-86VTqw2HsaCQ0DAunK2MH68P+8RLbbaK7HZP8nwDtwNoF44usxDCptmD8TC+zwQc7HM46AkrvVFb3ZkIb6VhMQ==" crossorigin="anonymous"></script>
-		<!--[if IE]>
-		<style type="text/css">body {padding-left:60px;padding-right:60px;} #ienotice {display:block;}</style>
-		<![endif]-->
+		<script type="text/javascript" data-cfasync="false" src="js/legacy.js?<?php echo rawurlencode($VERSION); ?>" integrity="sha512-dReLYyMJO3kV7/etVKlGWSbmJoWavMpbgG8Hg3759MNHv0mYuonh4Azif3y0F0oopKKlz3vtj8XZV7VY+e6dSQ==" crossorigin="anonymous"></script>
+		<script type="text/javascript" data-cfasync="false" src="js/privatebin.js?<?php echo rawurlencode($VERSION); ?>" integrity="sha512-4iMFqnyyoJ/FJ33aHov+QeItaZ0JegMUjx7J5pXeknEwjXzx4oLw9F9ePI3WK/h3sUYTOK+hdv2JINNGMwi2Vg==" crossorigin="anonymous"></script>
 		<link rel="apple-touch-icon" href="img/apple-touch-icon.png?<?php echo rawurlencode($VERSION); ?>" sizes="180x180" />
 		<link rel="apple-touch-icon" href="img/apple-touch-icon.png?<?php echo rawurlencode($VERSION); ?>" sizes="180x180" />
 		<link rel="icon" type="image/png" href="img/favicon-32x32.png?<?php echo rawurlencode($VERSION); ?>" sizes="32x32" />
 		<link rel="icon" type="image/png" href="img/favicon-32x32.png?<?php echo rawurlencode($VERSION); ?>" sizes="32x32" />
 		<link rel="icon" type="image/png" href="img/favicon-16x16.png?<?php echo rawurlencode($VERSION); ?>" sizes="16x16" />
 		<link rel="icon" type="image/png" href="img/favicon-16x16.png?<?php echo rawurlencode($VERSION); ?>" sizes="16x16" />
@@ -446,10 +444,6 @@ endif;
 				<div id="oldnotice" role="alert" class="hidden alert alert-danger">
 				<div id="oldnotice" role="alert" class="hidden alert alert-danger">
 					<span class="glyphicon glyphicon-alert" aria-hidden="true"></span>
 					<span class="glyphicon glyphicon-alert" aria-hidden="true"></span>
 					<?php echo I18n::_('%s requires a modern browser to work.', I18n::_($NAME)), PHP_EOL; ?>
 					<?php echo I18n::_('%s requires a modern browser to work.', I18n::_($NAME)), PHP_EOL; ?>
-				</div>
-				<div id="ienotice" role="alert" class="hidden alert alert-danger">
-					<span class="glyphicon glyphicon-question-sign" aria-hidden="true"></span>
-					<?php echo I18n::_('Still using Internet Explorer? Do yourself a favor, switch to a modern browser:'), PHP_EOL; ?>
 					<a href="https://www.mozilla.org/firefox/">Firefox</a>,
 					<a href="https://www.mozilla.org/firefox/">Firefox</a>,
 					<a href="https://www.opera.com/">Opera</a>,
 					<a href="https://www.opera.com/">Opera</a>,
 					<a href="https://www.google.com/chrome">Chrome</a>…
 					<a href="https://www.google.com/chrome">Chrome</a>…
@@ -462,6 +456,10 @@ if ($HTTPWARNING):
 					<?php echo I18n::_('This website is using an insecure connection! Please only use it for testing.'), PHP_EOL; ?><br />
 					<?php echo I18n::_('This website is using an insecure connection! Please only use it for testing.'), PHP_EOL; ?><br />
 					<span class="small"><?php echo I18n::_('For more information <a href="%s">see this FAQ entry</a>.', 'https://github.com/PrivateBin/PrivateBin/wiki/FAQ#why-does-it-show-me-an-error-about-an-insecure-connection'); ?></span>
 					<span class="small"><?php echo I18n::_('For more information <a href="%s">see this FAQ entry</a>.', 'https://github.com/PrivateBin/PrivateBin/wiki/FAQ#why-does-it-show-me-an-error-about-an-insecure-connection'); ?></span>
 				</div>
 				</div>
+				<div id="insecurecontextnotice" role="alert" class="hidden alert alert-danger">
+					<span class="glyphicon glyphicon-alert" aria-hidden="true"></span>
+					<?php echo I18n::_('Your browser may require an HTTPS connection to support the WebCrypto API. Try <a href="%s">switching to HTTPS</a>.', $HTTPSLINK); ?>
+				</div>
 <?php
 <?php
 endif;
 endif;
 ?>
 ?>

+ 7 - 7
tpl/page.php

@@ -49,10 +49,8 @@ if ($MARKDOWN):
 endif;
 endif;
 ?>
 ?>
 		<script type="text/javascript" data-cfasync="false" src="js/purify-2.0.1.js" integrity="sha512-ddI36MdUoXp/o7yhQtr9/qj4G3oFwCRga4jCGaoUYtORg0PPmFKVKG4Ess3fIknYzxwwKMlrIL9o4NwuPTCc1Q==" crossorigin="anonymous"></script>
 		<script type="text/javascript" data-cfasync="false" src="js/purify-2.0.1.js" integrity="sha512-ddI36MdUoXp/o7yhQtr9/qj4G3oFwCRga4jCGaoUYtORg0PPmFKVKG4Ess3fIknYzxwwKMlrIL9o4NwuPTCc1Q==" crossorigin="anonymous"></script>
-		<script type="text/javascript" data-cfasync="false" src="js/privatebin.js?<?php echo rawurlencode($VERSION); ?>" integrity="sha512-86VTqw2HsaCQ0DAunK2MH68P+8RLbbaK7HZP8nwDtwNoF44usxDCptmD8TC+zwQc7HM46AkrvVFb3ZkIb6VhMQ==" crossorigin="anonymous"></script>
-		<!--[if IE]>
-		<style type="text/css">body {padding-left:60px;padding-right:60px;} #ienotice {display:block;}</style>
-		<![endif]-->
+		<script type="text/javascript" data-cfasync="false" src="js/legacy.js?<?php echo rawurlencode($VERSION); ?>" integrity="sha512-dReLYyMJO3kV7/etVKlGWSbmJoWavMpbgG8Hg3759MNHv0mYuonh4Azif3y0F0oopKKlz3vtj8XZV7VY+e6dSQ==" crossorigin="anonymous"></script>
+		<script type="text/javascript" data-cfasync="false" src="js/privatebin.js?<?php echo rawurlencode($VERSION); ?>" integrity="sha512-4iMFqnyyoJ/FJ33aHov+QeItaZ0JegMUjx7J5pXeknEwjXzx4oLw9F9ePI3WK/h3sUYTOK+hdv2JINNGMwi2Vg==" crossorigin="anonymous"></script>
 		<link rel="apple-touch-icon" href="img/apple-touch-icon.png?<?php echo rawurlencode($VERSION); ?>" sizes="180x180" />
 		<link rel="apple-touch-icon" href="img/apple-touch-icon.png?<?php echo rawurlencode($VERSION); ?>" sizes="180x180" />
 		<link rel="icon" type="image/png" href="img/favicon-32x32.png?<?php echo rawurlencode($VERSION); ?>" sizes="32x32" />
 		<link rel="icon" type="image/png" href="img/favicon-32x32.png?<?php echo rawurlencode($VERSION); ?>" sizes="32x32" />
 		<link rel="icon" type="image/png" href="img/favicon-16x16.png?<?php echo rawurlencode($VERSION); ?>" sizes="16x16" />
 		<link rel="icon" type="image/png" href="img/favicon-16x16.png?<?php echo rawurlencode($VERSION); ?>" sizes="16x16" />
@@ -77,8 +75,7 @@ endif;
 			<h2 class="title"><?php echo I18n::_('Because ignorance is bliss'); ?></h2><br />
 			<h2 class="title"><?php echo I18n::_('Because ignorance is bliss'); ?></h2><br />
 			<h3 class="title"><?php echo $VERSION; ?></h3>
 			<h3 class="title"><?php echo $VERSION; ?></h3>
 			<noscript><div id="noscript" class="nonworking"><?php echo I18n::_('JavaScript is required for %s to work.<br />Sorry for the inconvenience.', I18n::_($NAME)); ?></div></noscript>
 			<noscript><div id="noscript" class="nonworking"><?php echo I18n::_('JavaScript is required for %s to work.<br />Sorry for the inconvenience.', I18n::_($NAME)); ?></div></noscript>
-			<div id="oldnotice" class="nonworking"><?php echo I18n::_('%s requires a modern browser to work.', I18n::_($NAME)); ?></div>
-			<div id="ienotice" class="nonworking"><?php echo I18n::_('Still using Internet Explorer? Do yourself a favor, switch to a modern browser:'), PHP_EOL; ?>
+			<div id="oldnotice" class="nonworking hidden"><?php echo I18n::_('%s requires a modern browser to work.', I18n::_($NAME)), PHP_EOL; ?>
 				<a href="https://www.mozilla.org/firefox/">Firefox</a>,
 				<a href="https://www.mozilla.org/firefox/">Firefox</a>,
 				<a href="https://www.opera.com/">Opera</a>,
 				<a href="https://www.opera.com/">Opera</a>,
 				<a href="https://www.google.com/chrome">Chrome</a>…
 				<a href="https://www.google.com/chrome">Chrome</a>…
@@ -86,10 +83,13 @@ endif;
 <?php
 <?php
 if ($HTTPWARNING):
 if ($HTTPWARNING):
 ?>
 ?>
-			<div id="httpnotice" class="errorMessage">
+			<div id="httpnotice" class="errorMessage hidden">
 				<?php echo I18n::_('This website is using an insecure connection! Please only use it for testing.'); ?>
 				<?php echo I18n::_('This website is using an insecure connection! Please only use it for testing.'); ?>
 				<span class="small"><?php echo I18n::_('For more information <a href="%s">see this FAQ entry</a>.', 'https://github.com/PrivateBin/PrivateBin/wiki/FAQ#why-does-it-show-me-an-error-about-an-insecure-connection'); ?></span>
 				<span class="small"><?php echo I18n::_('For more information <a href="%s">see this FAQ entry</a>.', 'https://github.com/PrivateBin/PrivateBin/wiki/FAQ#why-does-it-show-me-an-error-about-an-insecure-connection'); ?></span>
 			</div>
 			</div>
+			<div id="insecurecontextnotice" class="errorMessage hidden">
+				<?php echo I18n::_('Your browser may require an HTTPS connection to support the WebCrypto API. Try <a href="%s">switching to HTTPS</a>.', $HTTPSLINK); ?>
+			</div>
 <?php
 <?php
 endif;
 endif;
 ?>
 ?>

+ 1 - 0
tst/ViewTest.php

@@ -56,6 +56,7 @@ class ViewTest extends PHPUnit_Framework_TestCase
         $page->assign('URLSHORTENER', '');
         $page->assign('URLSHORTENER', '');
         $page->assign('QRCODE', true);
         $page->assign('QRCODE', true);
         $page->assign('HTTPWARNING', true);
         $page->assign('HTTPWARNING', true);
+        $page->assign('HTTPSLINK', 'https://example.com/');
         $page->assign('COMPRESSION', 'zlib');
         $page->assign('COMPRESSION', 'zlib');
 
 
         $dir = dir(PATH . 'tpl');
         $dir = dir(PATH . 'tpl');