From fd2cd292676208b90796858be50b74ebe6d683d0 Mon Sep 17 00:00:00 2001 From: idarlund Date: Tue, 10 Jan 2017 14:56:21 +0100 Subject: [PATCH 0001/1638] Create no.json norwegian translation first commit --- i18n/no.json | 151 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 151 insertions(+) create mode 100644 i18n/no.json diff --git a/i18n/no.json b/i18n/no.json new file mode 100644 index 00000000..05466d8a --- /dev/null +++ b/i18n/no.json @@ -0,0 +1,151 @@ +{ + "en": "no", + "Paste does not exist, has expired or has been deleted.": + "Innlegget eksisterer ikke, er utløpt eller har blitt slettet.", + "PrivateBin requires php 5.3.0 or above to work. Sorry.": + "Beklager, PrivateBin krever php 5.3.0 eller nyere for å kjøre.", + "PrivateBin requires configuration section [%s] to be present in configuration file.": + "PrivateBin krever konfigurasjonsdel [%s] å være til stede i konfigurasjonsfilen .", + "Please wait %d seconds between each post.": + "Vent %d sekunder mellom hvert innlegg.", + "Paste is limited to %s of encrypted data.": + "Innlegg er begrenset til %s av kryptert data.", + "Invalid data.": + "Ugyldige data.", + "You are unlucky. Try again.": + "Du er uheldig. Prøv igjen.", + "Error saving comment. Sorry.": + "Beklager, det oppstod en feil ved lagring kommentar.", + "Error saving paste. Sorry.": + "Beklager, det oppstod en feil ved lagring innlegg.", + "Invalid paste ID.": + "Feil innlegg ID.", + "Paste is not of burn-after-reading type.": + "Innlegg er ikke av slett-etter-lesing type.", + "Wrong deletion token. Paste was not deleted.": + "Feil slettenøkkel. Innlegg ble ikke slettet.", + "Paste was properly deleted.": + "Innlegget er slettet.", + "PrivateBin": "PrivateBin", + "PrivateBin is a minimalist, open source online pastebin where the server has zero knowledge of pasted data. Data is encrypted/decrypted in the browser using 256 bits AES. More information on the project page.": + "PrivateBin er en minimalistisk, åpen kildekode, elektronisk tilgjengelig pastebin hvor serveren ikke har kunnskap om dataene som limes inn. Dataene krypteres/dekrypteres i nettleseren ved hjelp av 256 bits AES. Mer informasjon om prosjektet på prosjektsiden.", + "Because ignorance is bliss": + "Fordi uvitenhet er lykke", + "JavaScript is required for PrivateBin to work.
Sorry for the inconvenience.": + "Beklager, Javascript kreves for at PrivateBin skal fungere.", + "PrivateBin requires a modern browser to work.": + "PrivateBin 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, bytt til en moderne nettleser:", + "New": + "Ny", + "Send": + "Send", + "Clone": + "Kopier", + "Raw text": + "Ren tekst", + "Expires": + "Utgår", + "Burn after reading": + "Slett etter lesing", + "Open discussion": + "Åpen diskusjon", + "Password (recommended)": + "Passord (anbefalt)", + "Discussion": + "Diskusjon", + "Toggle navigation": + "Navigasjon", + "%d seconds": ["%d sekund", "%d sekunder"], + "%d minutes": ["%d minutt", "%d minutter"], + "%d hours": ["%d time", "%d timer"], + "%d days": ["%d dag", "%d dager"], + "%d weeks": ["%d uke", "%d uker"], + "%d months": ["%d måned", "%d måneder"], + "%d years": ["%d år", "%d år"], + "Never": + "Aldri", + "Note: This is a test service: Data may be deleted anytime. Kittens will die if you abuse this service.": + "Merk: Dette er en test tjeneste: Data kan slettes når som helst. Kattunger vil dø hvis du misbruker denne tjenesten.", + "This document will expire in %d seconds.": + ["Dette dokumentet vil utløpe om %d sekund.", "Dette dokumentet vil utløpe om %d sekunder."], + "This document will expire in %d minutes.": + ["Dette dokumentet vil utløpe om %d minutt.", "Dette dokumentet vil utløpe om %d minutter."], + "This document will expire in %d hours.": + ["Dette dokumentet vil utløpe om %d time.", "Dette dokumentet vil utløpe om %d timer."], + "This document will expire in %d days.": + ["Dette dokumentet vil utløpe om %d dag.", "Dette dokumentet vil utløpe om %d dager."], + "This document will expire in %d months.": + ["Dette dokumentet vil utløpe om %d måned.", "Dette dokumentet vil utløpe om %d måneder."], + "Please enter the password for this paste:": + "Skriv inn passordet for dette innlegget:", + "Could not decrypt data (Wrong key?)": + "Kunne ikke dekryptere data (feil nøkkel?)", + "Could not delete the paste, it was not stored in burn after reading mode.": + "Kan ikke slette innlegget, det ble ikke lagret i slett-etter-les modus.", + "FOR YOUR EYES ONLY. Don't close this window, this message can't be displayed again.": + "KUN FOR DINE ØYNE. Ikke lukk dette vinduet, denne meldingen kan ikke bli vist igjen.", + "Could not decrypt comment; Wrong key?": + "Kan ikke dekryptere kommentar; Feil nøkkel?", + "Reply": + "Svar", + "Anonymous": + "Anonym", + "Anonymous avatar (Vizhash of the IP address)": + "Anonym avatar (Vizhash av IP addressen)", + "Add comment": + "Legg inn kommentar", + "Optional nickname...": + "Valgfritt kallenavn...", + "Post comment": + "Send kommentar", + "Sending comment...": + "Sender Kommentar...", + "Comment posted.": + "Kommentar sendt.", + "Could not refresh display: %s": + "Kunne ikke oppdatere skjermen: %s", + "unknown status": + "ukjent status", + "server error or not responding": + "server feilet eller svarer ikke", + "Could not post comment: %s": + "Kunne ikke sende kommentar: %s", + "Sending paste (Please move your mouse for more entropy)...": + "Sender innlegg (Flytt musen for mere entropi)...", + "Sending paste...": + "Sender innlegg...", + "Your paste is %s (Hit [Ctrl]+[c] to copy)": + "Ditt innlegg er %s (Trykk [Ctrl]+[c] for å kopiere)", + "Delete data": + "Slett data", + "Could not create paste: %s": + "Kunne ikke opprette innlegg: %s", + "Cannot decrypt paste: Decryption key missing in URL (Did you use a redirector or an URL shortener which strips part of the URL?)": + "Kan ikke dekryptere innlegg: Dekrypteringsnøkkelen mangler i addressen (Har du bruket en redirector eller en addresse forkorter som fjerner en del av addressen?)", + "Format": "Format", + "Plain Text": "Ren Tekst", + "Source Code": "Kildekode", + "Markdown": "Oppmerket", + "Download attachment": "Last ned vedlegg", + "Cloned file attached.": "Kopier vedlegg.", + "Attach a file": "Legg til fil", + "Remove attachment": "Slett vedlegg", + "Your browser does not support uploading encrypted files. Please use a newer browser.": + "Nettleseren din støtter ikke å laste opp krypterte filer. Vennligst bruk en nyere nettleser.", + "Invalid attachment.": "Ugyldig vedlegg.", + "Options": "Opsjoner", + "Shorten URL": "Addresse forkorter", + "Editor": "Rediger", + "Preview": "Forhåndsvis", + "PrivateBin requires the PATH to end in a \"%s\". Please update the PATH in your index.php.": + "PrivateBin krever at PATH ender på \"%s\". Vennligst oppdater PATH i index.php.", + "Decrypt": + "Dekrypter", + "Enter password": + "Skriv inn passord", + "Loading…": "Laster…", + "In case this message never disappears please have a look at this FAQ for information to troubleshoot.": + "Hvis denne meldingen ikke forsvinner kan du ta en titt på denne FAQen for informasjon om feilsøking." +} From 4a49613b47119170fa0fa8fd4f51955e57e41bb2 Mon Sep 17 00:00:00 2001 From: El RIDO Date: Tue, 10 Jan 2017 20:28:15 +0100 Subject: [PATCH 0002/1638] improvements suggested by @atluxity --- i18n/no.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/i18n/no.json b/i18n/no.json index 05466d8a..40b6920a 100644 --- a/i18n/no.json +++ b/i18n/no.json @@ -135,8 +135,8 @@ "Your browser does not support uploading encrypted files. Please use a newer browser.": "Nettleseren din støtter ikke å laste opp krypterte filer. Vennligst bruk en nyere nettleser.", "Invalid attachment.": "Ugyldig vedlegg.", - "Options": "Opsjoner", - "Shorten URL": "Addresse forkorter", + "Options": "Innstillinger", + "Shorten URL": "Addresse-forkorter", "Editor": "Rediger", "Preview": "Forhåndsvis", "PrivateBin requires the PATH to end in a \"%s\". Please update the PATH in your index.php.": From a7de0e095b9cccbff4b4a3bfafa5de72506c074f Mon Sep 17 00:00:00 2001 From: El RIDO Date: Tue, 10 Jan 2017 20:37:14 +0100 Subject: [PATCH 0003/1638] added supported language, updated credits and changelog --- CHANGELOG.md | 2 +- CREDITS.md | 1 + js/privatebin.js | 4 ++-- lib/I18n.php | 2 +- tpl/bootstrap.php | 2 +- tpl/page.php | 2 +- tst/I18nTest.php | 10 ++++++++++ 7 files changed, 17 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6197bb4b..2dc2e3c7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,7 @@ # PrivateBin version history * **next (not yet released)** - * ADDED: Translations for Spanish and Occitan + * ADDED: Translations for Spanish, Occitan and Norwegian * ADDED: Option in configuration to change the default "PrivateBin" title of the site * CHANGED: Cleanup of bootstrap template variants and moved icons to `img` directory * **1.1 (2016-12-26)** diff --git a/CREDITS.md b/CREDITS.md index 47d0bc2f..dfb2d83e 100644 --- a/CREDITS.md +++ b/CREDITS.md @@ -34,3 +34,4 @@ Sébastien Sauvage - original idea and main developer * R4SAS - Russian * Alfredo Fabián Altamirano Tena - Spanish * Quent-in - Occitan +* idarlund - Norwegian diff --git a/js/privatebin.js b/js/privatebin.js index 7030afb9..60e27754 100644 --- a/js/privatebin.js +++ b/js/privatebin.js @@ -329,7 +329,7 @@ $(function() { /** * supported languages, minus the built in 'en' */ - supportedLanguages: ['de', 'es', 'fr', 'it', 'pl', 'oc', 'ru', 'sl', 'zh'], + supportedLanguages: ['de', 'es', 'fr', 'it', 'no', 'pl', 'oc', 'ru', 'sl', 'zh'], /** * translate a string, alias for translate() @@ -420,7 +420,7 @@ $(function() { return (n % 10 === 1 && n % 100 !== 11 ? 0 : (n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 10 || n % 100 >= 20) ? 1 : 2)); case 'sl': return (n % 100 === 1 ? 1 : (n % 100 === 2 ? 2 : (n % 100 === 3 || n % 100 === 4 ? 3 : 0))); - // de, en, es, it + // de, en, es, it, no default: return (n !== 1 ? 1 : 0); } diff --git a/lib/I18n.php b/lib/I18n.php index 6419fee3..4c59ef57 100644 --- a/lib/I18n.php +++ b/lib/I18n.php @@ -304,7 +304,7 @@ class I18n return $n % 10 == 1 && $n % 100 != 11 ? 0 : ($n % 10 >= 2 && $n % 10 <= 4 && ($n % 100 < 10 || $n % 100 >= 20) ? 1 : 2); case 'sl': return $n % 100 == 1 ? 1 : ($n % 100 == 2 ? 2 : ($n % 100 == 3 || $n % 100 == 4 ? 3 : 0)); - // de, en, es, it + // de, en, es, it, no default: return $n != 1 ? 1 : 0; } diff --git a/tpl/bootstrap.php b/tpl/bootstrap.php index 951ba1f6..aa91756c 100644 --- a/tpl/bootstrap.php +++ b/tpl/bootstrap.php @@ -69,7 +69,7 @@ if ($MARKDOWN): - + diff --git a/tpl/page.php b/tpl/page.php index a4bdce32..484b299e 100644 --- a/tpl/page.php +++ b/tpl/page.php @@ -47,7 +47,7 @@ if ($MARKDOWN): - + diff --git a/tst/I18nTest.php b/tst/I18nTest.php index 7c75b696..91c92aa6 100644 --- a/tst/I18nTest.php +++ b/tst/I18nTest.php @@ -58,6 +58,16 @@ class I18nTest extends PHPUnit_Framework_TestCase $this->assertEquals('2 heures', I18n::_('%d hours', 2), '2 hours in French'); } + public function testBrowserLanguageNoDetection() + { + $_SERVER['HTTP_ACCEPT_LANGUAGE'] = 'no;q=0.8,en-GB;q=0.6,en-US;q=0.4,en;q=0.2'; + I18n::loadTranslations(); + $this->assertEquals('no', I18n::_('en'), 'browser language no'); + $this->assertEquals('0 timer', I18n::_('%d hours', 0), '0 hours in Norwegian'); + $this->assertEquals('1 time', I18n::_('%d hours', 1), '1 hour in Norwegian'); + $this->assertEquals('2 timer', I18n::_('%d hours', 2), '2 hours in Norwegian'); + } + public function testBrowserLanguageOcDetection() { $_SERVER['HTTP_ACCEPT_LANGUAGE'] = 'oc;q=0.8,en-GB;q=0.6,en-US;q=0.4,en;q=0.2'; From d6fd2b0e89d1c128a7886aaf713233ee05313e42 Mon Sep 17 00:00:00 2001 From: El RIDO Date: Tue, 10 Jan 2017 20:43:49 +0100 Subject: [PATCH 0004/1638] updated norwegian translation to new translation file convention --- i18n/no.json | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/i18n/no.json b/i18n/no.json index 40b6920a..bf8b68c1 100644 --- a/i18n/no.json +++ b/i18n/no.json @@ -1,11 +1,16 @@ { + "PrivateBin": "PrivateBin", + "%s is a minimalist, open source online pastebin where the server has zero knowledge of pasted data. Data is encrypted/decrypted in the browser using 256 bits AES. More information on the project page.": + "%s er en minimalistisk, åpen kildekode, elektronisk tilgjengelig pastebin hvor serveren ikke har kunnskap om dataene som limes inn. Dataene krypteres/dekrypteres i nettleseren ved hjelp av 256 bits AES. Mer informasjon om prosjektet på prosjektsiden.", + "Because ignorance is bliss": + "Fordi uvitenhet er lykke", "en": "no", "Paste does not exist, has expired or has been deleted.": "Innlegget eksisterer ikke, er utløpt eller har blitt slettet.", - "PrivateBin requires php 5.3.0 or above to work. Sorry.": - "Beklager, PrivateBin krever php 5.3.0 eller nyere for å kjøre.", - "PrivateBin requires configuration section [%s] to be present in configuration file.": - "PrivateBin krever konfigurasjonsdel [%s] å være til stede i konfigurasjonsfilen .", + "%s requires php 5.3.0 or above to work. Sorry.": + "Beklager, %s krever php 5.3.0 eller nyere for å kjøre.", + "%s requires configuration section [%s] to be present in configuration file.": + "%s krever konfigurasjonsdel [%s] å være til stede i konfigurasjonsfilen .", "Please wait %d seconds between each post.": "Vent %d sekunder mellom hvert innlegg.", "Paste is limited to %s of encrypted data.": @@ -26,15 +31,10 @@ "Feil slettenøkkel. Innlegg ble ikke slettet.", "Paste was properly deleted.": "Innlegget er slettet.", - "PrivateBin": "PrivateBin", - "PrivateBin is a minimalist, open source online pastebin where the server has zero knowledge of pasted data. Data is encrypted/decrypted in the browser using 256 bits AES. More information on the project page.": - "PrivateBin er en minimalistisk, åpen kildekode, elektronisk tilgjengelig pastebin hvor serveren ikke har kunnskap om dataene som limes inn. Dataene krypteres/dekrypteres i nettleseren ved hjelp av 256 bits AES. Mer informasjon om prosjektet på prosjektsiden.", - "Because ignorance is bliss": - "Fordi uvitenhet er lykke", - "JavaScript is required for PrivateBin to work.
Sorry for the inconvenience.": - "Beklager, Javascript kreves for at PrivateBin skal fungere.", - "PrivateBin requires a modern browser to work.": - "PrivateBin krever en moderne nettleser for å fungere.", + "JavaScript is required for %s to work.
Sorry for the inconvenience.": + "Beklager, Javascript kreves for at %s skal fungere.", + "%s requires a modern browser to work.": + "%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, bytt til en moderne nettleser:", "New": @@ -139,8 +139,8 @@ "Shorten URL": "Addresse-forkorter", "Editor": "Rediger", "Preview": "Forhåndsvis", - "PrivateBin requires the PATH to end in a \"%s\". Please update the PATH in your index.php.": - "PrivateBin krever at PATH ender på \"%s\". Vennligst oppdater PATH i index.php.", + "%s requires the PATH to end in a \"%s\". Please update the PATH in your index.php.": + "%s krever at PATH ender på \"%s\". Vennligst oppdater PATH i index.php.", "Decrypt": "Dekrypter", "Enter password": From 9acb121e67ffd744c2bc1a1702dfeaffb8cca1ab Mon Sep 17 00:00:00 2001 From: idarlund Date: Wed, 11 Jan 2017 15:19:40 +0100 Subject: [PATCH 0005/1638] Update no.json missbruker should be misbruker --- i18n/no.json | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/i18n/no.json b/i18n/no.json index bf8b68c1..34cf9714 100644 --- a/i18n/no.json +++ b/i18n/no.json @@ -12,7 +12,7 @@ "%s requires configuration section [%s] to be present in configuration file.": "%s krever konfigurasjonsdel [%s] å være til stede i konfigurasjonsfilen .", "Please wait %d seconds between each post.": - "Vent %d sekunder mellom hvert innlegg.", + "Vennligst vent %d sekunder mellom hvert innlegg.", "Paste is limited to %s of encrypted data.": "Innlegg er begrenset til %s av kryptert data.", "Invalid data.": @@ -26,17 +26,17 @@ "Invalid paste ID.": "Feil innlegg ID.", "Paste is not of burn-after-reading type.": - "Innlegg er ikke av slett-etter-lesing type.", + "Innlegg er ikke av type slett-etter-lesing.", "Wrong deletion token. Paste was not deleted.": - "Feil slettenøkkel. Innlegg ble ikke slettet.", + "Feil slettingsnøkkel. Innlegg ble ikke fjernet.", "Paste was properly deleted.": "Innlegget er slettet.", "JavaScript is required for %s to work.
Sorry for the inconvenience.": - "Beklager, Javascript kreves for at %s skal fungere.", + "Javascript kreves for at %s skal fungere
Beklager ulempene.", "%s requires a modern browser to work.": "%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, bytt til en moderne nettleser:", + "Fortsatt bruker av Internet Explorer? Gjør deg selv en tjeneste og bytt til en moderne nettleser:", "New": "Ny", "Send": @@ -56,7 +56,7 @@ "Discussion": "Diskusjon", "Toggle navigation": - "Navigasjon", + "Veksle navigasjon", "%d seconds": ["%d sekund", "%d sekunder"], "%d minutes": ["%d minutt", "%d minutter"], "%d hours": ["%d time", "%d timer"], @@ -67,7 +67,7 @@ "Never": "Aldri", "Note: This is a test service: Data may be deleted anytime. Kittens will die if you abuse this service.": - "Merk: Dette er en test tjeneste: Data kan slettes når som helst. Kattunger vil dø hvis du misbruker denne tjenesten.", + "Merk: Dette er en test tjeneste: Data kan slettes når som helst. Kattunger vil dø hvis du misbuker denne tjenesten.", "This document will expire in %d seconds.": ["Dette dokumentet vil utløpe om %d sekund.", "Dette dokumentet vil utløpe om %d sekunder."], "This document will expire in %d minutes.": @@ -79,9 +79,9 @@ "This document will expire in %d months.": ["Dette dokumentet vil utløpe om %d måned.", "Dette dokumentet vil utløpe om %d måneder."], "Please enter the password for this paste:": - "Skriv inn passordet for dette innlegget:", + "Vennligst skriv inn passordet for dette innlegget:", "Could not decrypt data (Wrong key?)": - "Kunne ikke dekryptere data (feil nøkkel?)", + "Kunne ikke dekryptere data (Feil nøkkel?)", "Could not delete the paste, it was not stored in burn after reading mode.": "Kan ikke slette innlegget, det ble ikke lagret i slett-etter-les modus.", "FOR YOUR EYES ONLY. Don't close this window, this message can't be displayed again.": @@ -93,9 +93,9 @@ "Anonymous": "Anonym", "Anonymous avatar (Vizhash of the IP address)": - "Anonym avatar (Vizhash av IP addressen)", + "Anonym avatar (Vizhash av IP adressen)", "Add comment": - "Legg inn kommentar", + "Legg til kommentar", "Optional nickname...": "Valgfritt kallenavn...", "Post comment": @@ -123,7 +123,7 @@ "Could not create paste: %s": "Kunne ikke opprette innlegg: %s", "Cannot decrypt paste: Decryption key missing in URL (Did you use a redirector or an URL shortener which strips part of the URL?)": - "Kan ikke dekryptere innlegg: Dekrypteringsnøkkelen mangler i addressen (Har du bruket en redirector eller en addresse forkorter som fjerner en del av addressen?)", + "Kan ikke dekryptere innlegg: Dekrypteringsnøkkelen mangler i adressen (Har du bruket en redirector eller en URL forkorter som fjerner en del av addressen?)", "Format": "Format", "Plain Text": "Ren Tekst", "Source Code": "Kildekode", @@ -135,8 +135,8 @@ "Your browser does not support uploading encrypted files. Please use a newer browser.": "Nettleseren din støtter ikke å laste opp krypterte filer. Vennligst bruk en nyere nettleser.", "Invalid attachment.": "Ugyldig vedlegg.", - "Options": "Innstillinger", - "Shorten URL": "Addresse-forkorter", + "Options": "Alternativer", + "Shorten URL": "Adresse-forkorter", "Editor": "Rediger", "Preview": "Forhåndsvis", "%s requires the PATH to end in a \"%s\". Please update the PATH in your index.php.": @@ -147,5 +147,5 @@ "Skriv inn passord", "Loading…": "Laster…", "In case this message never disappears please have a look at this FAQ for information to troubleshoot.": - "Hvis denne meldingen ikke forsvinner kan du ta en titt på denne FAQen for informasjon om feilsøking." + "Hvis denne meldingen ikke forsvinner kan du ta en titt på siden med ofte stilte spørsmål for informasjon om feilsøking." } From 6c523b31aa241689cc945cecc3ec0b22117ca74d Mon Sep 17 00:00:00 2001 From: idarlund Date: Wed, 11 Jan 2017 15:22:16 +0100 Subject: [PATCH 0006/1638] Update no.json --- i18n/no.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/i18n/no.json b/i18n/no.json index 34cf9714..1408b811 100644 --- a/i18n/no.json +++ b/i18n/no.json @@ -67,7 +67,7 @@ "Never": "Aldri", "Note: This is a test service: Data may be deleted anytime. Kittens will die if you abuse this service.": - "Merk: Dette er en test tjeneste: Data kan slettes når som helst. Kattunger vil dø hvis du misbuker denne tjenesten.", + "Merk: Dette er en test tjeneste: Data kan slettes når som helst. Kattunger vil dø hvis du misbruker denne tjenesten.", "This document will expire in %d seconds.": ["Dette dokumentet vil utløpe om %d sekund.", "Dette dokumentet vil utløpe om %d sekunder."], "This document will expire in %d minutes.": From db2778c64f39f863305119544c8ae662ce0a177b Mon Sep 17 00:00:00 2001 From: El RIDO Date: Sat, 14 Jan 2017 15:29:12 +0100 Subject: [PATCH 0007/1638] introduced JSDoc: changes for JSDoc compatibility and resolving inconsistencies in documentation, both semantic and in the logic --- doc/README.md | 39 +++- js/privatebin.js | 497 ++++++++++++++++++++++++++++++---------------- tpl/bootstrap.php | 2 +- tpl/page.php | 2 +- 4 files changed, 362 insertions(+), 178 deletions(-) diff --git a/doc/README.md b/doc/README.md index f0bc1997..d0ecdbcc 100644 --- a/doc/README.md +++ b/doc/README.md @@ -1,5 +1,5 @@ -Generating documentation -======================== +Generating PHP documentation +============================ In order to generate the documentation, you will need to install the following packages and its dependencies: @@ -12,7 +12,7 @@ can be found in that projects documentation. Example for Debian and Ubuntu: ```console -$ sudo aptitude install php-pear graphviz +$ sudo apt install php-pear graphviz $ sudo pear channel-discover pear.phpdoc.org $ sudo pear install phpdoc/phpDocumentor ``` @@ -20,10 +20,41 @@ $ sudo pear install phpdoc/phpDocumentor To generate the documentation, change into the main directory and run phpdoc: ```console $ cd PrivateBin -$ phpdoc -d lib/ -t doc/ +$ phpdoc -t doc/phpdoc -d lib/ ``` **Note:** When used with PHP 7, the prerelease of phpDocumentator 2.9 needs to be manually installed by downloading it from [GitHub](https://github.com/phpDocumentor/phpDocumentor2/releases/download/v2.9.0/phpDocumentor.phar) and then manually moving it to e.g. `/usr/local/bin` and making it executable. + +Generating JS documentation +============================ + +In order to generate the documentation, you will need to install the following +packages and its dependencies: +* npm + +Then you can use the node package manager to install the latest stable release +of jsdoc globally: + +```console +$ npm install -g jsdoc +``` + +Example for Debian and Ubuntu, including steps to allow current user to install +node modules globally: +```console +$ sudo apt install npm +$ sudo mkdir /usr/local/lib/node_modules +$ sudo chown -R $(whoami) $(npm config get prefix)/{lib/node_modules,bin,share} +$ npm install -g jsdoc +$ ln -s /usr/bin/nodejs /usr/local/bin/node +``` + +To generate the documentation, change into the main directory and run phpdoc: +```console +$ cd PrivateBin +$ jsdoc -d doc/jsdoc js/privatebin.js +``` + diff --git a/js/privatebin.js b/js/privatebin.js index 60e27754..c79c8174 100644 --- a/js/privatebin.js +++ b/js/privatebin.js @@ -3,10 +3,12 @@ * * a zero-knowledge paste bin * - * @link https://github.com/PrivateBin/PrivateBin - * @copyright 2012 Sébastien SAUVAGE (sebsauvage.net) - * @license https://www.opensource.org/licenses/zlib-license.php The zlib/libpng License + * @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.1 + * @name PrivateBin + * @namespace */ 'use strict'; @@ -26,13 +28,18 @@ sjcl.random.startCollectors(); $(function() { /** * static helper methods + * + * @name helper + * @class */ var helper = { /** - * Converts a duration (in seconds) into human friendly approximation. + * converts a duration (in seconds) into human friendly approximation * - * @param int seconds - * @return array + * @name helper.secondsToHuman + * @function + * @param {number} seconds + * @return {Array} */ secondsToHuman: function(seconds) { @@ -63,11 +70,13 @@ $(function() { }, /** - * Converts an associative array to an encoded string - * for appending to the anchor. + * converts an associative array to an encoded string + * for appending to the anchor * - * @param object associative_array Object to be serialized - * @return string + * @name helper.hashToParameterString + * @function + * @param {Object} associative_array - Object to be serialized + * @return {string} */ hashToParameterString: function(associativeArray) { @@ -92,10 +101,12 @@ $(function() { }, /** - * Converts a string to an associative array. + * converts a anchor string to an associative array * - * @param string parameter_string String containing parameters - * @return object + * @name helper.parameterStringToHash + * @function + * @param {string} parameter_string - String containing parameters + * @return {Object} */ parameterStringToHash: function(parameterString) { @@ -113,9 +124,11 @@ $(function() { }, /** - * Get an associative array of the parameters found in the anchor + * get an associative array of the parameters found in the anchor * - * @return object + * @name helper.getParameterHash + * @function + * @return {Object} */ getParameterHash: function() { @@ -131,10 +144,12 @@ $(function() { }, /** - * Text range selection. - * From: https://stackoverflow.com/questions/985272/jquery-selecting-text-in-an-element-akin-to-highlighting-with-your-mouse + * text range selection * - * @param string element : Indentifier of the element to select (id=""). + * @see {@link https://stackoverflow.com/questions/985272/jquery-selecting-text-in-an-element-akin-to-highlighting-with-your-mouse} + * @name helper.selectText + * @function + * @param {string} element - Indentifier of the element to select (id="") */ selectText: function(element) { @@ -162,11 +177,13 @@ $(function() { }, /** - * Set text of a DOM element (required for IE) - * This is equivalent to element.text(text) + * set text of a DOM element (required for IE), + * this is equivalent to element.text(text) * - * @param object element : a DOM element. - * @param string text : the text to enter. + * @name helper.setElementText + * @function + * @param {Object} element - a DOM element + * @param {string} text - the text to enter */ setElementText: function(element, text) { @@ -185,8 +202,10 @@ $(function() { /** * replace last child of element with message * - * @param object element : a jQuery wrapped DOM element. - * @param string message : the message to append. + * @name helper.setMessage + * @function + * @param {Object} element - a jQuery wrapped DOM element + * @param {string} message - the message to append */ setMessage: function(element, message) { @@ -202,15 +221,17 @@ $(function() { }, /** - * Convert URLs to clickable links. + * convert URLs to clickable links. * URLs to handle: - * + *
          *     magnet:?xt.1=urn:sha1:YNCKHTQCWBTRNJIV4WNAE52SJUQCZO5C&xt.2=urn:sha1:TXGCZQTH26NL6OUQAJJPFALHG2LTGBC7
-         *     http://localhost:8800/zero/?6f09182b8ea51997#WtLEUO5Epj9UHAV9JFs+6pUQZp13TuspAUjnF+iM+dM=
-         *     http://user:password@localhost:8800/zero/?6f09182b8ea51997#WtLEUO5Epj9UHAV9JFs+6pUQZp13TuspAUjnF+iM+dM=
-         * 
+         *     http://example.com:8800/zero/?6f09182b8ea51997#WtLEUO5Epj9UHAV9JFs+6pUQZp13TuspAUjnF+iM+dM=
+         *     http://user:example.com@localhost:8800/zero/?6f09182b8ea51997#WtLEUO5Epj9UHAV9JFs+6pUQZp13TuspAUjnF+iM+dM=
+         * 
* - * @param object element : a jQuery DOM element. + * @name helper.urls2links + * @function + * @param {Object} element - a jQuery DOM element */ urls2links: function(element) { @@ -231,11 +252,13 @@ $(function() { /** * minimal sprintf emulation for %s and %d formats - * From: https://stackoverflow.com/questions/610406/javascript-equivalent-to-printf-string-format#4795914 * - * @param string format - * @param mixed args one or multiple parameters injected into format string - * @return string + * @see {@link https://stackoverflow.com/questions/610406/javascript-equivalent-to-printf-string-format#4795914} + * @name helper.sprintf + * @function + * @param {string} format + * @param {...*} args - one or multiple parameters injected into format string + * @return {string} */ sprintf: function() { @@ -273,10 +296,12 @@ $(function() { /** * get value of cookie, if it was set, empty string otherwise - * From: http://www.w3schools.com/js/js_cookies.asp * - * @param string cname - * @return string + * @see {@link http://www.w3schools.com/js/js_cookies.asp} + * @name helper.getCookie + * @function + * @param {string} cname + * @return {string} */ getCookie: function(cname) { var name = cname + '='; @@ -293,12 +318,13 @@ $(function() { }, /** - * Convert all applicable characters to HTML entities. - * From: https://github.com/janl/mustache.js/blob/master/mustache.js#L60 - * Also: https://www.owasp.org/index.php/XSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet#RULE_.231_-_HTML_Escape_Before_Inserting_Untrusted_Data_into_HTML_Element_Content + * convert all applicable characters to HTML entities * - * @param string str - * @return string escaped HTML + * @see {@link https://www.owasp.org/index.php/XSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet#RULE_.231_-_HTML_Escape_Before_Inserting_Untrusted_Data_into_HTML_Element_Content} + * @name helper.htmlEntities + * @function + * @param {string} str + * @return {string} escaped HTML */ htmlEntities: function(str) { return String(str).replace( @@ -309,6 +335,11 @@ $(function() { /** * character to HTML entity lookup table + * + * @see {@link https://github.com/janl/mustache.js/blob/master/mustache.js#L60} + * @name helper.entityMap + * @enum {Object} + * @readonly */ entityMap: { '&': '&', @@ -324,19 +355,28 @@ $(function() { /** * internationalization methods + * + * @name i18n + * @class */ var i18n = { /** * supported languages, minus the built in 'en' + * + * @name i18n.supportedLanguages + * @prop {string[]} + * @readonly */ supportedLanguages: ['de', 'es', 'fr', 'it', 'no', 'pl', 'oc', 'ru', 'sl', 'zh'], /** - * translate a string, alias for translate() + * translate a string, alias for i18n.translate() * - * @param string $messageId - * @param mixed args one or multiple parameters injected into placeholders - * @return string + * @name i18n._ + * @function + * @param {string} messageId + * @param {...*} args - one or multiple parameters injected into placeholders + * @return {string} */ _: function() { @@ -346,9 +386,11 @@ $(function() { /** * translate a string * - * @param string $messageId - * @param mixed args one or multiple parameters injected into placeholders - * @return string + * @name i18n.translate + * @function + * @param {string} messageId + * @param {...*} args - one or multiple parameters injected into placeholders + * @return {string} */ translate: function() { @@ -402,10 +444,12 @@ $(function() { /** * per language functions to use to determine the plural form - * From: http://localization-guide.readthedocs.org/en/latest/l10n/pluralforms.html * - * @param int number - * @return int array key + * @see {@link http://localization-guide.readthedocs.org/en/latest/l10n/pluralforms.html} + * @name i18n.getPluralForm + * @function + * @param {number} n + * @return {number} array key */ getPluralForm: function(n) { switch (this.language) @@ -429,7 +473,9 @@ $(function() { /** * load translations into cache, then execute callback function * - * @param function callback + * @name i18n.loadTranslations + * @function + * @param {Function} callback */ loadTranslations: function(callback) { @@ -452,24 +498,35 @@ $(function() { /** * built in language + * + * @name i18n.language + * @prop {string} */ language: 'en', /** * translation cache + * + * @name i18n.translations + * @enum {Object} */ translations: {} }; /** * filter methods + * + * @name filter + * @class */ var filter = { /** - * Compress a message (deflate compression). Returns base64 encoded data. + * compress a message (deflate compression), returns base64 encoded data * - * @param string message - * @return base64 string data + * @name filter.compress + * @function + * @param {string} message + * @return {string} base64 data */ compress: function(message) { @@ -477,10 +534,12 @@ $(function() { }, /** - * Decompress a message compressed with compress(). + * decompress a message compressed with filter.compress() * - * @param base64 string data - * @return string message + * @name filter.decompress + * @function + * @param {string} base64 data + * @return {string} message */ decompress: function(data) { @@ -488,12 +547,14 @@ $(function() { }, /** - * Compress, then encrypt message with key. + * compress, then encrypt message with given key and password * - * @param string key - * @param string password - * @param string message - * @return encrypted string data + * @name filter.cipher + * @function + * @param {string} key + * @param {string} password + * @param {string} message + * @return {string} JSON with encrypted data */ cipher: function(key, password, message) { @@ -507,12 +568,14 @@ $(function() { }, /** - * Decrypt message with key, then decompress. + * decrypt message with key, then decompress * - * @param string key - * @param string password - * @param encrypted string data - * @return string readable message + * @name filter.decipher + * @function + * @param {string} key + * @param {string} password + * @param {string} JSON with encrypted data + * @return {string} decrypted message */ decipher: function(key, password, data) { @@ -536,27 +599,44 @@ $(function() { } }; + /** + * PrivateBin logic + * + * @name privatebin + * @class + */ var privatebin = { /** * headers to send in AJAX requests + * + * @name privatebin.headers + * @enum {Object} */ headers: {'X-Requested-With': 'JSONHttpRequest'}, /** * URL shortners create address + * + * @name privatebin.shortenerUrl + * @prop {string} */ shortenerUrl: '', /** * URL of newly created paste + * + * @name privatebin.createdPasteUrl + * @prop {string} */ createdPasteUrl: '', /** - * Get the current script location (without search or hash part of the URL). - * eg. http://server.com/zero/?aaaa#bbbb --> http://server.com/zero/ + * get the current script location (without search or hash part of the URL), + * eg. http://example.com/zero/?aaaa#bbbb --> http://example.com/zero/ * - * @return string current script location + * @name privatebin.scriptLocation + * @function + * @return {string} current script location */ scriptLocation: function() { @@ -571,10 +651,12 @@ $(function() { }, /** - * Get the pastes unique identifier from the URL - * eg. http://server.com/zero/?c05354954c49a487#c05354954c49a487 returns c05354954c49a487 + * get the pastes unique identifier from the URL, + * eg. http://example.com/zero/?c05354954c49a487#c05354954c49a487 returns c05354954c49a487 * - * @return string unique identifier + * @name privatebin.pasteID + * @function + * @return {string} unique identifier */ pasteID: function() { @@ -582,9 +664,11 @@ $(function() { }, /** - * Return the deciphering key stored in anchor part of the URL + * return the deciphering key stored in anchor part of the URL * - * @return string key + * @name privatebin.pageKey + * @function + * @return {string} key */ pageKey: function() { @@ -618,6 +702,9 @@ $(function() { /** * ask the user for the password and set it + * + * @name privatebin.requestPassword + * @function */ requestPassword: function() { @@ -642,8 +729,10 @@ $(function() { /** * use given format on paste, defaults to plain text * - * @param string format - * @param string text + * @name privatebin.formatPaste + * @function + * @param {string} format + * @param {string} text */ formatPaste: function(format, text) { @@ -683,7 +772,7 @@ $(function() { } // fall through, as the rest is the same default: - // Convert URLs to clickable links. + // convert URLs to clickable links helper.urls2links(this.clearText); helper.urls2links(this.prettyPrint); this.clearText.addClass('hidden'); @@ -698,9 +787,11 @@ $(function() { }, /** - * Show decrypted text in the display area, including discussion (if open) + * show decrypted text in the display area, including discussion (if open) * - * @param object paste (optional) object including comments to display (items = array with keys ('data','meta') + * @name privatebin.displayMessages + * @function + * @param {Object} [paste] - (optional) object including comments to display (items = array with keys ('data','meta')) */ displayMessages: function(paste) { @@ -779,7 +870,7 @@ $(function() { } } - // Display paste expiration / for your eyes only. + // display paste expiration / for your eyes only if (paste.meta.expire_date) { var expiration = helper.secondsToHuman(paste.meta.remaining_time), @@ -809,11 +900,11 @@ $(function() { )); this.remainingTime.addClass('foryoureyesonly') .removeClass('hidden'); - // Discourage cloning (as it can't really be prevented). + // discourage cloning (as it can't really be prevented) this.cloneButton.addClass('hidden'); } - // If the discussion is opened on this paste, display it. + // if the discussion is opened on this paste, display it if (paste.meta.opendiscussion) { this.comments.html(''); @@ -824,10 +915,10 @@ $(function() { var place = this.comments; var comment = paste.comments[i]; var commenttext = filter.decipher(key, password, comment.data); - // If parent comment exists, display below (CSS will automatically shift it right.) + // if parent comment exists, display below (CSS will automatically shift it to the right) var cname = '#comment_' + comment.parentid; - // If the element exists in page + // if the element exists in page if ($(cname).length) { place = $(cname); @@ -838,10 +929,9 @@ $(function() { + ''); divComment.find('button').click({commentid: comment.id}, $.proxy(this.openReply, this)); helper.setElementText(divComment.find('div.commentdata'), commenttext); - // Convert URLs to clickable links in comment. helper.urls2links(divComment.find('div.commentdata')); - // Try to get optional nickname: + // try to get optional nickname var nick = filter.decipher(key, password, comment.meta.nickname); if (nick.length > 0) { @@ -855,7 +945,7 @@ $(function() { .text(' (' + (new Date(comment.meta.postdate * 1000).toLocaleString()) + ')') .attr('title', 'CommentID: ' + comment.id); - // If an avatar is available, display it. + // if an avatar is available, display it if (comment.meta.vizhash) { divComment.find('span.nickname') @@ -878,9 +968,11 @@ $(function() { }, /** - * Open the comment entry when clicking the "Reply" button of a comment. + * open the comment entry when clicking the "Reply" button of a comment * - * @param Event event + * @name privatebin.openReply + * @function + * @param {Event} event */ openReply: function(event) { @@ -889,7 +981,7 @@ $(function() { commentid = event.data.commentid, hint = i18n._('Optional nickname...'); - // Remove any other reply area. + // remove any other reply area $('div.reply').remove(); var reply = $( '
' + @@ -905,15 +997,17 @@ $(function() { }, /** - * Send a reply in a discussion. + * send a reply in a discussion * - * @param Event event + * @name privatebin.sendComment + * @function + * @param {Event} event */ sendComment: function(event) { event.preventDefault(); this.errorMessage.addClass('hidden'); - // Do not send if no data. + // do not send if no data var replyMessage = $('#replymessage'); if (replyMessage.val().length === 0) { @@ -946,7 +1040,7 @@ $(function() { { if (data.status === 0) { - privatebin.showStatus(i18n._('Comment posted.'), false); + privatebin.showStatus(i18n._('Comment posted.')); $.ajax({ type: 'GET', url: privatebin.scriptLocation() + '?' + privatebin.pasteID(), @@ -988,9 +1082,11 @@ $(function() { }, /** - * Send a new paste to server + * send a new paste to server * - * @param Event event + * @name privatebin.sendData + * @function + * @param {Event} event */ sendData: function(event) { @@ -998,13 +1094,13 @@ $(function() { var file = document.getElementById('file'), files = (file && file.files) ? file.files : null; // FileList object - // Do not send if no data. + // do not send if no data. if (this.message.val().length === 0 && !(files && files[0])) { return; } - // If sjcl has not collected enough entropy yet, display a message. + // if sjcl has not collected enough entropy yet, display a message if (!sjcl.random.isReady()) { this.showStatus(i18n._('Sending paste (Please move your mouse for more entropy)...'), true); @@ -1028,7 +1124,7 @@ $(function() { return; } var reader = new FileReader(); - // Closure to capture the file information. + // closure to capture the file information reader.onload = (function(theFile) { return function(e) { @@ -1056,11 +1152,13 @@ $(function() { }, /** - * Send a new paste to server, step 2 + * send a new paste to server, step 2 * - * @param string randomkey - * @param encrypted string cipherdata_attachment - * @param encrypted string cipherdata_attachment_name + * @name privatebin.sendDataContinue + * @function + * @param {string} randomkey + * @param {string} cipherdata_attachment + * @param {string} cipherdata_attachment_name */ sendDataContinue: function(randomkey, cipherdata_attachment, cipherdata_attachment_name) { @@ -1092,7 +1190,7 @@ $(function() { privatebin.stateExistingPaste(); var url = privatebin.scriptLocation() + '?' + data.id + '#' + randomkey; var deleteUrl = privatebin.scriptLocation() + '?pasteid=' + data.id + '&deletetoken=' + data.deletetoken; - privatebin.showStatus('', false); + privatebin.showStatus(''); privatebin.errorMessage.addClass('hidden'); $('#pastelink').html( @@ -1107,9 +1205,9 @@ $(function() { } $('#deletelink').html('' + i18n._('Delete data') + ''); privatebin.pasteResult.removeClass('hidden'); - // We pre-select the link so that the user only has to [Ctrl]+[c] the link. + // we pre-select the link so that the user only has to [Ctrl]+[c] the link helper.selectText('pasteurl'); - privatebin.showStatus('', false); + privatebin.showStatus(''); privatebin.formatPaste(data_to_send.formatter, privatebin.message.val()); } else if (data.status === 1) @@ -1129,10 +1227,12 @@ $(function() { }, /** - * Check if a URL shortener was defined and create HTML containing a link to it. + * check if a URL shortener was defined and create HTML containing a link to it * - * @param string url - * @return string html + * @name privatebin.shortenUrl + * @function + * @param {string} url + * @return {string} html */ shortenUrl: function(url) { @@ -1146,7 +1246,10 @@ $(function() { }, /** - * Put the screen in "New paste" mode. + * put the screen in "New paste" mode + * + * @name privatebin.stateNewPaste + * @function */ stateNewPaste: function() { @@ -1173,9 +1276,11 @@ $(function() { }, /** - * Put the screen in "Existing paste" mode. + * put the screen in "Existing paste" mode * - * @param boolean preview (optional) tell if the preview tabs should be displayed, defaults to false. + * @name privatebin.stateExistingPaste + * @function + * @param {boolean} [preview=false] - (optional) tell if the preview tabs should be displayed, defaults to false */ stateExistingPaste: function(preview) { @@ -1183,7 +1288,7 @@ $(function() { if (!preview) { - // No "clone" for IE<10. + // no "clone" for IE<10. if ($('#oldienotice').is(":visible")) { this.cloneButton.addClass('hidden'); @@ -1211,7 +1316,10 @@ $(function() { }, /** - * If "burn after reading" is checked, disable discussion. + * when "burn after reading" is checked, disable discussion + * + * @name privatebin.changeBurnAfterReading + * @function */ changeBurnAfterReading: function() { @@ -1228,7 +1336,10 @@ $(function() { }, /** - * If discussion is checked, disable "burn after reading". + * when discussion is checked, disable "burn after reading" + * + * @name privatebin.changeOpenDisc + * @function */ changeOpenDisc: function() { @@ -1245,9 +1356,11 @@ $(function() { }, /** - * Forward to URL shortener. + * forward to URL shortener * - * @param Event event + * @name privatebin.sendToShortener + * @function + * @param {Event} event */ sendToShortener: function(event) { @@ -1256,9 +1369,11 @@ $(function() { }, /** - * Reload the page. + * reload the page * - * @param Event event + * @name privatebin.reloadPage + * @function + * @param {Event} event */ reloadPage: function(event) { @@ -1267,9 +1382,11 @@ $(function() { }, /** - * Return raw text. + * return raw text * - * @param Event event + * @name privatebin.rawText + * @function + * @param {Event} event */ rawText: function(event) { @@ -1288,19 +1405,21 @@ $(function() { }, /** - * Clone the current paste. + * clone the current paste * - * @param Event event + * @name privatebin.clonePaste + * @function + * @param {Event} event */ clonePaste: function(event) { event.preventDefault(); this.stateNewPaste(); - // Erase the id and the key in url + // erase the id and the key in url history.replaceState(null, document.title, this.scriptLocation()); - this.showStatus('', false); + this.showStatus(''); if (this.attachmentLink.attr('href')) { this.clonedFile.removeClass('hidden'); @@ -1314,9 +1433,11 @@ $(function() { }, /** - * Set the expiration on bootstrap templates. + * set the expiration on bootstrap templates * - * @param Event event + * @name privatebin.setExpiration + * @function + * @param {Event} event */ setExpiration: function(event) { @@ -1327,9 +1448,11 @@ $(function() { }, /** - * Set the format on bootstrap templates. + * set the format on bootstrap templates * - * @param Event event + * @name privatebin.setFormat + * @function + * @param {Event} event */ setFormat: function(event) { @@ -1344,11 +1467,11 @@ $(function() { }, /** - * Set the language on bootstrap templates. + * set the language in a cookie and reload the page * - * Sets the language cookie and reloads the page. - * - * @param Event event + * @name privatebin.setLanguage + * @function + * @param {Event} event */ setLanguage: function(event) { @@ -1357,9 +1480,11 @@ $(function() { }, /** - * Support input of tab character. + * support input of tab character * - * @param Event event + * @name privatebin.supportTabs + * @function + * @param {Event} event */ supportTabs: function(event) { @@ -1381,9 +1506,11 @@ $(function() { }, /** - * View the editor tab. + * view the editor tab * - * @param Event event + * @name privatebin.viewEditor + * @function + * @param {Event} event */ viewEditor: function(event) { @@ -1395,9 +1522,11 @@ $(function() { }, /** - * View the preview tab. + * view the preview tab * - * @param Event event + * @name privatebin.viewPreview + * @function + * @param {Event} event */ viewPreview: function(event) { @@ -1410,19 +1539,25 @@ $(function() { }, /** - * Create a new paste. + * create a new paste + * + * @name privatebin.newPaste + * @function */ newPaste: function() { this.stateNewPaste(); - this.showStatus('', false); + this.showStatus(''); this.message.text(''); this.changeBurnAfterReading(); this.changeOpenDisc(); }, /** - * Removes an attachment. + * removes an attachment + * + * @name privatebin.removeAttachment + * @function */ removeAttachment: function() { @@ -1435,7 +1570,10 @@ $(function() { }, /** - * Focus on the modal password dialog. + * focus on the modal password dialog + * + * @name privatebin.focusPasswordModal + * @function */ focusPasswordModal: function() { @@ -1443,7 +1581,10 @@ $(function() { }, /** - * Decrypt using the password from the modal dialog. + * decrypt using the password from the modal dialog + * + * @name privatebin.decryptPasswordModal + * @function */ decryptPasswordModal: function() { @@ -1452,9 +1593,11 @@ $(function() { }, /** - * Submit a password in the modal dialog. + * submit a password in the modal dialog * - * @param Event event + * @name privatebin.submitPasswordModal + * @function + * @param {Event} event */ submitPasswordModal: function(event) { @@ -1463,10 +1606,12 @@ $(function() { }, /** - * Display an error message - * (We use the same function for paste and reply to comments) + * display an error message, + * we use the same function for paste and reply to comments * - * @param string message : text to display + * @name privatebin.showError + * @function + * @param {string} message - text to display */ showError: function(message) { @@ -1494,14 +1639,24 @@ $(function() { }, /** - * Display a status message - * (We use the same function for paste and reply to comments) + * display a status message, + * we use the same function for paste and reply to comments * - * @param string message : text to display - * @param boolean spin (optional) : tell if the "spinning" animation should be displayed. + * @name privatebin.showStatus + * @function + * @param {string} message - text to display + * @param {boolean} [spin=false] - (optional) tell if the "spinning" animation should be displayed, defaults to false */ showStatus: function(message, spin) { + if (spin || false) + { + var img = ''; + this.status.prepend(img); + if (typeof this.replyStatus !== 'undefined') { + this.replyStatus.prepend(img); + } + } if (typeof this.replyStatus !== 'undefined') { this.replyStatus.removeClass('errorMessage').text(message); } @@ -1516,18 +1671,13 @@ $(function() { return; } this.status.removeClass('errorMessage').text(message); - if (spin) - { - var img = ''; - this.status.prepend(img); - if (typeof this.replyStatus !== 'undefined') { - this.replyStatus.prepend(img); - } - } }, /** * bind events to DOM elements + * + * @name privatebin.bindEvents + * @function */ bindEvents: function() { @@ -1558,6 +1708,9 @@ $(function() { /** * main application + * + * @name privatebin.init + * @function */ init: function() { @@ -1603,36 +1756,36 @@ $(function() { this.status = $('#status'); this.bindEvents(); - // Display status returned by php code if any (eg. Paste was properly deleted.) + // display status returned by php code, if any (eg. paste was properly deleted) if (this.status.text().length > 0) { - this.showStatus(this.status.text(), false); + this.showStatus(this.status.text()); return; } - // Keep line height even if content empty. + // keep line height even if content empty this.status.html(' '); - // Display an existing paste + // display an existing paste if (this.cipherData.text().length > 1) { - // Missing decryption key in URL? + // missing decryption key in URL? if (window.location.hash.length === 0) { this.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?)')); return; } - // Show proper elements on screen. + // show proper elements on screen this.stateExistingPaste(); this.displayMessages(); } - // Display error message from php code. + // display error message from php code else if (this.errorMessage.text().length > 1) { this.showError(this.errorMessage.text()); } - // Create a new paste. + // create a new paste else { this.newPaste(); diff --git a/tpl/bootstrap.php b/tpl/bootstrap.php index aa91756c..2c61604e 100644 --- a/tpl/bootstrap.php +++ b/tpl/bootstrap.php @@ -69,7 +69,7 @@ if ($MARKDOWN): - + diff --git a/tpl/page.php b/tpl/page.php index 484b299e..91db3230 100644 --- a/tpl/page.php +++ b/tpl/page.php @@ -47,7 +47,7 @@ if ($MARKDOWN): - + From 151005e6b80cdd225fe969e5a3cd71c1d118a359 Mon Sep 17 00:00:00 2001 From: El RIDO Date: Sat, 14 Jan 2017 15:32:14 +0100 Subject: [PATCH 0008/1638] imported changes from the wiki by @Based-Skid and @rugk --- INSTALL.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/INSTALL.md b/INSTALL.md index f175fdf6..5dbc5095 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -115,7 +115,8 @@ The table prefix option is called `tbl`. > know. For reference or if you want to create the table schema for yourself (replace -`prefix_` with your own table prefix): +`prefix_` with your own table prefix and create the table schema with phpMyAdmin +or the MYSQL console): CREATE TABLE prefix_paste ( dataid CHAR(16) NOT NULL, From 5f09264625a1e46db3e6f17d2d10011e17e57557 Mon Sep 17 00:00:00 2001 From: El RIDO Date: Sat, 14 Jan 2017 16:13:22 +0100 Subject: [PATCH 0009/1638] fixing documentation inconsitencies found by Scrutinizer CI --- js/privatebin.js | 37 +++++++++++++------------------------ tpl/bootstrap.php | 2 +- tpl/page.php | 2 +- 3 files changed, 15 insertions(+), 26 deletions(-) diff --git a/js/privatebin.js b/js/privatebin.js index c79c8174..7e4de064 100644 --- a/js/privatebin.js +++ b/js/privatebin.js @@ -75,23 +75,23 @@ $(function() { * * @name helper.hashToParameterString * @function - * @param {Object} associative_array - Object to be serialized + * @param {Object} hashMap - Object to be serialized * @return {string} */ - hashToParameterString: function(associativeArray) + hashToParameterString: function(hashMap) { var parameterString = ''; - for (var key in associativeArray) + for (var key in hashMap) { if(parameterString === '') { parameterString = encodeURIComponent(key); - parameterString += '=' + encodeURIComponent(associativeArray[key]); + parameterString += '=' + encodeURIComponent(hashMap[key]); } else { parameterString += '&' + encodeURIComponent(key); - parameterString += '=' + encodeURIComponent(associativeArray[key]); + parameterString += '=' + encodeURIComponent(hashMap[key]); } } // padding for URL shorteners @@ -105,8 +105,8 @@ $(function() { * * @name helper.parameterStringToHash * @function - * @param {string} parameter_string - String containing parameters - * @return {Object} + * @param {string} parameterString - String containing parameters + * @return {Object} hash map */ parameterStringToHash: function(parameterString) { @@ -267,9 +267,9 @@ $(function() { { args = arguments[0]; } - var string = args[0], + var format = args[0], i = 1; - return string.replace(/%((%)|s|d)/g, function (m) { + return format.replace(/%((%)|s|d)/g, function (m) { // m is the matched format, e.g. %s, %d var val; if (m[2]) { @@ -538,7 +538,7 @@ $(function() { * * @name filter.decompress * @function - * @param {string} base64 data + * @param {string} data - base64 data * @return {string} message */ decompress: function(data) @@ -554,7 +554,7 @@ $(function() { * @param {string} key * @param {string} password * @param {string} message - * @return {string} JSON with encrypted data + * @return {string} data - JSON with encrypted data */ cipher: function(key, password, message) { @@ -574,7 +574,7 @@ $(function() { * @function * @param {string} key * @param {string} password - * @param {string} JSON with encrypted data + * @param {string} data - JSON with encrypted data * @return {string} decrypted message */ decipher: function(key, password, data) @@ -1569,17 +1569,6 @@ $(function() { this.fileWrap.removeClass('hidden'); }, - /** - * focus on the modal password dialog - * - * @name privatebin.focusPasswordModal - * @function - */ - focusPasswordModal: function() - { - this.passwordDecrypt.focus(); - }, - /** * decrypt using the password from the modal dialog * @@ -1701,7 +1690,7 @@ $(function() { $('#language select option').click($.proxy(this.setLanguage, this)); // handle modal password request on decryption - this.passwordModal.on('shown.bs.modal', $.proxy(this.focusPasswordModal, this)); + this.passwordModal.on('shown.bs.modal', $.proxy(this.passwordDecrypt.focus, this)); this.passwordModal.on('hidden.bs.modal', $.proxy(this.decryptPasswordModal, this)); this.passwordForm.submit($.proxy(this.submitPasswordModal, this)); }, diff --git a/tpl/bootstrap.php b/tpl/bootstrap.php index 2c61604e..04f03668 100644 --- a/tpl/bootstrap.php +++ b/tpl/bootstrap.php @@ -69,7 +69,7 @@ if ($MARKDOWN): - + diff --git a/tpl/page.php b/tpl/page.php index 91db3230..bc18c41c 100644 --- a/tpl/page.php +++ b/tpl/page.php @@ -47,7 +47,7 @@ if ($MARKDOWN): - + From 8029c2819f67572cf1c4adaf0e71861f59dd9065 Mon Sep 17 00:00:00 2001 From: El RIDO Date: Sun, 22 Jan 2017 10:42:11 +0100 Subject: [PATCH 0010/1638] implementing JS module pattern to expose functions for unit testing --- js/privatebin.js | 22 +++++++++++++++------- tpl/bootstrap.php | 2 +- tpl/page.php | 2 +- 3 files changed, 17 insertions(+), 9 deletions(-) diff --git a/js/privatebin.js b/js/privatebin.js index 7e4de064..59417512 100644 --- a/js/privatebin.js +++ b/js/privatebin.js @@ -25,7 +25,7 @@ // Immediately start random number generator collector. sjcl.random.startCollectors(); -$(function() { +jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { /** * static helper methods * @@ -1782,9 +1782,17 @@ $(function() { } } - /** - * main application start, called when DOM is fully loaded - * runs privatebin when translations were loaded - */ - i18n.loadTranslations($.proxy(privatebin.init, privatebin)); -}); + return { + helper: helper, + i18n: i18n, + filter: filter, + privatebin: privatebin + }; +}(jQuery, sjcl, Base64, RawDeflate); + +/** + * main application start, called when DOM is fully loaded + * runs privatebin when translations were loaded + */ +jQuery(jQuery.PrivateBin.i18n.loadTranslations(jQuery.proxy(jQuery.PrivateBin.privatebin.init, jQuery.PrivateBin.privatebin))); + diff --git a/tpl/bootstrap.php b/tpl/bootstrap.php index 04f03668..3d51379c 100644 --- a/tpl/bootstrap.php +++ b/tpl/bootstrap.php @@ -69,7 +69,7 @@ if ($MARKDOWN): - + diff --git a/tpl/page.php b/tpl/page.php index bc18c41c..40c3e124 100644 --- a/tpl/page.php +++ b/tpl/page.php @@ -47,7 +47,7 @@ if ($MARKDOWN): - + From c5f7c1a3c925a7c83cf1578240143c217de361b7 Mon Sep 17 00:00:00 2001 From: Nathaniel Olsen Date: Mon, 23 Jan 2017 22:24:24 -0600 Subject: [PATCH 0011/1638] Progress on updating bootstrap --- js/bootstrap-3.3.5.js | 7 ------- js/bootstrap.3.3.7.js | 7 +++++++ 2 files changed, 7 insertions(+), 7 deletions(-) delete mode 100644 js/bootstrap-3.3.5.js create mode 100644 js/bootstrap.3.3.7.js diff --git a/js/bootstrap-3.3.5.js b/js/bootstrap-3.3.5.js deleted file mode 100644 index e7ac43c6..00000000 --- a/js/bootstrap-3.3.5.js +++ /dev/null @@ -1,7 +0,0 @@ -/*! - * Bootstrap v3.3.5 (https://getbootstrap.com) - * Copyright 2011-2015 Twitter, Inc. - * Licensed under the MIT license - */ -if("undefined"==typeof jQuery)throw new Error("Bootstrap's JavaScript requires jQuery");+function(a){"use strict";var b=a.fn.jquery.split(" ")[0].split(".");if(b[0]<2&&b[1]<9||1==b[0]&&9==b[1]&&b[2]<1)throw new Error("Bootstrap's JavaScript requires jQuery version 1.9.1 or higher")}(jQuery),+function(a){"use strict";function b(){var a=document.createElement("bootstrap"),b={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"};for(var c in b)if(void 0!==a.style[c])return{end:b[c]};return!1}a.fn.emulateTransitionEnd=function(b){var c=!1,d=this;a(this).one("bsTransitionEnd",function(){c=!0});var e=function(){c||a(d).trigger(a.support.transition.end)};return setTimeout(e,b),this},a(function(){a.support.transition=b(),a.support.transition&&(a.event.special.bsTransitionEnd={bindType:a.support.transition.end,delegateType:a.support.transition.end,handle:function(b){return a(b.target).is(this)?b.handleObj.handler.apply(this,arguments):void 0}})})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var c=a(this),e=c.data("bs.alert");e||c.data("bs.alert",e=new d(this)),"string"==typeof b&&e[b].call(c)})}var c='[data-dismiss="alert"]',d=function(b){a(b).on("click",c,this.close)};d.VERSION="3.3.5",d.TRANSITION_DURATION=150,d.prototype.close=function(b){function c(){g.detach().trigger("closed.bs.alert").remove()}var e=a(this),f=e.attr("data-target");f||(f=e.attr("href"),f=f&&f.replace(/.*(?=#[^\s]*$)/,""));var g=a(f);b&&b.preventDefault(),g.length||(g=e.closest(".alert")),g.trigger(b=a.Event("close.bs.alert")),b.isDefaultPrevented()||(g.removeClass("in"),a.support.transition&&g.hasClass("fade")?g.one("bsTransitionEnd",c).emulateTransitionEnd(d.TRANSITION_DURATION):c())};var e=a.fn.alert;a.fn.alert=b,a.fn.alert.Constructor=d,a.fn.alert.noConflict=function(){return a.fn.alert=e,this},a(document).on("click.bs.alert.data-api",c,d.prototype.close)}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.button"),f="object"==typeof b&&b;e||d.data("bs.button",e=new c(this,f)),"toggle"==b?e.toggle():b&&e.setState(b)})}var c=function(b,d){this.$element=a(b),this.options=a.extend({},c.DEFAULTS,d),this.isLoading=!1};c.VERSION="3.3.5",c.DEFAULTS={loadingText:"loading..."},c.prototype.setState=function(b){var c="disabled",d=this.$element,e=d.is("input")?"val":"html",f=d.data();b+="Text",null==f.resetText&&d.data("resetText",d[e]()),setTimeout(a.proxy(function(){d[e](null==f[b]?this.options[b]:f[b]),"loadingText"==b?(this.isLoading=!0,d.addClass(c).attr(c,c)):this.isLoading&&(this.isLoading=!1,d.removeClass(c).removeAttr(c))},this),0)},c.prototype.toggle=function(){var a=!0,b=this.$element.closest('[data-toggle="buttons"]');if(b.length){var c=this.$element.find("input");"radio"==c.prop("type")?(c.prop("checked")&&(a=!1),b.find(".active").removeClass("active"),this.$element.addClass("active")):"checkbox"==c.prop("type")&&(c.prop("checked")!==this.$element.hasClass("active")&&(a=!1),this.$element.toggleClass("active")),c.prop("checked",this.$element.hasClass("active")),a&&c.trigger("change")}else this.$element.attr("aria-pressed",!this.$element.hasClass("active")),this.$element.toggleClass("active")};var d=a.fn.button;a.fn.button=b,a.fn.button.Constructor=c,a.fn.button.noConflict=function(){return a.fn.button=d,this},a(document).on("click.bs.button.data-api",'[data-toggle^="button"]',function(c){var d=a(c.target);d.hasClass("btn")||(d=d.closest(".btn")),b.call(d,"toggle"),a(c.target).is('input[type="radio"]')||a(c.target).is('input[type="checkbox"]')||c.preventDefault()}).on("focus.bs.button.data-api blur.bs.button.data-api",'[data-toggle^="button"]',function(b){a(b.target).closest(".btn").toggleClass("focus",/^focus(in)?$/.test(b.type))})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.carousel"),f=a.extend({},c.DEFAULTS,d.data(),"object"==typeof b&&b),g="string"==typeof b?b:f.slide;e||d.data("bs.carousel",e=new c(this,f)),"number"==typeof b?e.to(b):g?e[g]():f.interval&&e.pause().cycle()})}var c=function(b,c){this.$element=a(b),this.$indicators=this.$element.find(".carousel-indicators"),this.options=c,this.paused=null,this.sliding=null,this.interval=null,this.$active=null,this.$items=null,this.options.keyboard&&this.$element.on("keydown.bs.carousel",a.proxy(this.keydown,this)),"hover"==this.options.pause&&!("ontouchstart"in document.documentElement)&&this.$element.on("mouseenter.bs.carousel",a.proxy(this.pause,this)).on("mouseleave.bs.carousel",a.proxy(this.cycle,this))};c.VERSION="3.3.5",c.TRANSITION_DURATION=600,c.DEFAULTS={interval:5e3,pause:"hover",wrap:!0,keyboard:!0},c.prototype.keydown=function(a){if(!/input|textarea/i.test(a.target.tagName)){switch(a.which){case 37:this.prev();break;case 39:this.next();break;default:return}a.preventDefault()}},c.prototype.cycle=function(b){return b||(this.paused=!1),this.interval&&clearInterval(this.interval),this.options.interval&&!this.paused&&(this.interval=setInterval(a.proxy(this.next,this),this.options.interval)),this},c.prototype.getItemIndex=function(a){return this.$items=a.parent().children(".item"),this.$items.index(a||this.$active)},c.prototype.getItemForDirection=function(a,b){var c=this.getItemIndex(b),d="prev"==a&&0===c||"next"==a&&c==this.$items.length-1;if(d&&!this.options.wrap)return b;var e="prev"==a?-1:1,f=(c+e)%this.$items.length;return this.$items.eq(f)},c.prototype.to=function(a){var b=this,c=this.getItemIndex(this.$active=this.$element.find(".item.active"));return a>this.$items.length-1||0>a?void 0:this.sliding?this.$element.one("slid.bs.carousel",function(){b.to(a)}):c==a?this.pause().cycle():this.slide(a>c?"next":"prev",this.$items.eq(a))},c.prototype.pause=function(b){return b||(this.paused=!0),this.$element.find(".next, .prev").length&&a.support.transition&&(this.$element.trigger(a.support.transition.end),this.cycle(!0)),this.interval=clearInterval(this.interval),this},c.prototype.next=function(){return this.sliding?void 0:this.slide("next")},c.prototype.prev=function(){return this.sliding?void 0:this.slide("prev")},c.prototype.slide=function(b,d){var e=this.$element.find(".item.active"),f=d||this.getItemForDirection(b,e),g=this.interval,h="next"==b?"left":"right",i=this;if(f.hasClass("active"))return this.sliding=!1;var j=f[0],k=a.Event("slide.bs.carousel",{relatedTarget:j,direction:h});if(this.$element.trigger(k),!k.isDefaultPrevented()){if(this.sliding=!0,g&&this.pause(),this.$indicators.length){this.$indicators.find(".active").removeClass("active");var l=a(this.$indicators.children()[this.getItemIndex(f)]);l&&l.addClass("active")}var m=a.Event("slid.bs.carousel",{relatedTarget:j,direction:h});return a.support.transition&&this.$element.hasClass("slide")?(f.addClass(b),f[0].offsetWidth,e.addClass(h),f.addClass(h),e.one("bsTransitionEnd",function(){f.removeClass([b,h].join(" ")).addClass("active"),e.removeClass(["active",h].join(" ")),i.sliding=!1,setTimeout(function(){i.$element.trigger(m)},0)}).emulateTransitionEnd(c.TRANSITION_DURATION)):(e.removeClass("active"),f.addClass("active"),this.sliding=!1,this.$element.trigger(m)),g&&this.cycle(),this}};var d=a.fn.carousel;a.fn.carousel=b,a.fn.carousel.Constructor=c,a.fn.carousel.noConflict=function(){return a.fn.carousel=d,this};var e=function(c){var d,e=a(this),f=a(e.attr("data-target")||(d=e.attr("href"))&&d.replace(/.*(?=#[^\s]+$)/,""));if(f.hasClass("carousel")){var g=a.extend({},f.data(),e.data()),h=e.attr("data-slide-to");h&&(g.interval=!1),b.call(f,g),h&&f.data("bs.carousel").to(h),c.preventDefault()}};a(document).on("click.bs.carousel.data-api","[data-slide]",e).on("click.bs.carousel.data-api","[data-slide-to]",e),a(window).on("load",function(){a('[data-ride="carousel"]').each(function(){var c=a(this);b.call(c,c.data())})})}(jQuery),+function(a){"use strict";function b(b){var c,d=b.attr("data-target")||(c=b.attr("href"))&&c.replace(/.*(?=#[^\s]+$)/,"");return a(d)}function c(b){return this.each(function(){var c=a(this),e=c.data("bs.collapse"),f=a.extend({},d.DEFAULTS,c.data(),"object"==typeof b&&b);!e&&f.toggle&&/show|hide/.test(b)&&(f.toggle=!1),e||c.data("bs.collapse",e=new d(this,f)),"string"==typeof b&&e[b]()})}var d=function(b,c){this.$element=a(b),this.options=a.extend({},d.DEFAULTS,c),this.$trigger=a('[data-toggle="collapse"][href="#'+b.id+'"],[data-toggle="collapse"][data-target="#'+b.id+'"]'),this.transitioning=null,this.options.parent?this.$parent=this.getParent():this.addAriaAndCollapsedClass(this.$element,this.$trigger),this.options.toggle&&this.toggle()};d.VERSION="3.3.5",d.TRANSITION_DURATION=350,d.DEFAULTS={toggle:!0},d.prototype.dimension=function(){var a=this.$element.hasClass("width");return a?"width":"height"},d.prototype.show=function(){if(!this.transitioning&&!this.$element.hasClass("in")){var b,e=this.$parent&&this.$parent.children(".panel").children(".in, .collapsing");if(!(e&&e.length&&(b=e.data("bs.collapse"),b&&b.transitioning))){var f=a.Event("show.bs.collapse");if(this.$element.trigger(f),!f.isDefaultPrevented()){e&&e.length&&(c.call(e,"hide"),b||e.data("bs.collapse",null));var g=this.dimension();this.$element.removeClass("collapse").addClass("collapsing")[g](0).attr("aria-expanded",!0),this.$trigger.removeClass("collapsed").attr("aria-expanded",!0),this.transitioning=1;var h=function(){this.$element.removeClass("collapsing").addClass("collapse in")[g](""),this.transitioning=0,this.$element.trigger("shown.bs.collapse")};if(!a.support.transition)return h.call(this);var i=a.camelCase(["scroll",g].join("-"));this.$element.one("bsTransitionEnd",a.proxy(h,this)).emulateTransitionEnd(d.TRANSITION_DURATION)[g](this.$element[0][i])}}}},d.prototype.hide=function(){if(!this.transitioning&&this.$element.hasClass("in")){var b=a.Event("hide.bs.collapse");if(this.$element.trigger(b),!b.isDefaultPrevented()){var c=this.dimension();this.$element[c](this.$element[c]())[0].offsetHeight,this.$element.addClass("collapsing").removeClass("collapse in").attr("aria-expanded",!1),this.$trigger.addClass("collapsed").attr("aria-expanded",!1),this.transitioning=1;var e=function(){this.transitioning=0,this.$element.removeClass("collapsing").addClass("collapse").trigger("hidden.bs.collapse")};return a.support.transition?void this.$element[c](0).one("bsTransitionEnd",a.proxy(e,this)).emulateTransitionEnd(d.TRANSITION_DURATION):e.call(this)}}},d.prototype.toggle=function(){this[this.$element.hasClass("in")?"hide":"show"]()},d.prototype.getParent=function(){return a(this.options.parent).find('[data-toggle="collapse"][data-parent="'+this.options.parent+'"]').each(a.proxy(function(c,d){var e=a(d);this.addAriaAndCollapsedClass(b(e),e)},this)).end()},d.prototype.addAriaAndCollapsedClass=function(a,b){var c=a.hasClass("in");a.attr("aria-expanded",c),b.toggleClass("collapsed",!c).attr("aria-expanded",c)};var e=a.fn.collapse;a.fn.collapse=c,a.fn.collapse.Constructor=d,a.fn.collapse.noConflict=function(){return a.fn.collapse=e,this},a(document).on("click.bs.collapse.data-api",'[data-toggle="collapse"]',function(d){var e=a(this);e.attr("data-target")||d.preventDefault();var f=b(e),g=f.data("bs.collapse"),h=g?"toggle":e.data();c.call(f,h)})}(jQuery),+function(a){"use strict";function b(b){var c=b.attr("data-target");c||(c=b.attr("href"),c=c&&/#[A-Za-z]/.test(c)&&c.replace(/.*(?=#[^\s]*$)/,""));var d=c&&a(c);return d&&d.length?d:b.parent()}function c(c){c&&3===c.which||(a(e).remove(),a(f).each(function(){var d=a(this),e=b(d),f={relatedTarget:this};e.hasClass("open")&&(c&&"click"==c.type&&/input|textarea/i.test(c.target.tagName)&&a.contains(e[0],c.target)||(e.trigger(c=a.Event("hide.bs.dropdown",f)),c.isDefaultPrevented()||(d.attr("aria-expanded","false"),e.removeClass("open").trigger("hidden.bs.dropdown",f))))}))}function d(b){return this.each(function(){var c=a(this),d=c.data("bs.dropdown");d||c.data("bs.dropdown",d=new g(this)),"string"==typeof b&&d[b].call(c)})}var e=".dropdown-backdrop",f='[data-toggle="dropdown"]',g=function(b){a(b).on("click.bs.dropdown",this.toggle)};g.VERSION="3.3.5",g.prototype.toggle=function(d){var e=a(this);if(!e.is(".disabled, :disabled")){var f=b(e),g=f.hasClass("open");if(c(),!g){"ontouchstart"in document.documentElement&&!f.closest(".navbar-nav").length&&a(document.createElement("div")).addClass("dropdown-backdrop").insertAfter(a(this)).on("click",c);var h={relatedTarget:this};if(f.trigger(d=a.Event("show.bs.dropdown",h)),d.isDefaultPrevented())return;e.trigger("focus").attr("aria-expanded","true"),f.toggleClass("open").trigger("shown.bs.dropdown",h)}return!1}},g.prototype.keydown=function(c){if(/(38|40|27|32)/.test(c.which)&&!/input|textarea/i.test(c.target.tagName)){var d=a(this);if(c.preventDefault(),c.stopPropagation(),!d.is(".disabled, :disabled")){var e=b(d),g=e.hasClass("open");if(!g&&27!=c.which||g&&27==c.which)return 27==c.which&&e.find(f).trigger("focus"),d.trigger("click");var h=" li:not(.disabled):visible a",i=e.find(".dropdown-menu"+h);if(i.length){var j=i.index(c.target);38==c.which&&j>0&&j--,40==c.which&&jdocument.documentElement.clientHeight;this.$element.css({paddingLeft:!this.bodyIsOverflowing&&a?this.scrollbarWidth:"",paddingRight:this.bodyIsOverflowing&&!a?this.scrollbarWidth:""})},c.prototype.resetAdjustments=function(){this.$element.css({paddingLeft:"",paddingRight:""})},c.prototype.checkScrollbar=function(){var a=window.innerWidth;if(!a){var b=document.documentElement.getBoundingClientRect();a=b.right-Math.abs(b.left)}this.bodyIsOverflowing=document.body.clientWidth
',trigger:"hover focus",title:"",delay:0,html:!1,container:!1,viewport:{selector:"body",padding:0}},c.prototype.init=function(b,c,d){if(this.enabled=!0,this.type=b,this.$element=a(c),this.options=this.getOptions(d),this.$viewport=this.options.viewport&&a(a.isFunction(this.options.viewport)?this.options.viewport.call(this,this.$element):this.options.viewport.selector||this.options.viewport),this.inState={click:!1,hover:!1,focus:!1},this.$element[0]instanceof document.constructor&&!this.options.selector)throw new Error("`selector` option must be specified when initializing "+this.type+" on the window.document object!");for(var e=this.options.trigger.split(" "),f=e.length;f--;){var g=e[f];if("click"==g)this.$element.on("click."+this.type,this.options.selector,a.proxy(this.toggle,this));else if("manual"!=g){var h="hover"==g?"mouseenter":"focusin",i="hover"==g?"mouseleave":"focusout";this.$element.on(h+"."+this.type,this.options.selector,a.proxy(this.enter,this)),this.$element.on(i+"."+this.type,this.options.selector,a.proxy(this.leave,this))}}this.options.selector?this._options=a.extend({},this.options,{trigger:"manual",selector:""}):this.fixTitle()},c.prototype.getDefaults=function(){return c.DEFAULTS},c.prototype.getOptions=function(b){return b=a.extend({},this.getDefaults(),this.$element.data(),b),b.delay&&"number"==typeof b.delay&&(b.delay={show:b.delay,hide:b.delay}),b},c.prototype.getDelegateOptions=function(){var b={},c=this.getDefaults();return this._options&&a.each(this._options,function(a,d){c[a]!=d&&(b[a]=d)}),b},c.prototype.enter=function(b){var c=b instanceof this.constructor?b:a(b.currentTarget).data("bs."+this.type);return c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c)),b instanceof a.Event&&(c.inState["focusin"==b.type?"focus":"hover"]=!0),c.tip().hasClass("in")||"in"==c.hoverState?void(c.hoverState="in"):(clearTimeout(c.timeout),c.hoverState="in",c.options.delay&&c.options.delay.show?void(c.timeout=setTimeout(function(){"in"==c.hoverState&&c.show()},c.options.delay.show)):c.show())},c.prototype.isInStateTrue=function(){for(var a in this.inState)if(this.inState[a])return!0;return!1},c.prototype.leave=function(b){var c=b instanceof this.constructor?b:a(b.currentTarget).data("bs."+this.type);return c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c)),b instanceof a.Event&&(c.inState["focusout"==b.type?"focus":"hover"]=!1),c.isInStateTrue()?void 0:(clearTimeout(c.timeout),c.hoverState="out",c.options.delay&&c.options.delay.hide?void(c.timeout=setTimeout(function(){"out"==c.hoverState&&c.hide()},c.options.delay.hide)):c.hide())},c.prototype.show=function(){var b=a.Event("show.bs."+this.type);if(this.hasContent()&&this.enabled){this.$element.trigger(b);var d=a.contains(this.$element[0].ownerDocument.documentElement,this.$element[0]);if(b.isDefaultPrevented()||!d)return;var e=this,f=this.tip(),g=this.getUID(this.type);this.setContent(),f.attr("id",g),this.$element.attr("aria-describedby",g),this.options.animation&&f.addClass("fade");var h="function"==typeof this.options.placement?this.options.placement.call(this,f[0],this.$element[0]):this.options.placement,i=/\s?auto?\s?/i,j=i.test(h);j&&(h=h.replace(i,"")||"top"),f.detach().css({top:0,left:0,display:"block"}).addClass(h).data("bs."+this.type,this),this.options.container?f.appendTo(this.options.container):f.insertAfter(this.$element),this.$element.trigger("inserted.bs."+this.type);var k=this.getPosition(),l=f[0].offsetWidth,m=f[0].offsetHeight;if(j){var n=h,o=this.getPosition(this.$viewport);h="bottom"==h&&k.bottom+m>o.bottom?"top":"top"==h&&k.top-mo.width?"left":"left"==h&&k.left-lg.top+g.height&&(e.top=g.top+g.height-i)}else{var j=b.left-f,k=b.left+f+c;jg.right&&(e.left=g.left+g.width-k)}return e},c.prototype.getTitle=function(){var a,b=this.$element,c=this.options;return a=b.attr("data-original-title")||("function"==typeof c.title?c.title.call(b[0]):c.title)},c.prototype.getUID=function(a){do a+=~~(1e6*Math.random());while(document.getElementById(a));return a},c.prototype.tip=function(){if(!this.$tip&&(this.$tip=a(this.options.template),1!=this.$tip.length))throw new Error(this.type+" `template` option must consist of exactly 1 top-level element!");return this.$tip},c.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".tooltip-arrow")},c.prototype.enable=function(){this.enabled=!0},c.prototype.disable=function(){this.enabled=!1},c.prototype.toggleEnabled=function(){this.enabled=!this.enabled},c.prototype.toggle=function(b){var c=this;b&&(c=a(b.currentTarget).data("bs."+this.type),c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c))),b?(c.inState.click=!c.inState.click,c.isInStateTrue()?c.enter(c):c.leave(c)):c.tip().hasClass("in")?c.leave(c):c.enter(c)},c.prototype.destroy=function(){var a=this;clearTimeout(this.timeout),this.hide(function(){a.$element.off("."+a.type).removeData("bs."+a.type),a.$tip&&a.$tip.detach(),a.$tip=null,a.$arrow=null,a.$viewport=null})};var d=a.fn.tooltip;a.fn.tooltip=b,a.fn.tooltip.Constructor=c,a.fn.tooltip.noConflict=function(){return a.fn.tooltip=d,this}}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.popover"),f="object"==typeof b&&b;(e||!/destroy|hide/.test(b))&&(e||d.data("bs.popover",e=new c(this,f)),"string"==typeof b&&e[b]())})}var c=function(a,b){this.init("popover",a,b)};if(!a.fn.tooltip)throw new Error("Popover requires tooltip.js");c.VERSION="3.3.5",c.DEFAULTS=a.extend({},a.fn.tooltip.Constructor.DEFAULTS,{placement:"right",trigger:"click",content:"",template:''}),c.prototype=a.extend({},a.fn.tooltip.Constructor.prototype),c.prototype.constructor=c,c.prototype.getDefaults=function(){return c.DEFAULTS},c.prototype.setContent=function(){var a=this.tip(),b=this.getTitle(),c=this.getContent();a.find(".popover-title")[this.options.html?"html":"text"](b),a.find(".popover-content").children().detach().end()[this.options.html?"string"==typeof c?"html":"append":"text"](c),a.removeClass("fade top bottom left right in"),a.find(".popover-title").html()||a.find(".popover-title").hide()},c.prototype.hasContent=function(){return this.getTitle()||this.getContent()},c.prototype.getContent=function(){var a=this.$element,b=this.options;return a.attr("data-content")||("function"==typeof b.content?b.content.call(a[0]):b.content)},c.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".arrow")};var d=a.fn.popover;a.fn.popover=b,a.fn.popover.Constructor=c,a.fn.popover.noConflict=function(){return a.fn.popover=d,this}}(jQuery),+function(a){"use strict";function b(c,d){this.$body=a(document.body),this.$scrollElement=a(a(c).is(document.body)?window:c),this.options=a.extend({},b.DEFAULTS,d),this.selector=(this.options.target||"")+" .nav li > a",this.offsets=[],this.targets=[],this.activeTarget=null,this.scrollHeight=0,this.$scrollElement.on("scroll.bs.scrollspy",a.proxy(this.process,this)),this.refresh(),this.process()}function c(c){return this.each(function(){var d=a(this),e=d.data("bs.scrollspy"),f="object"==typeof c&&c;e||d.data("bs.scrollspy",e=new b(this,f)),"string"==typeof c&&e[c]()})}b.VERSION="3.3.5",b.DEFAULTS={offset:10},b.prototype.getScrollHeight=function(){return this.$scrollElement[0].scrollHeight||Math.max(this.$body[0].scrollHeight,document.documentElement.scrollHeight)},b.prototype.refresh=function(){var b=this,c="offset",d=0;this.offsets=[],this.targets=[],this.scrollHeight=this.getScrollHeight(),a.isWindow(this.$scrollElement[0])||(c="position",d=this.$scrollElement.scrollTop()),this.$body.find(this.selector).map(function(){var b=a(this),e=b.data("target")||b.attr("href"),f=/^#./.test(e)&&a(e);return f&&f.length&&f.is(":visible")&&[[f[c]().top+d,e]]||null}).sort(function(a,b){return a[0]-b[0]}).each(function(){b.offsets.push(this[0]),b.targets.push(this[1])})},b.prototype.process=function(){var a,b=this.$scrollElement.scrollTop()+this.options.offset,c=this.getScrollHeight(),d=this.options.offset+c-this.$scrollElement.height(),e=this.offsets,f=this.targets,g=this.activeTarget;if(this.scrollHeight!=c&&this.refresh(),b>=d)return g!=(a=f[f.length-1])&&this.activate(a);if(g&&b=e[a]&&(void 0===e[a+1]||b .dropdown-menu > .active").removeClass("active").end().find('[data-toggle="tab"]').attr("aria-expanded",!1),b.addClass("active").find('[data-toggle="tab"]').attr("aria-expanded",!0),h?(b[0].offsetWidth,b.addClass("in")):b.removeClass("fade"),b.parent(".dropdown-menu").length&&b.closest("li.dropdown").addClass("active").end().find('[data-toggle="tab"]').attr("aria-expanded",!0),e&&e()}var g=d.find("> .active"),h=e&&a.support.transition&&(g.length&&g.hasClass("fade")||!!d.find("> .fade").length);g.length&&h?g.one("bsTransitionEnd",f).emulateTransitionEnd(c.TRANSITION_DURATION):f(),g.removeClass("in")};var d=a.fn.tab;a.fn.tab=b,a.fn.tab.Constructor=c,a.fn.tab.noConflict=function(){return a.fn.tab=d,this};var e=function(c){c.preventDefault(),b.call(a(this),"show")};a(document).on("click.bs.tab.data-api",'[data-toggle="tab"]',e).on("click.bs.tab.data-api",'[data-toggle="pill"]',e)}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.affix"),f="object"==typeof b&&b;e||d.data("bs.affix",e=new c(this,f)),"string"==typeof b&&e[b]()})}var c=function(b,d){this.options=a.extend({},c.DEFAULTS,d),this.$target=a(this.options.target).on("scroll.bs.affix.data-api",a.proxy(this.checkPosition,this)).on("click.bs.affix.data-api",a.proxy(this.checkPositionWithEventLoop,this)),this.$element=a(b),this.affixed=null,this.unpin=null,this.pinnedOffset=null,this.checkPosition()};c.VERSION="3.3.5",c.RESET="affix affix-top affix-bottom",c.DEFAULTS={offset:0,target:window},c.prototype.getState=function(a,b,c,d){var e=this.$target.scrollTop(),f=this.$element.offset(),g=this.$target.height();if(null!=c&&"top"==this.affixed)return c>e?"top":!1;if("bottom"==this.affixed)return null!=c?e+this.unpin<=f.top?!1:"bottom":a-d>=e+g?!1:"bottom";var h=null==this.affixed,i=h?e:f.top,j=h?g:b;return null!=c&&c>=e?"top":null!=d&&i+j>=a-d?"bottom":!1},c.prototype.getPinnedOffset=function(){if(this.pinnedOffset)return this.pinnedOffset;this.$element.removeClass(c.RESET).addClass("affix");var a=this.$target.scrollTop(),b=this.$element.offset();return this.pinnedOffset=b.top-a},c.prototype.checkPositionWithEventLoop=function(){setTimeout(a.proxy(this.checkPosition,this),1)},c.prototype.checkPosition=function(){if(this.$element.is(":visible")){var b=this.$element.height(),d=this.options.offset,e=d.top,f=d.bottom,g=Math.max(a(document).height(),a(document.body).height());"object"!=typeof d&&(f=e=d),"function"==typeof e&&(e=d.top(this.$element)),"function"==typeof f&&(f=d.bottom(this.$element));var h=this.getState(g,b,e,f);if(this.affixed!=h){null!=this.unpin&&this.$element.css("top","");var i="affix"+(h?"-"+h:""),j=a.Event(i+".bs.affix");if(this.$element.trigger(j),j.isDefaultPrevented())return;this.affixed=h,this.unpin="bottom"==h?this.getPinnedOffset():null,this.$element.removeClass(c.RESET).addClass(i).trigger(i.replace("affix","affixed")+".bs.affix")}"bottom"==h&&this.$element.offset({top:g-b-f})}};var d=a.fn.affix;a.fn.affix=b,a.fn.affix.Constructor=c,a.fn.affix.noConflict=function(){return a.fn.affix=d,this},a(window).on("load",function(){a('[data-spy="affix"]').each(function(){var c=a(this),d=c.data();d.offset=d.offset||{},null!=d.offsetBottom&&(d.offset.bottom=d.offsetBottom),null!=d.offsetTop&&(d.offset.top=d.offsetTop),b.call(c,d)})})}(jQuery); \ No newline at end of file diff --git a/js/bootstrap.3.3.7.js b/js/bootstrap.3.3.7.js new file mode 100644 index 00000000..9bcd2fcc --- /dev/null +++ b/js/bootstrap.3.3.7.js @@ -0,0 +1,7 @@ +/*! + * Bootstrap v3.3.7 (http://getbootstrap.com) + * Copyright 2011-2016 Twitter, Inc. + * Licensed under the MIT license + */ +if("undefined"==typeof jQuery)throw new Error("Bootstrap's JavaScript requires jQuery");+function(a){"use strict";var b=a.fn.jquery.split(" ")[0].split(".");if(b[0]<2&&b[1]<9||1==b[0]&&9==b[1]&&b[2]<1||b[0]>3)throw new Error("Bootstrap's JavaScript requires jQuery version 1.9.1 or higher, but lower than version 4")}(jQuery),+function(a){"use strict";function b(){var a=document.createElement("bootstrap"),b={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"};for(var c in b)if(void 0!==a.style[c])return{end:b[c]};return!1}a.fn.emulateTransitionEnd=function(b){var c=!1,d=this;a(this).one("bsTransitionEnd",function(){c=!0});var e=function(){c||a(d).trigger(a.support.transition.end)};return setTimeout(e,b),this},a(function(){a.support.transition=b(),a.support.transition&&(a.event.special.bsTransitionEnd={bindType:a.support.transition.end,delegateType:a.support.transition.end,handle:function(b){if(a(b.target).is(this))return b.handleObj.handler.apply(this,arguments)}})})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var c=a(this),e=c.data("bs.alert");e||c.data("bs.alert",e=new d(this)),"string"==typeof b&&e[b].call(c)})}var c='[data-dismiss="alert"]',d=function(b){a(b).on("click",c,this.close)};d.VERSION="3.3.7",d.TRANSITION_DURATION=150,d.prototype.close=function(b){function c(){g.detach().trigger("closed.bs.alert").remove()}var e=a(this),f=e.attr("data-target");f||(f=e.attr("href"),f=f&&f.replace(/.*(?=#[^\s]*$)/,""));var g=a("#"===f?[]:f);b&&b.preventDefault(),g.length||(g=e.closest(".alert")),g.trigger(b=a.Event("close.bs.alert")),b.isDefaultPrevented()||(g.removeClass("in"),a.support.transition&&g.hasClass("fade")?g.one("bsTransitionEnd",c).emulateTransitionEnd(d.TRANSITION_DURATION):c())};var e=a.fn.alert;a.fn.alert=b,a.fn.alert.Constructor=d,a.fn.alert.noConflict=function(){return a.fn.alert=e,this},a(document).on("click.bs.alert.data-api",c,d.prototype.close)}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.button"),f="object"==typeof b&&b;e||d.data("bs.button",e=new c(this,f)),"toggle"==b?e.toggle():b&&e.setState(b)})}var c=function(b,d){this.$element=a(b),this.options=a.extend({},c.DEFAULTS,d),this.isLoading=!1};c.VERSION="3.3.7",c.DEFAULTS={loadingText:"loading..."},c.prototype.setState=function(b){var c="disabled",d=this.$element,e=d.is("input")?"val":"html",f=d.data();b+="Text",null==f.resetText&&d.data("resetText",d[e]()),setTimeout(a.proxy(function(){d[e](null==f[b]?this.options[b]:f[b]),"loadingText"==b?(this.isLoading=!0,d.addClass(c).attr(c,c).prop(c,!0)):this.isLoading&&(this.isLoading=!1,d.removeClass(c).removeAttr(c).prop(c,!1))},this),0)},c.prototype.toggle=function(){var a=!0,b=this.$element.closest('[data-toggle="buttons"]');if(b.length){var c=this.$element.find("input");"radio"==c.prop("type")?(c.prop("checked")&&(a=!1),b.find(".active").removeClass("active"),this.$element.addClass("active")):"checkbox"==c.prop("type")&&(c.prop("checked")!==this.$element.hasClass("active")&&(a=!1),this.$element.toggleClass("active")),c.prop("checked",this.$element.hasClass("active")),a&&c.trigger("change")}else this.$element.attr("aria-pressed",!this.$element.hasClass("active")),this.$element.toggleClass("active")};var d=a.fn.button;a.fn.button=b,a.fn.button.Constructor=c,a.fn.button.noConflict=function(){return a.fn.button=d,this},a(document).on("click.bs.button.data-api",'[data-toggle^="button"]',function(c){var d=a(c.target).closest(".btn");b.call(d,"toggle"),a(c.target).is('input[type="radio"], input[type="checkbox"]')||(c.preventDefault(),d.is("input,button")?d.trigger("focus"):d.find("input:visible,button:visible").first().trigger("focus"))}).on("focus.bs.button.data-api blur.bs.button.data-api",'[data-toggle^="button"]',function(b){a(b.target).closest(".btn").toggleClass("focus",/^focus(in)?$/.test(b.type))})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.carousel"),f=a.extend({},c.DEFAULTS,d.data(),"object"==typeof b&&b),g="string"==typeof b?b:f.slide;e||d.data("bs.carousel",e=new c(this,f)),"number"==typeof b?e.to(b):g?e[g]():f.interval&&e.pause().cycle()})}var c=function(b,c){this.$element=a(b),this.$indicators=this.$element.find(".carousel-indicators"),this.options=c,this.paused=null,this.sliding=null,this.interval=null,this.$active=null,this.$items=null,this.options.keyboard&&this.$element.on("keydown.bs.carousel",a.proxy(this.keydown,this)),"hover"==this.options.pause&&!("ontouchstart"in document.documentElement)&&this.$element.on("mouseenter.bs.carousel",a.proxy(this.pause,this)).on("mouseleave.bs.carousel",a.proxy(this.cycle,this))};c.VERSION="3.3.7",c.TRANSITION_DURATION=600,c.DEFAULTS={interval:5e3,pause:"hover",wrap:!0,keyboard:!0},c.prototype.keydown=function(a){if(!/input|textarea/i.test(a.target.tagName)){switch(a.which){case 37:this.prev();break;case 39:this.next();break;default:return}a.preventDefault()}},c.prototype.cycle=function(b){return b||(this.paused=!1),this.interval&&clearInterval(this.interval),this.options.interval&&!this.paused&&(this.interval=setInterval(a.proxy(this.next,this),this.options.interval)),this},c.prototype.getItemIndex=function(a){return this.$items=a.parent().children(".item"),this.$items.index(a||this.$active)},c.prototype.getItemForDirection=function(a,b){var c=this.getItemIndex(b),d="prev"==a&&0===c||"next"==a&&c==this.$items.length-1;if(d&&!this.options.wrap)return b;var e="prev"==a?-1:1,f=(c+e)%this.$items.length;return this.$items.eq(f)},c.prototype.to=function(a){var b=this,c=this.getItemIndex(this.$active=this.$element.find(".item.active"));if(!(a>this.$items.length-1||a<0))return this.sliding?this.$element.one("slid.bs.carousel",function(){b.to(a)}):c==a?this.pause().cycle():this.slide(a>c?"next":"prev",this.$items.eq(a))},c.prototype.pause=function(b){return b||(this.paused=!0),this.$element.find(".next, .prev").length&&a.support.transition&&(this.$element.trigger(a.support.transition.end),this.cycle(!0)),this.interval=clearInterval(this.interval),this},c.prototype.next=function(){if(!this.sliding)return this.slide("next")},c.prototype.prev=function(){if(!this.sliding)return this.slide("prev")},c.prototype.slide=function(b,d){var e=this.$element.find(".item.active"),f=d||this.getItemForDirection(b,e),g=this.interval,h="next"==b?"left":"right",i=this;if(f.hasClass("active"))return this.sliding=!1;var j=f[0],k=a.Event("slide.bs.carousel",{relatedTarget:j,direction:h});if(this.$element.trigger(k),!k.isDefaultPrevented()){if(this.sliding=!0,g&&this.pause(),this.$indicators.length){this.$indicators.find(".active").removeClass("active");var l=a(this.$indicators.children()[this.getItemIndex(f)]);l&&l.addClass("active")}var m=a.Event("slid.bs.carousel",{relatedTarget:j,direction:h});return a.support.transition&&this.$element.hasClass("slide")?(f.addClass(b),f[0].offsetWidth,e.addClass(h),f.addClass(h),e.one("bsTransitionEnd",function(){f.removeClass([b,h].join(" ")).addClass("active"),e.removeClass(["active",h].join(" ")),i.sliding=!1,setTimeout(function(){i.$element.trigger(m)},0)}).emulateTransitionEnd(c.TRANSITION_DURATION)):(e.removeClass("active"),f.addClass("active"),this.sliding=!1,this.$element.trigger(m)),g&&this.cycle(),this}};var d=a.fn.carousel;a.fn.carousel=b,a.fn.carousel.Constructor=c,a.fn.carousel.noConflict=function(){return a.fn.carousel=d,this};var e=function(c){var d,e=a(this),f=a(e.attr("data-target")||(d=e.attr("href"))&&d.replace(/.*(?=#[^\s]+$)/,""));if(f.hasClass("carousel")){var g=a.extend({},f.data(),e.data()),h=e.attr("data-slide-to");h&&(g.interval=!1),b.call(f,g),h&&f.data("bs.carousel").to(h),c.preventDefault()}};a(document).on("click.bs.carousel.data-api","[data-slide]",e).on("click.bs.carousel.data-api","[data-slide-to]",e),a(window).on("load",function(){a('[data-ride="carousel"]').each(function(){var c=a(this);b.call(c,c.data())})})}(jQuery),+function(a){"use strict";function b(b){var c,d=b.attr("data-target")||(c=b.attr("href"))&&c.replace(/.*(?=#[^\s]+$)/,"");return a(d)}function c(b){return this.each(function(){var c=a(this),e=c.data("bs.collapse"),f=a.extend({},d.DEFAULTS,c.data(),"object"==typeof b&&b);!e&&f.toggle&&/show|hide/.test(b)&&(f.toggle=!1),e||c.data("bs.collapse",e=new d(this,f)),"string"==typeof b&&e[b]()})}var d=function(b,c){this.$element=a(b),this.options=a.extend({},d.DEFAULTS,c),this.$trigger=a('[data-toggle="collapse"][href="#'+b.id+'"],[data-toggle="collapse"][data-target="#'+b.id+'"]'),this.transitioning=null,this.options.parent?this.$parent=this.getParent():this.addAriaAndCollapsedClass(this.$element,this.$trigger),this.options.toggle&&this.toggle()};d.VERSION="3.3.7",d.TRANSITION_DURATION=350,d.DEFAULTS={toggle:!0},d.prototype.dimension=function(){var a=this.$element.hasClass("width");return a?"width":"height"},d.prototype.show=function(){if(!this.transitioning&&!this.$element.hasClass("in")){var b,e=this.$parent&&this.$parent.children(".panel").children(".in, .collapsing");if(!(e&&e.length&&(b=e.data("bs.collapse"),b&&b.transitioning))){var f=a.Event("show.bs.collapse");if(this.$element.trigger(f),!f.isDefaultPrevented()){e&&e.length&&(c.call(e,"hide"),b||e.data("bs.collapse",null));var g=this.dimension();this.$element.removeClass("collapse").addClass("collapsing")[g](0).attr("aria-expanded",!0),this.$trigger.removeClass("collapsed").attr("aria-expanded",!0),this.transitioning=1;var h=function(){this.$element.removeClass("collapsing").addClass("collapse in")[g](""),this.transitioning=0,this.$element.trigger("shown.bs.collapse")};if(!a.support.transition)return h.call(this);var i=a.camelCase(["scroll",g].join("-"));this.$element.one("bsTransitionEnd",a.proxy(h,this)).emulateTransitionEnd(d.TRANSITION_DURATION)[g](this.$element[0][i])}}}},d.prototype.hide=function(){if(!this.transitioning&&this.$element.hasClass("in")){var b=a.Event("hide.bs.collapse");if(this.$element.trigger(b),!b.isDefaultPrevented()){var c=this.dimension();this.$element[c](this.$element[c]())[0].offsetHeight,this.$element.addClass("collapsing").removeClass("collapse in").attr("aria-expanded",!1),this.$trigger.addClass("collapsed").attr("aria-expanded",!1),this.transitioning=1;var e=function(){this.transitioning=0,this.$element.removeClass("collapsing").addClass("collapse").trigger("hidden.bs.collapse")};return a.support.transition?void this.$element[c](0).one("bsTransitionEnd",a.proxy(e,this)).emulateTransitionEnd(d.TRANSITION_DURATION):e.call(this)}}},d.prototype.toggle=function(){this[this.$element.hasClass("in")?"hide":"show"]()},d.prototype.getParent=function(){return a(this.options.parent).find('[data-toggle="collapse"][data-parent="'+this.options.parent+'"]').each(a.proxy(function(c,d){var e=a(d);this.addAriaAndCollapsedClass(b(e),e)},this)).end()},d.prototype.addAriaAndCollapsedClass=function(a,b){var c=a.hasClass("in");a.attr("aria-expanded",c),b.toggleClass("collapsed",!c).attr("aria-expanded",c)};var e=a.fn.collapse;a.fn.collapse=c,a.fn.collapse.Constructor=d,a.fn.collapse.noConflict=function(){return a.fn.collapse=e,this},a(document).on("click.bs.collapse.data-api",'[data-toggle="collapse"]',function(d){var e=a(this);e.attr("data-target")||d.preventDefault();var f=b(e),g=f.data("bs.collapse"),h=g?"toggle":e.data();c.call(f,h)})}(jQuery),+function(a){"use strict";function b(b){var c=b.attr("data-target");c||(c=b.attr("href"),c=c&&/#[A-Za-z]/.test(c)&&c.replace(/.*(?=#[^\s]*$)/,""));var d=c&&a(c);return d&&d.length?d:b.parent()}function c(c){c&&3===c.which||(a(e).remove(),a(f).each(function(){var d=a(this),e=b(d),f={relatedTarget:this};e.hasClass("open")&&(c&&"click"==c.type&&/input|textarea/i.test(c.target.tagName)&&a.contains(e[0],c.target)||(e.trigger(c=a.Event("hide.bs.dropdown",f)),c.isDefaultPrevented()||(d.attr("aria-expanded","false"),e.removeClass("open").trigger(a.Event("hidden.bs.dropdown",f)))))}))}function d(b){return this.each(function(){var c=a(this),d=c.data("bs.dropdown");d||c.data("bs.dropdown",d=new g(this)),"string"==typeof b&&d[b].call(c)})}var e=".dropdown-backdrop",f='[data-toggle="dropdown"]',g=function(b){a(b).on("click.bs.dropdown",this.toggle)};g.VERSION="3.3.7",g.prototype.toggle=function(d){var e=a(this);if(!e.is(".disabled, :disabled")){var f=b(e),g=f.hasClass("open");if(c(),!g){"ontouchstart"in document.documentElement&&!f.closest(".navbar-nav").length&&a(document.createElement("div")).addClass("dropdown-backdrop").insertAfter(a(this)).on("click",c);var h={relatedTarget:this};if(f.trigger(d=a.Event("show.bs.dropdown",h)),d.isDefaultPrevented())return;e.trigger("focus").attr("aria-expanded","true"),f.toggleClass("open").trigger(a.Event("shown.bs.dropdown",h))}return!1}},g.prototype.keydown=function(c){if(/(38|40|27|32)/.test(c.which)&&!/input|textarea/i.test(c.target.tagName)){var d=a(this);if(c.preventDefault(),c.stopPropagation(),!d.is(".disabled, :disabled")){var e=b(d),g=e.hasClass("open");if(!g&&27!=c.which||g&&27==c.which)return 27==c.which&&e.find(f).trigger("focus"),d.trigger("click");var h=" li:not(.disabled):visible a",i=e.find(".dropdown-menu"+h);if(i.length){var j=i.index(c.target);38==c.which&&j>0&&j--,40==c.which&&jdocument.documentElement.clientHeight;this.$element.css({paddingLeft:!this.bodyIsOverflowing&&a?this.scrollbarWidth:"",paddingRight:this.bodyIsOverflowing&&!a?this.scrollbarWidth:""})},c.prototype.resetAdjustments=function(){this.$element.css({paddingLeft:"",paddingRight:""})},c.prototype.checkScrollbar=function(){var a=window.innerWidth;if(!a){var b=document.documentElement.getBoundingClientRect();a=b.right-Math.abs(b.left)}this.bodyIsOverflowing=document.body.clientWidth
',trigger:"hover focus",title:"",delay:0,html:!1,container:!1,viewport:{selector:"body",padding:0}},c.prototype.init=function(b,c,d){if(this.enabled=!0,this.type=b,this.$element=a(c),this.options=this.getOptions(d),this.$viewport=this.options.viewport&&a(a.isFunction(this.options.viewport)?this.options.viewport.call(this,this.$element):this.options.viewport.selector||this.options.viewport),this.inState={click:!1,hover:!1,focus:!1},this.$element[0]instanceof document.constructor&&!this.options.selector)throw new Error("`selector` option must be specified when initializing "+this.type+" on the window.document object!");for(var e=this.options.trigger.split(" "),f=e.length;f--;){var g=e[f];if("click"==g)this.$element.on("click."+this.type,this.options.selector,a.proxy(this.toggle,this));else if("manual"!=g){var h="hover"==g?"mouseenter":"focusin",i="hover"==g?"mouseleave":"focusout";this.$element.on(h+"."+this.type,this.options.selector,a.proxy(this.enter,this)),this.$element.on(i+"."+this.type,this.options.selector,a.proxy(this.leave,this))}}this.options.selector?this._options=a.extend({},this.options,{trigger:"manual",selector:""}):this.fixTitle()},c.prototype.getDefaults=function(){return c.DEFAULTS},c.prototype.getOptions=function(b){return b=a.extend({},this.getDefaults(),this.$element.data(),b),b.delay&&"number"==typeof b.delay&&(b.delay={show:b.delay,hide:b.delay}),b},c.prototype.getDelegateOptions=function(){var b={},c=this.getDefaults();return this._options&&a.each(this._options,function(a,d){c[a]!=d&&(b[a]=d)}),b},c.prototype.enter=function(b){var c=b instanceof this.constructor?b:a(b.currentTarget).data("bs."+this.type);return c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c)),b instanceof a.Event&&(c.inState["focusin"==b.type?"focus":"hover"]=!0),c.tip().hasClass("in")||"in"==c.hoverState?void(c.hoverState="in"):(clearTimeout(c.timeout),c.hoverState="in",c.options.delay&&c.options.delay.show?void(c.timeout=setTimeout(function(){"in"==c.hoverState&&c.show()},c.options.delay.show)):c.show())},c.prototype.isInStateTrue=function(){for(var a in this.inState)if(this.inState[a])return!0;return!1},c.prototype.leave=function(b){var c=b instanceof this.constructor?b:a(b.currentTarget).data("bs."+this.type);if(c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c)),b instanceof a.Event&&(c.inState["focusout"==b.type?"focus":"hover"]=!1),!c.isInStateTrue())return clearTimeout(c.timeout),c.hoverState="out",c.options.delay&&c.options.delay.hide?void(c.timeout=setTimeout(function(){"out"==c.hoverState&&c.hide()},c.options.delay.hide)):c.hide()},c.prototype.show=function(){var b=a.Event("show.bs."+this.type);if(this.hasContent()&&this.enabled){this.$element.trigger(b);var d=a.contains(this.$element[0].ownerDocument.documentElement,this.$element[0]);if(b.isDefaultPrevented()||!d)return;var e=this,f=this.tip(),g=this.getUID(this.type);this.setContent(),f.attr("id",g),this.$element.attr("aria-describedby",g),this.options.animation&&f.addClass("fade");var h="function"==typeof this.options.placement?this.options.placement.call(this,f[0],this.$element[0]):this.options.placement,i=/\s?auto?\s?/i,j=i.test(h);j&&(h=h.replace(i,"")||"top"),f.detach().css({top:0,left:0,display:"block"}).addClass(h).data("bs."+this.type,this),this.options.container?f.appendTo(this.options.container):f.insertAfter(this.$element),this.$element.trigger("inserted.bs."+this.type);var k=this.getPosition(),l=f[0].offsetWidth,m=f[0].offsetHeight;if(j){var n=h,o=this.getPosition(this.$viewport);h="bottom"==h&&k.bottom+m>o.bottom?"top":"top"==h&&k.top-mo.width?"left":"left"==h&&k.left-lg.top+g.height&&(e.top=g.top+g.height-i)}else{var j=b.left-f,k=b.left+f+c;jg.right&&(e.left=g.left+g.width-k)}return e},c.prototype.getTitle=function(){var a,b=this.$element,c=this.options;return a=b.attr("data-original-title")||("function"==typeof c.title?c.title.call(b[0]):c.title)},c.prototype.getUID=function(a){do a+=~~(1e6*Math.random());while(document.getElementById(a));return a},c.prototype.tip=function(){if(!this.$tip&&(this.$tip=a(this.options.template),1!=this.$tip.length))throw new Error(this.type+" `template` option must consist of exactly 1 top-level element!");return this.$tip},c.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".tooltip-arrow")},c.prototype.enable=function(){this.enabled=!0},c.prototype.disable=function(){this.enabled=!1},c.prototype.toggleEnabled=function(){this.enabled=!this.enabled},c.prototype.toggle=function(b){var c=this;b&&(c=a(b.currentTarget).data("bs."+this.type),c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c))),b?(c.inState.click=!c.inState.click,c.isInStateTrue()?c.enter(c):c.leave(c)):c.tip().hasClass("in")?c.leave(c):c.enter(c)},c.prototype.destroy=function(){var a=this;clearTimeout(this.timeout),this.hide(function(){a.$element.off("."+a.type).removeData("bs."+a.type),a.$tip&&a.$tip.detach(),a.$tip=null,a.$arrow=null,a.$viewport=null,a.$element=null})};var d=a.fn.tooltip;a.fn.tooltip=b,a.fn.tooltip.Constructor=c,a.fn.tooltip.noConflict=function(){return a.fn.tooltip=d,this}}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.popover"),f="object"==typeof b&&b;!e&&/destroy|hide/.test(b)||(e||d.data("bs.popover",e=new c(this,f)),"string"==typeof b&&e[b]())})}var c=function(a,b){this.init("popover",a,b)};if(!a.fn.tooltip)throw new Error("Popover requires tooltip.js");c.VERSION="3.3.7",c.DEFAULTS=a.extend({},a.fn.tooltip.Constructor.DEFAULTS,{placement:"right",trigger:"click",content:"",template:''}),c.prototype=a.extend({},a.fn.tooltip.Constructor.prototype),c.prototype.constructor=c,c.prototype.getDefaults=function(){return c.DEFAULTS},c.prototype.setContent=function(){var a=this.tip(),b=this.getTitle(),c=this.getContent();a.find(".popover-title")[this.options.html?"html":"text"](b),a.find(".popover-content").children().detach().end()[this.options.html?"string"==typeof c?"html":"append":"text"](c),a.removeClass("fade top bottom left right in"),a.find(".popover-title").html()||a.find(".popover-title").hide()},c.prototype.hasContent=function(){return this.getTitle()||this.getContent()},c.prototype.getContent=function(){var a=this.$element,b=this.options;return a.attr("data-content")||("function"==typeof b.content?b.content.call(a[0]):b.content)},c.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".arrow")};var d=a.fn.popover;a.fn.popover=b,a.fn.popover.Constructor=c,a.fn.popover.noConflict=function(){return a.fn.popover=d,this}}(jQuery),+function(a){"use strict";function b(c,d){this.$body=a(document.body),this.$scrollElement=a(a(c).is(document.body)?window:c),this.options=a.extend({},b.DEFAULTS,d),this.selector=(this.options.target||"")+" .nav li > a",this.offsets=[],this.targets=[],this.activeTarget=null,this.scrollHeight=0,this.$scrollElement.on("scroll.bs.scrollspy",a.proxy(this.process,this)),this.refresh(),this.process()}function c(c){return this.each(function(){var d=a(this),e=d.data("bs.scrollspy"),f="object"==typeof c&&c;e||d.data("bs.scrollspy",e=new b(this,f)),"string"==typeof c&&e[c]()})}b.VERSION="3.3.7",b.DEFAULTS={offset:10},b.prototype.getScrollHeight=function(){return this.$scrollElement[0].scrollHeight||Math.max(this.$body[0].scrollHeight,document.documentElement.scrollHeight)},b.prototype.refresh=function(){var b=this,c="offset",d=0;this.offsets=[],this.targets=[],this.scrollHeight=this.getScrollHeight(),a.isWindow(this.$scrollElement[0])||(c="position",d=this.$scrollElement.scrollTop()),this.$body.find(this.selector).map(function(){var b=a(this),e=b.data("target")||b.attr("href"),f=/^#./.test(e)&&a(e);return f&&f.length&&f.is(":visible")&&[[f[c]().top+d,e]]||null}).sort(function(a,b){return a[0]-b[0]}).each(function(){b.offsets.push(this[0]),b.targets.push(this[1])})},b.prototype.process=function(){var a,b=this.$scrollElement.scrollTop()+this.options.offset,c=this.getScrollHeight(),d=this.options.offset+c-this.$scrollElement.height(),e=this.offsets,f=this.targets,g=this.activeTarget;if(this.scrollHeight!=c&&this.refresh(),b>=d)return g!=(a=f[f.length-1])&&this.activate(a);if(g&&b=e[a]&&(void 0===e[a+1]||b .dropdown-menu > .active").removeClass("active").end().find('[data-toggle="tab"]').attr("aria-expanded",!1),b.addClass("active").find('[data-toggle="tab"]').attr("aria-expanded",!0),h?(b[0].offsetWidth,b.addClass("in")):b.removeClass("fade"),b.parent(".dropdown-menu").length&&b.closest("li.dropdown").addClass("active").end().find('[data-toggle="tab"]').attr("aria-expanded",!0),e&&e()}var g=d.find("> .active"),h=e&&a.support.transition&&(g.length&&g.hasClass("fade")||!!d.find("> .fade").length);g.length&&h?g.one("bsTransitionEnd",f).emulateTransitionEnd(c.TRANSITION_DURATION):f(),g.removeClass("in")};var d=a.fn.tab;a.fn.tab=b,a.fn.tab.Constructor=c,a.fn.tab.noConflict=function(){return a.fn.tab=d,this};var e=function(c){c.preventDefault(),b.call(a(this),"show")};a(document).on("click.bs.tab.data-api",'[data-toggle="tab"]',e).on("click.bs.tab.data-api",'[data-toggle="pill"]',e)}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.affix"),f="object"==typeof b&&b;e||d.data("bs.affix",e=new c(this,f)),"string"==typeof b&&e[b]()})}var c=function(b,d){this.options=a.extend({},c.DEFAULTS,d),this.$target=a(this.options.target).on("scroll.bs.affix.data-api",a.proxy(this.checkPosition,this)).on("click.bs.affix.data-api",a.proxy(this.checkPositionWithEventLoop,this)),this.$element=a(b),this.affixed=null,this.unpin=null,this.pinnedOffset=null,this.checkPosition()};c.VERSION="3.3.7",c.RESET="affix affix-top affix-bottom",c.DEFAULTS={offset:0,target:window},c.prototype.getState=function(a,b,c,d){var e=this.$target.scrollTop(),f=this.$element.offset(),g=this.$target.height();if(null!=c&&"top"==this.affixed)return e=a-d&&"bottom"},c.prototype.getPinnedOffset=function(){if(this.pinnedOffset)return this.pinnedOffset;this.$element.removeClass(c.RESET).addClass("affix");var a=this.$target.scrollTop(),b=this.$element.offset();return this.pinnedOffset=b.top-a},c.prototype.checkPositionWithEventLoop=function(){setTimeout(a.proxy(this.checkPosition,this),1)},c.prototype.checkPosition=function(){if(this.$element.is(":visible")){var b=this.$element.height(),d=this.options.offset,e=d.top,f=d.bottom,g=Math.max(a(document).height(),a(document.body).height());"object"!=typeof d&&(f=e=d),"function"==typeof e&&(e=d.top(this.$element)),"function"==typeof f&&(f=d.bottom(this.$element));var h=this.getState(g,b,e,f);if(this.affixed!=h){null!=this.unpin&&this.$element.css("top","");var i="affix"+(h?"-"+h:""),j=a.Event(i+".bs.affix");if(this.$element.trigger(j),j.isDefaultPrevented())return;this.affixed=h,this.unpin="bottom"==h?this.getPinnedOffset():null,this.$element.removeClass(c.RESET).addClass(i).trigger(i.replace("affix","affixed")+".bs.affix")}"bottom"==h&&this.$element.offset({top:g-b-f})}};var d=a.fn.affix;a.fn.affix=b,a.fn.affix.Constructor=c,a.fn.affix.noConflict=function(){return a.fn.affix=d,this},a(window).on("load",function(){a('[data-spy="affix"]').each(function(){var c=a(this),d=c.data();d.offset=d.offset||{},null!=d.offsetBottom&&(d.offset.bottom=d.offsetBottom),null!=d.offsetTop&&(d.offset.top=d.offsetTop),b.call(c,d)})})}(jQuery); \ No newline at end of file From 47e4478e174b23003b1b2253ef86164074e37373 Mon Sep 17 00:00:00 2001 From: Nathaniel Olsen Date: Mon, 23 Jan 2017 22:42:05 -0600 Subject: [PATCH 0012/1638] Updated sjcl, bootstrap, and the hashes --- js/{bootstrap.3.3.7.js => bootstrap-3.3.7.js} | 0 js/sjcl-1.0.4.js | 58 ------------------ js/sjcl-1.0.6.js | 60 +++++++++++++++++++ tpl/bootstrap.php | 4 +- 4 files changed, 62 insertions(+), 60 deletions(-) rename js/{bootstrap.3.3.7.js => bootstrap-3.3.7.js} (100%) delete mode 100644 js/sjcl-1.0.4.js create mode 100644 js/sjcl-1.0.6.js diff --git a/js/bootstrap.3.3.7.js b/js/bootstrap-3.3.7.js similarity index 100% rename from js/bootstrap.3.3.7.js rename to js/bootstrap-3.3.7.js diff --git a/js/sjcl-1.0.4.js b/js/sjcl-1.0.4.js deleted file mode 100644 index e22ab036..00000000 --- a/js/sjcl-1.0.4.js +++ /dev/null @@ -1,58 +0,0 @@ -"use strict";function q(a){throw a;}var s=void 0,u=!1;var sjcl={cipher:{},hash:{},keyexchange:{},mode:{},misc:{},codec:{},exception:{corrupt:function(a){this.toString=function(){return"CORRUPT: "+this.message};this.message=a},invalid:function(a){this.toString=function(){return"INVALID: "+this.message};this.message=a},bug:function(a){this.toString=function(){return"BUG: "+this.message};this.message=a},notReady:function(a){this.toString=function(){return"NOT READY: "+this.message};this.message=a}}}; -"undefined"!==typeof module&&module.exports&&(module.exports=sjcl);"function"===typeof define&&define([],function(){return sjcl}); -sjcl.cipher.aes=function(a){this.l[0][0][0]||this.G();var b,c,d,e,g=this.l[0][4],f=this.l[1];b=a.length;var h=1;4!==b&&(6!==b&&8!==b)&&q(new sjcl.exception.invalid("invalid aes key size"));this.b=[d=a.slice(0),e=[]];for(a=b;a<4*b+28;a++){c=d[a-1];if(0===a%b||8===b&&4===a%b)c=g[c>>>24]<<24^g[c>>16&255]<<16^g[c>>8&255]<<8^g[c&255],0===a%b&&(c=c<<8^c>>>24^h<<24,h=h<<1^283*(h>>7));d[a]=d[a-b]^c}for(b=0;a;b++,a--)c=d[b&3?a:a-4],e[b]=4>=a||4>b?c:f[0][g[c>>>24]]^f[1][g[c>>16&255]]^f[2][g[c>>8&255]]^f[3][g[c& -255]]}; -sjcl.cipher.aes.prototype={encrypt:function(a){return w(this,a,0)},decrypt:function(a){return w(this,a,1)},l:[[[],[],[],[],[]],[[],[],[],[],[]]],G:function(){var a=this.l[0],b=this.l[1],c=a[4],d=b[4],e,g,f,h=[],l=[],k,m,n,p;for(e=0;0x100>e;e++)l[(h[e]=e<<1^283*(e>>7))^e]=e;for(g=f=0;!c[g];g^=k||1,f=l[f]||1){n=f^f<<1^f<<2^f<<3^f<<4;n=n>>8^n&255^99;c[g]=n;d[n]=g;m=h[e=h[k=h[g]]];p=0x1010101*m^0x10001*e^0x101*k^0x1010100*g;m=0x101*h[n]^0x1010100*n;for(e=0;4>e;e++)a[e][g]=m=m<<24^m>>>8,b[e][n]=p=p<<24^p>>>8}for(e= -0;5>e;e++)a[e]=a[e].slice(0),b[e]=b[e].slice(0)}}; -function w(a,b,c){4!==b.length&&q(new sjcl.exception.invalid("invalid aes block size"));var d=a.b[c],e=b[0]^d[0],g=b[c?3:1]^d[1],f=b[2]^d[2];b=b[c?1:3]^d[3];var h,l,k,m=d.length/4-2,n,p=4,t=[0,0,0,0];h=a.l[c];a=h[0];var r=h[1],v=h[2],y=h[3],z=h[4];for(n=0;n>>24]^r[g>>16&255]^v[f>>8&255]^y[b&255]^d[p],l=a[g>>>24]^r[f>>16&255]^v[b>>8&255]^y[e&255]^d[p+1],k=a[f>>>24]^r[b>>16&255]^v[e>>8&255]^y[g&255]^d[p+2],b=a[b>>>24]^r[e>>16&255]^v[g>>8&255]^y[f&255]^d[p+3],p+=4,e=h,g=l,f=k;for(n=0;4> -n;n++)t[c?3&-n:n]=z[e>>>24]<<24^z[g>>16&255]<<16^z[f>>8&255]<<8^z[b&255]^d[p++],h=e,e=g,g=f,f=b,b=h;return t} -sjcl.bitArray={bitSlice:function(a,b,c){a=sjcl.bitArray.R(a.slice(b/32),32-(b&31)).slice(1);return c===s?a:sjcl.bitArray.clamp(a,c-b)},extract:function(a,b,c){var d=Math.floor(-b-c&31);return((b+c-1^b)&-32?a[b/32|0]<<32-d^a[b/32+1|0]>>>d:a[b/32|0]>>>d)&(1<>b-1,1));return a},partial:function(a,b,c){return 32===a?b:(c?b|0:b<<32-a)+0x10000000000*a},getPartial:function(a){return Math.round(a/0x10000000000)||32},equal:function(a,b){if(sjcl.bitArray.bitLength(a)!==sjcl.bitArray.bitLength(b))return u;var c=0,d;for(d=0;d>>b),c=a[e]<<32-b;e=a.length?a[a.length-1]:0;a=sjcl.bitArray.getPartial(e);d.push(sjcl.bitArray.partial(b+a&31,32>>24|c>>>8&0xff00|(c&0xff00)<<8|c<<24;return a}}; -sjcl.codec.utf8String={fromBits:function(a){var b="",c=sjcl.bitArray.bitLength(a),d,e;for(d=0;d>>24),e<<=8;return decodeURIComponent(escape(b))},toBits:function(a){a=unescape(encodeURIComponent(a));var b=[],c,d=0;for(c=0;c>>f)>>>e),fm){if(!b)try{return sjcl.codec.base32hex.toBits(a)}catch(p){}q(new sjcl.exception.invalid("this isn't "+n+"!"))}h>e?(h-=e,g.push(k^m>>>h),k=m<>>e)>>>26),6>e?(f=a[c]<<6-e,e+=26,c++):(f<<=6,e-=6);for(;d.length&3&&!b;)d+="=";return d},toBits:function(a,b){a=a.replace(/\s|=/g,"");var c=[],d,e=0,g=sjcl.codec.base64.p,f=0,h;b&&(g=g.substr(0,62)+"-_");for(d=0;dh&&q(new sjcl.exception.invalid("this isn't base64!")),26>>e),f=h<<32-e):(e+=6,f^=h<<32-e);e&56&&c.push(sjcl.bitArray.partial(e&56,f,1));return c}};sjcl.codec.base64url={fromBits:function(a){return sjcl.codec.base64.fromBits(a,1,1)},toBits:function(a){return sjcl.codec.base64.toBits(a,1)}};sjcl.hash.sha256=function(a){this.b[0]||this.G();a?(this.s=a.s.slice(0),this.o=a.o.slice(0),this.i=a.i):this.reset()};sjcl.hash.sha256.hash=function(a){return(new sjcl.hash.sha256).update(a).finalize()}; -sjcl.hash.sha256.prototype={blockSize:512,reset:function(){this.s=this.P.slice(0);this.o=[];this.i=0;return this},update:function(a){"string"===typeof a&&(a=sjcl.codec.utf8String.toBits(a));var b,c=this.o=sjcl.bitArray.concat(this.o,a);b=this.i;a=this.i=b+sjcl.bitArray.bitLength(a);for(b=512+b&-512;b<=a;b+=512)x(this,c.splice(0,16));return this},finalize:function(){var a,b=this.o,c=this.s,b=sjcl.bitArray.concat(b,[sjcl.bitArray.partial(1,1)]);for(a=b.length+2;a&15;a++)b.push(0);b.push(Math.floor(this.i/ -4294967296));for(b.push(this.i|0);b.length;)x(this,b.splice(0,16));this.reset();return c},P:[],b:[],G:function(){function a(a){return 0x100000000*(a-Math.floor(a))|0}var b=0,c=2,d;a:for(;64>b;c++){for(d=2;d*d<=c;d++)if(0===c%d)continue a;8>b&&(this.P[b]=a(Math.pow(c,0.5)));this.b[b]=a(Math.pow(c,1/3));b++}}}; -function x(a,b){var c,d,e,g=b.slice(0),f=a.s,h=a.b,l=f[0],k=f[1],m=f[2],n=f[3],p=f[4],t=f[5],r=f[6],v=f[7];for(c=0;64>c;c++)16>c?d=g[c]:(d=g[c+1&15],e=g[c+14&15],d=g[c&15]=(d>>>7^d>>>18^d>>>3^d<<25^d<<14)+(e>>>17^e>>>19^e>>>10^e<<15^e<<13)+g[c&15]+g[c+9&15]|0),d=d+v+(p>>>6^p>>>11^p>>>25^p<<26^p<<21^p<<7)+(r^p&(t^r))+h[c],v=r,r=t,t=p,p=n+d|0,n=m,m=k,k=l,l=d+(k&m^n&(k^m))+(k>>>2^k>>>13^k>>>22^k<<30^k<<19^k<<10)|0;f[0]=f[0]+l|0;f[1]=f[1]+k|0;f[2]=f[2]+m|0;f[3]=f[3]+n|0;f[4]=f[4]+p|0;f[5]=f[5]+t|0;f[6]= -f[6]+r|0;f[7]=f[7]+v|0} -sjcl.mode.ccm={name:"ccm",t:[],listenProgress:function(a){sjcl.mode.ccm.t.push(a)},unListenProgress:function(a){a=sjcl.mode.ccm.t.indexOf(a);-1l&&q(new sjcl.exception.invalid("ccm: iv must be at least 7 bytes"));for(g=2;4>g&&k>>>8*g;g++);g<15-l&&(g=15-l);c=h.clamp(c,8*(15- -g));b=sjcl.mode.ccm.M(a,b,c,d,e,g);f=sjcl.mode.ccm.q(a,f,c,b,e,g);return h.concat(f.data,f.tag)},decrypt:function(a,b,c,d,e){e=e||64;d=d||[];var g=sjcl.bitArray,f=g.bitLength(c)/8,h=g.bitLength(b),l=g.clamp(b,h-e),k=g.bitSlice(b,h-e),h=(h-e)/8;7>f&&q(new sjcl.exception.invalid("ccm: iv must be at least 7 bytes"));for(b=2;4>b&&h>>>8*b;b++);b<15-f&&(b=15-f);c=g.clamp(c,8*(15-b));l=sjcl.mode.ccm.q(a,l,c,k,e,b);a=sjcl.mode.ccm.M(a,l.data,c,d,e,b);g.equal(l.tag,a)||q(new sjcl.exception.corrupt("ccm: tag doesn't match")); -return l.data},ea:function(a,b,c,d,e,g){var f=[],h=sjcl.bitArray,l=h.g;d=[h.partial(8,(b.length?64:0)|d-2<<2|g-1)];d=h.concat(d,c);d[3]|=e;d=a.encrypt(d);if(b.length){c=h.bitLength(b)/8;65279>=c?f=[h.partial(16,c)]:0xffffffff>=c&&(f=h.concat([h.partial(16,65534)],[c]));f=h.concat(f,b);for(b=0;be||16m&&(sjcl.mode.ccm.X(f/ -l),m+=n),c[3]++,e=a.encrypt(c),b[f]^=e[0],b[f+1]^=e[1],b[f+2]^=e[2],b[f+3]^=e[3];return{tag:d,data:h.clamp(b,k)}}}; -sjcl.mode.ocb2={name:"ocb2",encrypt:function(a,b,c,d,e,g){128!==sjcl.bitArray.bitLength(c)&&q(new sjcl.exception.invalid("ocb iv must be 128 bits"));var f,h=sjcl.mode.ocb2.J,l=sjcl.bitArray,k=l.g,m=[0,0,0,0];c=h(a.encrypt(c));var n,p=[];d=d||[];e=e||64;for(f=0;f+4e.bitLength(c)&&(h=g(h,d(h)),c=e.concat(c,[-2147483648,0,0,0]));f=g(f,c);return a.encrypt(g(d(g(h, -d(h))),f))},J:function(a){return[a[0]<<1^a[1]>>>31,a[1]<<1^a[2]>>>31,a[2]<<1^a[3]>>>31,a[3]<<1^135*(a[0]>>>31)]}}; -sjcl.mode.gcm={name:"gcm",encrypt:function(a,b,c,d,e){var g=b.slice(0);b=sjcl.bitArray;d=d||[];a=sjcl.mode.gcm.q(!0,a,g,d,c,e||128);return b.concat(a.data,a.tag)},decrypt:function(a,b,c,d,e){var g=b.slice(0),f=sjcl.bitArray,h=f.bitLength(g);e=e||128;d=d||[];e<=h?(b=f.bitSlice(g,h-e),g=f.bitSlice(g,0,h-e)):(b=g,g=[]);a=sjcl.mode.gcm.q(u,a,g,d,c,e);f.equal(a.tag,b)||q(new sjcl.exception.corrupt("gcm: tag doesn't match"));return a.data},ba:function(a,b){var c,d,e,g,f,h=sjcl.bitArray.g;e=[0,0,0,0];g= -b.slice(0);for(c=0;128>c;c++){(d=0!==(a[Math.floor(c/32)]&1<<31-c%32))&&(e=h(e,g));f=0!==(g[3]&1);for(d=3;0>>1|(g[d-1]&1)<<31;g[0]>>>=1;f&&(g[0]^=-0x1f000000)}return e},h:function(a,b,c){var d,e=c.length;b=b.slice(0);for(d=0;de&&(a=b.hash(a));for(d=0;dd||0>c)&&q(sjcl.exception.invalid("invalid params to pbkdf2"));"string"===typeof a&&(a=sjcl.codec.utf8String.toBits(a));"string"===typeof b&&(b=sjcl.codec.utf8String.toBits(b));e=e||sjcl.misc.hmac;a=new e(a);var g,f,h,l,k=[],m=sjcl.bitArray;for(l=1;32*k.length<(d||1);l++){e=g=a.encrypt(m.concat(b,[l]));for(f=1;ff;f++)e.push(0x100000000*Math.random()|0);for(f=0;f=1<this.k&&(this.k=g);this.H++; -this.b=sjcl.hash.sha256.hash(this.b.concat(e));this.C=new sjcl.cipher.aes(this.b);for(d=0;4>d&&!(this.f[d]=this.f[d]+1|0,this.f[d]);d++);}for(d=0;d>>=1;this.c[f].update([d,this.F++,2,b,g,a.length].concat(a))}break;case "string":b===s&&(b=a.length);this.c[f].update([d,this.F++,3,b,g,a.length]);this.c[f].update(a);break;default:l=1}l&&q(new sjcl.exception.bug("random: addEntropy only supports number, array of numbers or string"));this.j[f]+=b;this.d+=b;h===this.m&&(this.isReady()!==this.m&&C("seeded",Math.max(this.k,this.d)),C("progress",this.getProgress()))},isReady:function(a){a=this.K[a!==s?a:this.D];return this.k&&this.k>=a?this.j[0]>this.T&& -(new Date).valueOf()>this.Q?this.A|this.w:this.w:this.d>=a?this.A|this.m:this.m},getProgress:function(a){a=this.K[a?a:this.D];return this.k>=a?1:this.d>a?1:this.d/a},startCollectors:function(){this.r||(this.a={loadTimeCollector:D(this,this.da),mouseCollector:D(this,this.fa),keyboardCollector:D(this,this.ca),accelerometerCollector:D(this,this.W),touchCollector:D(this,this.ha)},window.addEventListener?(window.addEventListener("load",this.a.loadTimeCollector,u),window.addEventListener("mousemove",this.a.mouseCollector, -u),window.addEventListener("keypress",this.a.keyboardCollector,u),window.addEventListener("devicemotion",this.a.accelerometerCollector,u),window.addEventListener("touchmove",this.a.touchCollector,u)):document.attachEvent?(document.attachEvent("onload",this.a.loadTimeCollector),document.attachEvent("onmousemove",this.a.mouseCollector),document.attachEvent("keypress",this.a.keyboardCollector)):q(new sjcl.exception.bug("can't attach event")),this.r=!0)},stopCollectors:function(){this.r&&(window.removeEventListener? -(window.removeEventListener("load",this.a.loadTimeCollector,u),window.removeEventListener("mousemove",this.a.mouseCollector,u),window.removeEventListener("keypress",this.a.keyboardCollector,u),window.removeEventListener("devicemotion",this.a.accelerometerCollector,u),window.removeEventListener("touchmove",this.a.touchCollector,u)):document.detachEvent&&(document.detachEvent("onload",this.a.loadTimeCollector),document.detachEvent("onmousemove",this.a.mouseCollector),document.detachEvent("keypress", -this.a.keyboardCollector)),this.r=u)},addEventListener:function(a,b){this.B[a][this.Y++]=b},removeEventListener:function(a,b){var c,d,e=this.B[a],g=[];for(d in e)e.hasOwnProperty(d)&&e[d]===b&&g.push(d);for(c=0;cb&&!(a.f[b]=a.f[b]+1|0,a.f[b]);b++);return a.C.encrypt(a.f)}function D(a,b){return function(){b.apply(a,arguments)}}sjcl.random=new sjcl.prng(6); -a:try{var F,G,H,I;if(I="undefined"!==typeof module){var J;if(J=module.exports){var K;try{K=require("crypto")}catch(L){K=null}J=(G=K)&&G.randomBytes}I=J}if(I)F=G.randomBytes(128),F=new Uint32Array((new Uint8Array(F)).buffer),sjcl.random.addEntropy(F,1024,"crypto['randomBytes']");else if("undefined"!==typeof window&&"undefined"!==typeof Uint32Array){H=new Uint32Array(32);if(window.crypto&&window.crypto.getRandomValues)window.crypto.getRandomValues(H);else if(window.msCrypto&&window.msCrypto.getRandomValues)window.msCrypto.getRandomValues(H); -else break a;sjcl.random.addEntropy(H,1024,"crypto['getRandomValues']")}}catch(M){"undefined"!==typeof window&&window.console&&(console.log("There was an error collecting entropy from the browser:"),console.log(M))} -sjcl.json={defaults:{v:1,iter:1E3,ks:128,ts:64,mode:"ccm",adata:"",cipher:"aes"},aa:function(a,b,c,d){c=c||{};d=d||{};var e=sjcl.json,g=e.e({iv:sjcl.random.randomWords(4,0)},e.defaults),f;e.e(g,c);c=g.adata;"string"===typeof g.salt&&(g.salt=sjcl.codec.base64.toBits(g.salt));"string"===typeof g.iv&&(g.iv=sjcl.codec.base64.toBits(g.iv));(!sjcl.mode[g.mode]||!sjcl.cipher[g.cipher]||"string"===typeof a&&100>=g.iter||64!==g.ts&&96!==g.ts&&128!==g.ts||128!==g.ks&&192!==g.ks&&0x100!==g.ks||2>g.iv.length|| -4=b.iter||64!==b.ts&&96!==b.ts&&128!==b.ts||128!==b.ks&&192!==b.ks&&0x100!==b.ks||!b.iv||2>b.iv.length||4>>24]<<24^f[c>>16&255]<<16^f[c>>8&255]<<8^f[c&255],0===a%b&&(c=c<<8^c>>>24^h<<24,h=h<<1^283*(h>>7));d[a]=d[a-b]^c}for(b=0;a;b++,a--)c=d[b&3?a:a-4],e[b]=4>=a||4>b?c:g[0][f[c>>>24]]^g[1][f[c>>16&255]]^g[2][f[c>>8&255]]^g[3][f[c& +255]]}; +sjcl.cipher.aes.prototype={encrypt:function(a){return t(this,a,0)},decrypt:function(a){return t(this,a,1)},s:[[[],[],[],[],[]],[[],[],[],[],[]]],O:function(){var a=this.s[0],b=this.s[1],c=a[4],d=b[4],e,f,g,h=[],k=[],l,n,m,p;for(e=0;0x100>e;e++)k[(h[e]=e<<1^283*(e>>7))^e]=e;for(f=g=0;!c[f];f^=l||1,g=k[g]||1)for(m=g^g<<1^g<<2^g<<3^g<<4,m=m>>8^m&255^99,c[f]=m,d[m]=f,n=h[e=h[l=h[f]]],p=0x1010101*n^0x10001*e^0x101*l^0x1010100*f,n=0x101*h[m]^0x1010100*m,e=0;4>e;e++)a[e][f]=n=n<<24^n>>>8,b[e][m]=p=p<<24^p>>>8;for(e= +0;5>e;e++)a[e]=a[e].slice(0),b[e]=b[e].slice(0)}}; +function t(a,b,c){if(4!==b.length)throw new sjcl.exception.invalid("invalid aes block size");var d=a.b[c],e=b[0]^d[0],f=b[c?3:1]^d[1],g=b[2]^d[2];b=b[c?1:3]^d[3];var h,k,l,n=d.length/4-2,m,p=4,r=[0,0,0,0];h=a.s[c];a=h[0];var q=h[1],v=h[2],w=h[3],x=h[4];for(m=0;m>>24]^q[f>>16&255]^v[g>>8&255]^w[b&255]^d[p],k=a[f>>>24]^q[g>>16&255]^v[b>>8&255]^w[e&255]^d[p+1],l=a[g>>>24]^q[b>>16&255]^v[e>>8&255]^w[f&255]^d[p+2],b=a[b>>>24]^q[e>>16&255]^v[f>>8&255]^w[g&255]^d[p+3],p+=4,e=h,f=k,g=l;for(m= +0;4>m;m++)r[c?3&-m:m]=x[e>>>24]<<24^x[f>>16&255]<<16^x[g>>8&255]<<8^x[b&255]^d[p++],h=e,e=f,f=g,g=b,b=h;return r} +sjcl.bitArray={bitSlice:function(a,b,c){a=sjcl.bitArray.$(a.slice(b/32),32-(b&31)).slice(1);return void 0===c?a:sjcl.bitArray.clamp(a,c-b)},extract:function(a,b,c){var d=Math.floor(-b-c&31);return((b+c-1^b)&-32?a[b/32|0]<<32-d^a[b/32+1|0]>>>d:a[b/32|0]>>>d)&(1<>b-1,1));return a},partial:function(a,b,c){return 32===a?b:(c?b|0:b<<32-a)+0x10000000000*a},getPartial:function(a){return Math.round(a/0x10000000000)||32},equal:function(a,b){if(sjcl.bitArray.bitLength(a)!==sjcl.bitArray.bitLength(b))return!1;var c=0,d;for(d=0;d>>b),c=a[e]<<32-b;e=a.length?a[a.length-1]:0;a=sjcl.bitArray.getPartial(e);d.push(sjcl.bitArray.partial(b+a&31,32>>24|c>>>8&0xff00|(c&0xff00)<<8|c<<24;return a}}; +sjcl.codec.utf8String={fromBits:function(a){var b="",c=sjcl.bitArray.bitLength(a),d,e;for(d=0;d>>24),e<<=8;return decodeURIComponent(escape(b))},toBits:function(a){a=unescape(encodeURIComponent(a));var b=[],c,d=0;for(c=0;c>>g)>>>e),gn){if(!b)try{return sjcl.codec.base32hex.toBits(a)}catch(p){}throw new sjcl.exception.invalid("this isn't "+m+"!");}h>e?(h-=e,f.push(l^n>>>h),l=n<>>e)>>>26),6>e?(g=a[c]<<6-e,e+=26,c++):(g<<=6,e-=6);for(;d.length&3&&!b;)d+="=";return d},toBits:function(a,b){a=a.replace(/\s|=/g,"");var c=[],d,e=0,f=sjcl.codec.base64.B,g=0,h;b&&(f=f.substr(0,62)+"-_");for(d=0;dh)throw new sjcl.exception.invalid("this isn't base64!");26>>e),g=h<<32-e):(e+=6,g^=h<<32-e)}e&56&&c.push(sjcl.bitArray.partial(e&56,g,1));return c}};sjcl.codec.base64url={fromBits:function(a){return sjcl.codec.base64.fromBits(a,1,1)},toBits:function(a){return sjcl.codec.base64.toBits(a,1)}};sjcl.hash.sha256=function(a){this.b[0]||this.O();a?(this.F=a.F.slice(0),this.A=a.A.slice(0),this.l=a.l):this.reset()};sjcl.hash.sha256.hash=function(a){return(new sjcl.hash.sha256).update(a).finalize()}; +sjcl.hash.sha256.prototype={blockSize:512,reset:function(){this.F=this.Y.slice(0);this.A=[];this.l=0;return this},update:function(a){"string"===typeof a&&(a=sjcl.codec.utf8String.toBits(a));var b,c=this.A=sjcl.bitArray.concat(this.A,a);b=this.l;a=this.l=b+sjcl.bitArray.bitLength(a);if(0x1fffffffffffffb;c++){e=!0;for(d=2;d*d<=c;d++)if(0===c%d){e= +!1;break}e&&(8>b&&(this.Y[b]=a(Math.pow(c,.5))),this.b[b]=a(Math.pow(c,1/3)),b++)}}}; +function u(a,b){var c,d,e,f=a.F,g=a.b,h=f[0],k=f[1],l=f[2],n=f[3],m=f[4],p=f[5],r=f[6],q=f[7];for(c=0;64>c;c++)16>c?d=b[c]:(d=b[c+1&15],e=b[c+14&15],d=b[c&15]=(d>>>7^d>>>18^d>>>3^d<<25^d<<14)+(e>>>17^e>>>19^e>>>10^e<<15^e<<13)+b[c&15]+b[c+9&15]|0),d=d+q+(m>>>6^m>>>11^m>>>25^m<<26^m<<21^m<<7)+(r^m&(p^r))+g[c],q=r,r=p,p=m,m=n+d|0,n=l,l=k,k=h,h=d+(k&l^n&(k^l))+(k>>>2^k>>>13^k>>>22^k<<30^k<<19^k<<10)|0;f[0]=f[0]+h|0;f[1]=f[1]+k|0;f[2]=f[2]+l|0;f[3]=f[3]+n|0;f[4]=f[4]+m|0;f[5]=f[5]+p|0;f[6]=f[6]+r|0;f[7]= +f[7]+q|0} +sjcl.mode.ccm={name:"ccm",G:[],listenProgress:function(a){sjcl.mode.ccm.G.push(a)},unListenProgress:function(a){a=sjcl.mode.ccm.G.indexOf(a);-1k)throw new sjcl.exception.invalid("ccm: iv must be at least 7 bytes");for(f=2;4>f&&l>>>8*f;f++);f<15-k&&(f=15-k);c=h.clamp(c, +8*(15-f));b=sjcl.mode.ccm.V(a,b,c,d,e,f);g=sjcl.mode.ccm.C(a,g,c,b,e,f);return h.concat(g.data,g.tag)},decrypt:function(a,b,c,d,e){e=e||64;d=d||[];var f=sjcl.bitArray,g=f.bitLength(c)/8,h=f.bitLength(b),k=f.clamp(b,h-e),l=f.bitSlice(b,h-e),h=(h-e)/8;if(7>g)throw new sjcl.exception.invalid("ccm: iv must be at least 7 bytes");for(b=2;4>b&&h>>>8*b;b++);b<15-g&&(b=15-g);c=f.clamp(c,8*(15-b));k=sjcl.mode.ccm.C(a,k,c,l,e,b);a=sjcl.mode.ccm.V(a,k.data,c,d,e,b);if(!f.equal(k.tag,a))throw new sjcl.exception.corrupt("ccm: tag doesn't match"); +return k.data},na:function(a,b,c,d,e,f){var g=[],h=sjcl.bitArray,k=h.i;d=[h.partial(8,(b.length?64:0)|d-2<<2|f-1)];d=h.concat(d,c);d[3]|=e;d=a.encrypt(d);if(b.length)for(c=h.bitLength(b)/8,65279>=c?g=[h.partial(16,c)]:0xffffffff>=c&&(g=h.concat([h.partial(16,65534)],[c])),g=h.concat(g,b),b=0;be||16n&&(sjcl.mode.ccm.fa(g/ +k),n+=m),c[3]++,e=a.encrypt(c),b[g]^=e[0],b[g+1]^=e[1],b[g+2]^=e[2],b[g+3]^=e[3];return{tag:d,data:h.clamp(b,l)}}}; +sjcl.mode.ocb2={name:"ocb2",encrypt:function(a,b,c,d,e,f){if(128!==sjcl.bitArray.bitLength(c))throw new sjcl.exception.invalid("ocb iv must be 128 bits");var g,h=sjcl.mode.ocb2.S,k=sjcl.bitArray,l=k.i,n=[0,0,0,0];c=h(a.encrypt(c));var m,p=[];d=d||[];e=e||64;for(g=0;g+4e.bitLength(c)&&(h=f(h,d(h)),c=e.concat(c,[-2147483648,0,0,0]));g=f(g,c); +return a.encrypt(f(d(f(h,d(h))),g))},S:function(a){return[a[0]<<1^a[1]>>>31,a[1]<<1^a[2]>>>31,a[2]<<1^a[3]>>>31,a[3]<<1^135*(a[0]>>>31)]}}; +sjcl.mode.gcm={name:"gcm",encrypt:function(a,b,c,d,e){var f=b.slice(0);b=sjcl.bitArray;d=d||[];a=sjcl.mode.gcm.C(!0,a,f,d,c,e||128);return b.concat(a.data,a.tag)},decrypt:function(a,b,c,d,e){var f=b.slice(0),g=sjcl.bitArray,h=g.bitLength(f);e=e||128;d=d||[];e<=h?(b=g.bitSlice(f,h-e),f=g.bitSlice(f,0,h-e)):(b=f,f=[]);a=sjcl.mode.gcm.C(!1,a,f,d,c,e);if(!g.equal(a.tag,b))throw new sjcl.exception.corrupt("gcm: tag doesn't match");return a.data},ka:function(a,b){var c,d,e,f,g,h=sjcl.bitArray.i;e=[0,0, +0,0];f=b.slice(0);for(c=0;128>c;c++){(d=0!==(a[Math.floor(c/32)]&1<<31-c%32))&&(e=h(e,f));g=0!==(f[3]&1);for(d=3;0>>1|(f[d-1]&1)<<31;f[0]>>>=1;g&&(f[0]^=-0x1f000000)}return e},j:function(a,b,c){var d,e=c.length;b=b.slice(0);for(d=0;de&&(a=b.hash(a));for(d=0;dd||0>c)throw new sjcl.exception.invalid("invalid params to pbkdf2");"string"===typeof a&&(a=sjcl.codec.utf8String.toBits(a));"string"===typeof b&&(b=sjcl.codec.utf8String.toBits(b));e=e||sjcl.misc.hmac;a=new e(a);var f,g,h,k,l=[],n=sjcl.bitArray;for(k=1;32*l.length<(d||1);k++){e=f=a.encrypt(n.concat(b,[k]));for(g=1;gg;g++)e.push(0x100000000*Math.random()|0);for(g=0;g=1<this.o&&(this.o= +f);this.P++;this.b=sjcl.hash.sha256.hash(this.b.concat(e));this.L=new sjcl.cipher.aes(this.b);for(d=0;4>d&&(this.h[d]=this.h[d]+1|0,!this.h[d]);d++);}for(d=0;d>>1;this.c[g].update([d,this.N++,2,b,f,a.length].concat(a))}break;case "string":void 0===b&&(b=a.length);this.c[g].update([d,this.N++,3,b,f,a.length]);this.c[g].update(a);break;default:k=1}if(k)throw new sjcl.exception.bug("random: addEntropy only supports number, array of numbers or string");this.m[g]+=b;this.f+=b;h===this.u&&(this.isReady()!==this.u&&A("seeded",Math.max(this.o,this.f)),A("progress",this.getProgress()))}, +isReady:function(a){a=this.T[void 0!==a?a:this.M];return this.o&&this.o>=a?this.m[0]>this.ba&&(new Date).valueOf()>this.Z?this.J|this.I:this.I:this.f>=a?this.J|this.u:this.u},getProgress:function(a){a=this.T[a?a:this.M];return this.o>=a?1:this.f>a?1:this.f/a},startCollectors:function(){if(!this.D){this.a={loadTimeCollector:B(this,this.ma),mouseCollector:B(this,this.oa),keyboardCollector:B(this,this.la),accelerometerCollector:B(this,this.ea),touchCollector:B(this,this.qa)};if(window.addEventListener)window.addEventListener("load", +this.a.loadTimeCollector,!1),window.addEventListener("mousemove",this.a.mouseCollector,!1),window.addEventListener("keypress",this.a.keyboardCollector,!1),window.addEventListener("devicemotion",this.a.accelerometerCollector,!1),window.addEventListener("touchmove",this.a.touchCollector,!1);else if(document.attachEvent)document.attachEvent("onload",this.a.loadTimeCollector),document.attachEvent("onmousemove",this.a.mouseCollector),document.attachEvent("keypress",this.a.keyboardCollector);else throw new sjcl.exception.bug("can't attach event"); +this.D=!0}},stopCollectors:function(){this.D&&(window.removeEventListener?(window.removeEventListener("load",this.a.loadTimeCollector,!1),window.removeEventListener("mousemove",this.a.mouseCollector,!1),window.removeEventListener("keypress",this.a.keyboardCollector,!1),window.removeEventListener("devicemotion",this.a.accelerometerCollector,!1),window.removeEventListener("touchmove",this.a.touchCollector,!1)):document.detachEvent&&(document.detachEvent("onload",this.a.loadTimeCollector),document.detachEvent("onmousemove", +this.a.mouseCollector),document.detachEvent("keypress",this.a.keyboardCollector)),this.D=!1)},addEventListener:function(a,b){this.K[a][this.ga++]=b},removeEventListener:function(a,b){var c,d,e=this.K[a],f=[];for(d in e)e.hasOwnProperty(d)&&e[d]===b&&f.push(d);for(c=0;cb&&(a.h[b]=a.h[b]+1|0,!a.h[b]);b++);return a.L.encrypt(a.h)} +function B(a,b){return function(){b.apply(a,arguments)}}sjcl.random=new sjcl.prng(6); +a:try{var D,E,F,G;if(G="undefined"!==typeof module&&module.exports){var H;try{H=require("crypto")}catch(a){H=null}G=E=H}if(G&&E.randomBytes)D=E.randomBytes(128),D=new Uint32Array((new Uint8Array(D)).buffer),sjcl.random.addEntropy(D,1024,"crypto['randomBytes']");else if("undefined"!==typeof window&&"undefined"!==typeof Uint32Array){F=new Uint32Array(32);if(window.crypto&&window.crypto.getRandomValues)window.crypto.getRandomValues(F);else if(window.msCrypto&&window.msCrypto.getRandomValues)window.msCrypto.getRandomValues(F); +else break a;sjcl.random.addEntropy(F,1024,"crypto['getRandomValues']")}}catch(a){"undefined"!==typeof window&&window.console&&(console.log("There was an error collecting entropy from the browser:"),console.log(a))} +sjcl.json={defaults:{v:1,iter:1E4,ks:128,ts:64,mode:"ccm",adata:"",cipher:"aes"},ja:function(a,b,c,d){c=c||{};d=d||{};var e=sjcl.json,f=e.g({iv:sjcl.random.randomWords(4,0)},e.defaults),g;e.g(f,c);c=f.adata;"string"===typeof f.salt&&(f.salt=sjcl.codec.base64.toBits(f.salt));"string"===typeof f.iv&&(f.iv=sjcl.codec.base64.toBits(f.iv));if(!sjcl.mode[f.mode]||!sjcl.cipher[f.cipher]||"string"===typeof a&&100>=f.iter||64!==f.ts&&96!==f.ts&&128!==f.ts||128!==f.ks&&192!==f.ks&&0x100!==f.ks||2>f.iv.length|| +4=b.iter||64!==b.ts&&96!==b.ts&&128!==b.ts||128!==b.ks&&192!==b.ks&&0x100!==b.ks||!b.iv||2>b.iv.length||4 - + @@ -56,7 +56,7 @@ endif; ?> - + From a005b1128b45730e09b0868d144dc8f9753ef660 Mon Sep 17 00:00:00 2001 From: Nathaniel Olsen Date: Mon, 23 Jan 2017 22:49:03 -0600 Subject: [PATCH 0013/1638] Updated showdown --- js/showdown-1.4.1.js | 1 - js/showdown-1.6.0.js | 2 ++ tpl/bootstrap.php | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) delete mode 100644 js/showdown-1.4.1.js create mode 100644 js/showdown-1.6.0.js diff --git a/js/showdown-1.4.1.js b/js/showdown-1.4.1.js deleted file mode 100644 index a42abe6f..00000000 --- a/js/showdown-1.4.1.js +++ /dev/null @@ -1 +0,0 @@ -(function(){function a(a){"use strict";var b={omitExtraWLInCodeBlocks:{"default":!1,describe:"Omit the default extra whiteline added to code blocks",type:"boolean"},noHeaderId:{"default":!1,describe:"Turn on/off generated header id",type:"boolean"},prefixHeaderId:{"default":!1,describe:"Specify a prefix to generated header ids",type:"string"},headerLevelStart:{"default":!1,describe:"The header blocks level start",type:"integer"},parseImgDimensions:{"default":!1,describe:"Turn on/off image dimension parsing",type:"boolean"},simplifiedAutoLink:{"default":!1,describe:"Turn on/off GFM autolink style",type:"boolean"},literalMidWordUnderscores:{"default":!1,describe:"Parse midword underscores as literal underscores",type:"boolean"},strikethrough:{"default":!1,describe:"Turn on/off strikethrough support",type:"boolean"},tables:{"default":!1,describe:"Turn on/off tables support",type:"boolean"},tablesHeaderId:{"default":!1,describe:"Add an id to table headers",type:"boolean"},ghCodeBlocks:{"default":!0,describe:"Turn on/off GFM fenced code blocks support",type:"boolean"},tasklists:{"default":!1,describe:"Turn on/off GFM tasklist support",type:"boolean"},smoothLivePreview:{"default":!1,describe:"Prevents weird effects in live previews due to incomplete input",type:"boolean"}};if(a===!1)return JSON.parse(JSON.stringify(b));var c={};for(var d in b)b.hasOwnProperty(d)&&(c[d]=b[d]["default"]);return c}function b(a,b){"use strict";var c=b?"Error in "+b+" extension->":"Error in unnamed extension",e={valid:!0,error:""};d.helper.isArray(a)||(a=[a]);for(var f=0;f-1,l=new RegExp(b+"|"+c,"g"+j.replace(/g/g,"")),m=new RegExp(b,j.replace(/g/g,"")),n=[];do for(e=0;g=l.exec(a);)if(m.test(g[0]))e++||(f=l.lastIndex,h=f-g[0].length);else if(e&&!--e){i=g.index+g[0].length;var o={left:{start:h,end:f},match:{start:f,end:g.index},right:{start:g.index,end:i},wholeMatch:{start:h,end:i}};if(n.push(o),!k)return n}while(e&&(l.lastIndex=f));return n};d.helper.matchRecursiveRegExp=function(a,b,c,d){"use strict";for(var e=i(a,b,c,d),f=[],g=0;g0){var l=[];0!==h[0].wholeMatch.start&&l.push(a.slice(0,h[0].wholeMatch.start));for(var m=0;k>m;++m)l.push(b(a.slice(h[m].wholeMatch.start,h[m].wholeMatch.end),a.slice(h[m].match.start,h[m].match.end),a.slice(h[m].left.start,h[m].left.end),a.slice(h[m].right.start,h[m].right.end))),k-1>m&&l.push(a.slice(h[m].wholeMatch.end,h[m+1].wholeMatch.start));h[k-1].wholeMatch.end-1))return a;m=""}else m=c.gUrls[l],d.helper.isUndefined(c.gTitles[l])||(n=c.gTitles[l]);m=d.helper.escapeCharacters(m,"*_",!1);var o='"};return a=a.replace(/(\[((?:\[[^\]]*]|[^\[\]])*)][ ]?(?:\n[ ]*)?\[(.*?)])()()()()/g,e),a=a.replace(/(\[((?:\[[^\]]*]|[^\[\]])*)]\([ \t]*()?[ \t]*((['"])(.*?)\6[ \t]*)?\))/g,e),a=a.replace(/(\[([^\[\]]+)])()()()()()/g,e),a=c.converter._dispatch("anchors.after",a,b,c)}),d.subParser("autoLinks",function(a,b,c){"use strict";function e(a,b){var c=d.subParser("unescapeSpecialChars")(b);return d.subParser("encodeEmailAddress")(c)}a=c.converter._dispatch("autoLinks.before",a,b,c);var f=/\b(((https?|ftp|dict):\/\/|www\.)[^'">\s]+\.[^'">\s]+)(?=\s|$)(?!["<>])/gi,g=/<(((https?|ftp|dict):\/\/|www\.)[^'">\s]+)>/gi,h=/(?:^|[ \n\t])([A-Za-z0-9!#$%&'*+-\/=?^_`\{|}~\.]+@[-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+)(?:$|[ \n\t])/gi,i=/<(?:mailto:)?([-.\w]+@[-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+)>/gi;return a=a.replace(g,'$1'),a=a.replace(i,e),b.simplifiedAutoLink&&(a=a.replace(f,'$1'),a=a.replace(h,e)),a=c.converter._dispatch("autoLinks.after",a,b,c)}),d.subParser("blockGamut",function(a,b,c){"use strict";a=c.converter._dispatch("blockGamut.before",a,b,c),a=d.subParser("blockQuotes")(a,b,c),a=d.subParser("headers")(a,b,c);var e=d.subParser("hashBlock")("
",b,c);return a=a.replace(/^[ ]{0,2}([ ]?\*[ ]?){3,}[ \t]*$/gm,e),a=a.replace(/^[ ]{0,2}([ ]?\-[ ]?){3,}[ \t]*$/gm,e),a=a.replace(/^[ ]{0,2}([ ]?_[ ]?){3,}[ \t]*$/gm,e),a=d.subParser("lists")(a,b,c),a=d.subParser("codeBlocks")(a,b,c),a=d.subParser("tables")(a,b,c),a=d.subParser("hashHTMLBlocks")(a,b,c),a=d.subParser("paragraphs")(a,b,c),a=c.converter._dispatch("blockGamut.after",a,b,c)}),d.subParser("blockQuotes",function(a,b,c){"use strict";return a=c.converter._dispatch("blockQuotes.before",a,b,c),a=a.replace(/((^[ \t]{0,3}>[ \t]?.+\n(.+\n)*\n*)+)/gm,function(a,e){var f=e;return f=f.replace(/^[ \t]*>[ \t]?/gm,"~0"),f=f.replace(/~0/g,""),f=f.replace(/^[ \t]+$/gm,""),f=d.subParser("githubCodeBlocks")(f,b,c),f=d.subParser("blockGamut")(f,b,c),f=f.replace(/(^|\n)/g,"$1 "),f=f.replace(/(\s*
[^\r]+?<\/pre>)/gm,function(a,b){var c=b;return c=c.replace(/^  /gm,"~0"),c=c.replace(/~0/g,"")}),d.subParser("hashBlock")("
\n"+f+"\n
",b,c)}),a=c.converter._dispatch("blockQuotes.after",a,b,c)}),d.subParser("codeBlocks",function(a,b,c){"use strict";a=c.converter._dispatch("codeBlocks.before",a,b,c),a+="~0";var e=/(?:\n\n|^)((?:(?:[ ]{4}|\t).*\n+)+)(\n*[ ]{0,3}[^ \t\n]|(?=~0))/g;return a=a.replace(e,function(a,e,f){var g=e,h=f,i="\n";return g=d.subParser("outdent")(g),g=d.subParser("encodeCode")(g),g=d.subParser("detab")(g),g=g.replace(/^\n+/g,""),g=g.replace(/\n+$/g,""),b.omitExtraWLInCodeBlocks&&(i=""),g="
"+g+i+"
",d.subParser("hashBlock")(g,b,c)+h}),a=a.replace(/~0/,""),a=c.converter._dispatch("codeBlocks.after",a,b,c)}),d.subParser("codeSpans",function(a,b,c){"use strict";return a=c.converter._dispatch("codeSpans.before",a,b,c),a=a.replace(/(^|[^\\])(`+)([^\r]*?[^`])\2(?!`)/gm,function(a,b,c,e){var f=e;return f=f.replace(/^([ \t]*)/g,""),f=f.replace(/[ \t]*$/g,""),f=d.subParser("encodeCode")(f),b+""+f+""}),a=c.converter._dispatch("codeSpans.after",a,b,c)}),d.subParser("detab",function(a){"use strict";return a=a.replace(/\t(?=\t)/g," "),a=a.replace(/\t/g,"~A~B"),a=a.replace(/~B(.+?)~A/g,function(a,b){for(var c=b,d=4-c.length%4,e=0;d>e;e++)c+=" ";return c}),a=a.replace(/~A/g," "),a=a.replace(/~B/g,"")}),d.subParser("encodeAmpsAndAngles",function(a){"use strict";return a=a.replace(/&(?!#?[xX]?(?:[0-9a-fA-F]+|\w+);)/g,"&"),a=a.replace(/<(?![a-z\/?\$!])/gi,"<")}),d.subParser("encodeBackslashEscapes",function(a){"use strict";return a=a.replace(/\\(\\)/g,d.helper.escapeCharactersCallback),a=a.replace(/\\([`*_{}\[\]()>#+-.!])/g,d.helper.escapeCharactersCallback)}),d.subParser("encodeCode",function(a){"use strict";return a=a.replace(/&/g,"&"),a=a.replace(//g,">"),a=d.helper.escapeCharacters(a,"*_{}[]\\",!1)}),d.subParser("encodeEmailAddress",function(a){"use strict";var b=[function(a){return"&#"+a.charCodeAt(0)+";"},function(a){return"&#x"+a.charCodeAt(0).toString(16)+";"},function(a){return a}];return a="mailto:"+a,a=a.replace(/./g,function(a){if("@"===a)a=b[Math.floor(2*Math.random())](a);else if(":"!==a){var c=Math.random();a=c>.9?b[2](a):c>.45?b[1](a):b[0](a)}return a}),a=''+a+"",a=a.replace(/">.+:/g,'">')}),d.subParser("escapeSpecialCharsWithinTagAttributes",function(a){"use strict";var b=/(<[a-z\/!$]("[^"]*"|'[^']*'|[^'">])*>|)/gi;return a=a.replace(b,function(a){var b=a.replace(/(.)<\/?code>(?=.)/g,"$1`");return b=d.helper.escapeCharacters(b,"\\`*_",!1)})}),d.subParser("githubCodeBlocks",function(a,b,c){"use strict";return b.ghCodeBlocks?(a=c.converter._dispatch("githubCodeBlocks.before",a,b,c),a+="~0",a=a.replace(/(?:^|\n)```(.*)\n([\s\S]*?)\n```/g,function(a,e,f){var g=b.omitExtraWLInCodeBlocks?"":"\n";return f=d.subParser("encodeCode")(f),f=d.subParser("detab")(f),f=f.replace(/^\n+/g,""),f=f.replace(/\n+$/g,""),f="
"+f+g+"
",f=d.subParser("hashBlock")(f,b,c),"\n\n~G"+(c.ghCodeBlocks.push({text:a,codeblock:f})-1)+"G\n\n"}),a=a.replace(/~0/,""),c.converter._dispatch("githubCodeBlocks.after",a,b,c)):a}),d.subParser("hashBlock",function(a,b,c){"use strict";return a=a.replace(/(^\n+|\n+$)/g,""),"\n\n~K"+(c.gHtmlBlocks.push(a)-1)+"K\n\n"}),d.subParser("hashElement",function(a,b,c){"use strict";return function(a,b){var d=b;return d=d.replace(/\n\n/g,"\n"),d=d.replace(/^\n/,""),d=d.replace(/\n+$/g,""),d="\n\n~K"+(c.gHtmlBlocks.push(d)-1)+"K\n\n"}}),d.subParser("hashHTMLBlocks",function(a,b,c){"use strict";for(var e=["pre","div","h1","h2","h3","h4","h5","h6","blockquote","table","dl","ol","ul","script","noscript","form","fieldset","iframe","math","style","section","header","footer","nav","article","aside","address","audio","canvas","figure","hgroup","output","video","p"],f=function(a,b,d,e){var f=a;return-1!==d.search(/\bmarkdown\b/)&&(f=d+c.converter.makeHtml(b)+e),"\n\n~K"+(c.gHtmlBlocks.push(f)-1)+"K\n\n"},g=0;g]*>","","gim");return a=a.replace(/(\n[ ]{0,3}(<(hr)\b([^<>])*?\/?>)[ \t]*(?=\n{2,}))/g,d.subParser("hashElement")(a,b,c)),a=a.replace(/([ \t]*(?=\n{2,}))/g,d.subParser("hashElement")(a,b,c)),a=a.replace(/(?:\n\n)([ ]{0,3}(?:<([?%])[^\r]*?\2>)[ \t]*(?=\n{2,}))/g,d.subParser("hashElement")(a,b,c))}),d.subParser("hashHTMLSpans",function(a,b,c){"use strict";for(var e=d.helper.matchRecursiveRegExp(a,"]*>","","gi"),f=0;f]*>\\s*]*>","^(?: |\\t){0,3}\\s*
","gim")}),d.subParser("headers",function(a,b,c){"use strict";function e(a){var b,e=a.replace(/[^\w]/g,"").toLowerCase();return c.hashLinkCounts[e]?b=e+"-"+c.hashLinkCounts[e]++:(b=e,c.hashLinkCounts[e]=1),f===!0&&(f="section"),d.helper.isString(f)?f+b:b}a=c.converter._dispatch("headers.before",a,b,c);var f=b.prefixHeaderId,g=isNaN(parseInt(b.headerLevelStart))?1:parseInt(b.headerLevelStart),h=b.smoothLivePreview?/^(.+)[ \t]*\n={2,}[ \t]*\n+/gm:/^(.+)[ \t]*\n=+[ \t]*\n+/gm,i=b.smoothLivePreview?/^(.+)[ \t]*\n-{2,}[ \t]*\n+/gm:/^(.+)[ \t]*\n-+[ \t]*\n+/gm;return a=a.replace(h,function(a,f){var h=d.subParser("spanGamut")(f,b,c),i=b.noHeaderId?"":' id="'+e(f)+'"',j=g,k=""+h+"";return d.subParser("hashBlock")(k,b,c)}),a=a.replace(i,function(a,f){var h=d.subParser("spanGamut")(f,b,c),i=b.noHeaderId?"":' id="'+e(f)+'"',j=g+1,k=""+h+"";return d.subParser("hashBlock")(k,b,c)}),a=a.replace(/^(#{1,6})[ \t]*(.+?)[ \t]*#*\n+/gm,function(a,f,h){var i=d.subParser("spanGamut")(h,b,c),j=b.noHeaderId?"":' id="'+e(h)+'"',k=g-1+f.length,l=""+i+"";return d.subParser("hashBlock")(l,b,c)}),a=c.converter._dispatch("headers.after",a,b,c)}),d.subParser("images",function(a,b,c){"use strict";function e(a,b,e,f,g,h,i,j){var k=c.gUrls,l=c.gTitles,m=c.gDimensions;if(e=e.toLowerCase(),j||(j=""),""===f||null===f){if((""===e||null===e)&&(e=b.toLowerCase().replace(/ ?\n/g," ")),f="#"+e,d.helper.isUndefined(k[e]))return a;f=k[e],d.helper.isUndefined(l[e])||(j=l[e]),d.helper.isUndefined(m[e])||(g=m[e].width,h=m[e].height)}b=b.replace(/"/g,"""),b=d.helper.escapeCharacters(b,"*_",!1),f=d.helper.escapeCharacters(f,"*_",!1);var n=''+b+'?(?: =([*\d]+[A-Za-z%]{0,4})x([*\d]+[A-Za-z%]{0,4}))?[ \t]*(?:(['"])(.*?)\6[ \t]*)?\)/g,g=/!\[(.*?)][ ]?(?:\n[ ]*)?\[(.*?)]()()()()()/g;return a=a.replace(g,e),a=a.replace(f,e),a=c.converter._dispatch("images.after",a,b,c)}),d.subParser("italicsAndBold",function(a,b,c){"use strict";return a=c.converter._dispatch("italicsAndBold.before",a,b,c),b.literalMidWordUnderscores?(a=a.replace(/(^|\s|>|\b)__(?=\S)([^]+?)__(?=\b|<|\s|$)/gm,"$1$2"),a=a.replace(/(^|\s|>|\b)_(?=\S)([^]+?)_(?=\b|<|\s|$)/gm,"$1$2"),a=a.replace(/(\*\*)(?=\S)([^\r]*?\S[*]*)\1/g,"$2"),a=a.replace(/(\*)(?=\S)([^\r]*?\S)\1/g,"$2")):(a=a.replace(/(\*\*|__)(?=\S)([^\r]*?\S[*_]*)\1/g,"$2"),a=a.replace(/(\*|_)(?=\S)([^\r]*?\S)\1/g,"$2")),a=c.converter._dispatch("italicsAndBold.after",a,b,c)}),d.subParser("lists",function(a,b,c){"use strict";function e(a,e){c.gListLevel++,a=a.replace(/\n{2,}$/,"\n"),a+="~0";var f=/(\n)?(^[ \t]*)([*+-]|\d+[.])[ \t]+((\[(x|X| )?])?[ \t]*[^\r]+?(\n{1,2}))(?=\n*(~0|\2([*+-]|\d+[.])[ \t]+))/gm,g=/\n[ \t]*\n(?!~0)/.test(a);return a=a.replace(f,function(a,e,f,h,i,j,k){k=k&&""!==k.trim();var l=d.subParser("outdent")(i,b,c),m="";return j&&b.tasklists&&(m=' class="task-list-item" style="list-style-type: none;"',l=l.replace(/^[ \t]*\[(x|X| )?]/m,function(){var a='-1?(l=d.subParser("githubCodeBlocks")(l,b,c),l=d.subParser("blockGamut")(l,b,c)):(l=d.subParser("lists")(l,b,c),l=l.replace(/\n$/,""),l=g?d.subParser("paragraphs")(l,b,c):d.subParser("spanGamut")(l,b,c)),l="\n"+l+"\n"}),a=a.replace(/~0/g,""),c.gListLevel--,e&&(a=a.replace(/\s+$/,"")),a}function f(a,b,c){var d="ul"===b?/^ {0,2}\d+\.[ \t]/gm:/^ {0,2}[*+-][ \t]/gm,f=[],g="";if(-1!==a.search(d)){!function i(a){var f=a.search(d);-1!==f?(g+="\n\n<"+b+">"+e(a.slice(0,f),!!c)+"\n\n",b="ul"===b?"ol":"ul",d="ul"===b?/^ {0,2}\d+\.[ \t]/gm:/^ {0,2}[*+-][ \t]/gm,i(a.slice(f))):g+="\n\n<"+b+">"+e(a,!!c)+"\n\n"}(a);for(var h=0;h"+e(a,!!c)+"\n\n";return g}a=c.converter._dispatch("lists.before",a,b,c),a+="~0";var g=/^(([ ]{0,3}([*+-]|\d+[.])[ \t]+)[^\r]+?(~0|\n{2,}(?=\S)(?![ \t]*(?:[*+-]|\d+[.])[ \t]+)))/gm;return c.gListLevel?a=a.replace(g,function(a,b,c){var d=c.search(/[*+-]/g)>-1?"ul":"ol";return f(b,d,!0)}):(g=/(\n\n|^\n?)(([ ]{0,3}([*+-]|\d+[.])[ \t]+)[^\r]+?(~0|\n{2,}(?=\S)(?![ \t]*(?:[*+-]|\d+[.])[ \t]+)))/gm,a=a.replace(g,function(a,b,c,d){var e=d.search(/[*+-]/g)>-1?"ul":"ol";return f(c,e)})),a=a.replace(/~0/,""),a=c.converter._dispatch("lists.after",a,b,c)}),d.subParser("outdent",function(a){"use strict";return a=a.replace(/^(\t|[ ]{1,4})/gm,"~0"),a=a.replace(/~0/g,"")}),d.subParser("paragraphs",function(a,b,c){"use strict";a=c.converter._dispatch("paragraphs.before",a,b,c),a=a.replace(/^\n+/g,""),a=a.replace(/\n+$/g,"");for(var e=a.split(/\n{2,}/g),f=[],g=e.length,h=0;g>h;h++){var i=e[h];i.search(/~(K|G)(\d+)\1/g)>=0?f.push(i):(i=d.subParser("spanGamut")(i,b,c),i=i.replace(/^([ \t]*)/g,"

"),i+="

",f.push(i))}for(g=f.length,h=0;g>h;h++){for(var j="",k=f[h],l=!1;k.search(/~(K|G)(\d+)\1/)>=0;){var m=RegExp.$1,n=RegExp.$2;j="K"===m?c.gHtmlBlocks[n]:l?d.subParser("encodeCode")(c.ghCodeBlocks[n].text):c.ghCodeBlocks[n].codeblock,j=j.replace(/\$/g,"$$$$"),k=k.replace(/(\n\n)?~(K|G)\d+\2(\n\n)?/,j),/^]*>\s*]*>/.test(k)&&(l=!0)}f[h]=k}return a=f.join("\n\n"),a=a.replace(/^\n+/g,""),a=a.replace(/\n+$/g,""),c.converter._dispatch("paragraphs.after",a,b,c)}),d.subParser("runExtension",function(a,b,c,d){"use strict";if(a.filter)b=a.filter(b,d.converter,c);else if(a.regex){var e=a.regex;!e instanceof RegExp&&(e=new RegExp(e,"g")),b=b.replace(e,a.replace)}return b}),d.subParser("spanGamut",function(a,b,c){"use strict";return a=c.converter._dispatch("spanGamut.before",a,b,c),a=d.subParser("codeSpans")(a,b,c),a=d.subParser("escapeSpecialCharsWithinTagAttributes")(a,b,c),a=d.subParser("encodeBackslashEscapes")(a,b,c),a=d.subParser("images")(a,b,c),a=d.subParser("anchors")(a,b,c),a=d.subParser("autoLinks")(a,b,c),a=d.subParser("encodeAmpsAndAngles")(a,b,c),a=d.subParser("italicsAndBold")(a,b,c),a=d.subParser("strikethrough")(a,b,c),a=a.replace(/ +\n/g,"
\n"),a=c.converter._dispatch("spanGamut.after",a,b,c)}),d.subParser("strikethrough",function(a,b,c){"use strict";return b.strikethrough&&(a=c.converter._dispatch("strikethrough.before",a,b,c),a=a.replace(/(?:~T){2}([\s\S]+?)(?:~T){2}/g,"$1"),a=c.converter._dispatch("strikethrough.after",a,b,c)),a}),d.subParser("stripBlankLines",function(a){"use strict";return a.replace(/^[ \t]+$/gm,"")}),d.subParser("stripLinkDefinitions",function(a,b,c){"use strict";var e=/^ {0,3}\[(.+)]:[ \t]*\n?[ \t]*?(?: =([*\d]+[A-Za-z%]{0,4})x([*\d]+[A-Za-z%]{0,4}))?[ \t]*\n?[ \t]*(?:(\n*)["|'(](.+?)["|')][ \t]*)?(?:\n+|(?=~0))/gm;return a+="~0",a=a.replace(e,function(a,e,f,g,h,i,j){return e=e.toLowerCase(),c.gUrls[e]=d.subParser("encodeAmpsAndAngles")(f),i?i+j:(j&&(c.gTitles[e]=j.replace(/"|'/g,""")),b.parseImgDimensions&&g&&h&&(c.gDimensions[e]={width:g,height:h}),"")}),a=a.replace(/~0/,"")}),d.subParser("tables",function(a,b,c){"use strict";function e(a){return/^:[ \t]*--*$/.test(a)?' style="text-align:left;"':/^--*[ \t]*:[ \t]*$/.test(a)?' style="text-align:right;"':/^:[ \t]*--*[ \t]*:$/.test(a)?' style="text-align:center;"':""}function f(a,e){var f="";return a=a.trim(),b.tableHeaderId&&(f=' id="'+a.replace(/ /g,"_").toLowerCase()+'"'),a=d.subParser("spanGamut")(a,b,c),""+a+"\n"}function g(a,e){var f=d.subParser("spanGamut")(a,b,c);return""+f+"\n"}function h(a,b){for(var c="\n\n\n",d=a.length,e=0;d>e;++e)c+=a[e];for(c+="\n\n\n",e=0;e\n";for(var f=0;d>f;++f)c+=b[e][f];c+="\n"}return c+="\n
\n"}if(!b.tables)return a;var i=/^[ \t]{0,3}\|?.+\|.+\n[ \t]{0,3}\|?[ \t]*:?[ \t]*(?:-|=){2,}[ \t]*:?[ \t]*\|[ \t]*:?[ \t]*(?:-|=){2,}[^]+?(?:\n\n|~0)/gm;return a=c.converter._dispatch("tables.before",a,b,c),a=a.replace(i,function(a){var b,c=a.split("\n");for(b=0;b - + From abaf8e96a6728f1642604440f9980256c0b8ecdd Mon Sep 17 00:00:00 2001 From: El RIDO Date: Tue, 24 Jan 2017 19:27:35 +0100 Subject: [PATCH 0015/1638] updated page template --- tpl/page.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tpl/page.php b/tpl/page.php index bc18c41c..7044f901 100644 --- a/tpl/page.php +++ b/tpl/page.php @@ -21,7 +21,7 @@ if ($SYNTAXHIGHLIGHTING): endif; ?> - + @@ -43,7 +43,7 @@ if ($SYNTAXHIGHLIGHTING): endif; if ($MARKDOWN): ?> - + From 065f5e53b381f1e809d762afe5335ca93bdfd337 Mon Sep 17 00:00:00 2001 From: rugk Date: Thu, 26 Jan 2017 16:40:33 +0100 Subject: [PATCH 0016/1638] Remove composer-lock from .gitignore As per https://github.com/PrivateBin/PrivateBin/issues/84#issuecomment-275065350 Thanks, @jelhan. --- .gitignore | 2 -- 1 file changed, 2 deletions(-) diff --git a/.gitignore b/.gitignore index dfecbe3d..519cad1c 100644 --- a/.gitignore +++ b/.gitignore @@ -23,8 +23,6 @@ vendor/**/tests vendor/**/build_phar.php !vendor/**/*.php -composer.lock - # Ignore unit testing logs, api docs and eclipse project files tst/log/ .settings From fdef8bc5be89bbd82ad00af9794748893301c290 Mon Sep 17 00:00:00 2001 From: El RIDO Date: Sun, 29 Jan 2017 14:31:44 +0100 Subject: [PATCH 0017/1638] starting to work on JSVerify & Mocha based unit tests for our JS code base --- .gitattributes | 4 +++ .gitignore | 1 + js/.istanbul.yml | 7 +++++ js/test.js | 78 ++++++++++++++++++++++++++++++++++++++++++++++++ tst/README.md | 48 +++++++++++++++++++++++++---- tst/phpunit.xml | 2 +- 6 files changed, 133 insertions(+), 7 deletions(-) create mode 100644 js/.istanbul.yml create mode 100644 js/test.js diff --git a/.gitattributes b/.gitattributes index e26be35a..daef8b1c 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,5 +1,9 @@ doc/ export-ignore tst/ export-ignore +js/.istanbul.yml export-ignore +js/test.js export-ignore +js/mocha-3.2.0.js export-ignore +css/mocha-3.2.0.css export-ignore .codeclimate.yml export-ignore .csslintrc export-ignore .dockerignore export-ignore diff --git a/.gitignore b/.gitignore index dfecbe3d..9bfc9918 100644 --- a/.gitignore +++ b/.gitignore @@ -26,6 +26,7 @@ vendor/**/build_phar.php composer.lock # Ignore unit testing logs, api docs and eclipse project files +js/node_modules/ tst/log/ .settings .buildpath diff --git a/js/.istanbul.yml b/js/.istanbul.yml new file mode 100644 index 00000000..bf6e0675 --- /dev/null +++ b/js/.istanbul.yml @@ -0,0 +1,7 @@ +--- +instrumentation: + excludes: + - jquery-3.1.1.js + baseline-file: ../tst/log/js-coverage-baseline.json +reporting: + dir: ../tst/log/js-coverage-report diff --git a/js/test.js b/js/test.js new file mode 100644 index 00000000..399a8566 --- /dev/null +++ b/js/test.js @@ -0,0 +1,78 @@ +'use strict'; +var jsc = require('jsverify'); + +before(function () { + this.jsdom = require('jsdom-global')(); + global.$ = global.jQuery = require('./jquery-3.1.1'); + global.sjcl = require('./sjcl-1.0.4'); + global.Base64 = require('./base64-2.1.9'); + global.RawDeflate = require('./rawdeflate-0.5'); + require('./rawinflate-0.3'); + require('./privatebin'); +}) + +after(function () { + this.jsdom(); +}) + +describe('helper', function () { + describe('secondsToHuman', function () { + jsc.property('returns an array with a number and a word', 'integer', function (number) { + var result = $.PrivateBin.helper.secondsToHuman(number); + return Array.isArray(result) && + result.length === 2 && + result[0] === parseInt(result[0], 10) && + typeof result[1] === 'string'; + }); + jsc.property('returns seconds on the first array position', 'integer 59', function (number) { + return $.PrivateBin.helper.secondsToHuman(number)[0] === number; + }); + jsc.property('returns seconds on the second array position', 'integer 59', function (number) { + return $.PrivateBin.helper.secondsToHuman(number)[1] === 'second'; + }); + jsc.property('returns minutes on the first array position', 'integer 60 3599', function (number) { + return $.PrivateBin.helper.secondsToHuman(number)[0] === Math.floor(number / 60); + }); + jsc.property('returns minutes on the second array position', 'integer 60 3599', function (number) { + return $.PrivateBin.helper.secondsToHuman(number)[1] === 'minute'; + }); + jsc.property('returns hours on the first array position', 'integer 3600 86399', function (number) { + return $.PrivateBin.helper.secondsToHuman(number)[0] === Math.floor(number / (60 * 60)); + }); + jsc.property('returns hours on the second array position', 'integer 3600 86399', function (number) { + return $.PrivateBin.helper.secondsToHuman(number)[1] === 'hour'; + }); + jsc.property('returns days on the first array position', 'integer 86400 5184000', function (number) { + return $.PrivateBin.helper.secondsToHuman(number)[0] === Math.floor(number / (60 * 60 * 24)); + }); + jsc.property('returns days on the second array position', 'integer 86400 5184000', function (number) { + return $.PrivateBin.helper.secondsToHuman(number)[1] === 'day'; + }); + // max safe integer as per http://ecma262-5.com/ELS5_HTML.htm#Section_8.5 + jsc.property('returns months on the first array position', 'integer 5184000 9007199254740991', function (number) { + return $.PrivateBin.helper.secondsToHuman(number)[0] === Math.floor(number / (60 * 60 * 24 * 30)); + }); + jsc.property('returns months on the second array position', 'integer 5184000 9007199254740991', function (number) { + return $.PrivateBin.helper.secondsToHuman(number)[1] === 'month'; + }); + }); + + describe('hashToParameterString', function () { + jsc.property('returns strings', 'dict nestring', function (dict) { + return typeof $.PrivateBin.helper.hashToParameterString(dict) === 'string'; + }); + }); + + describe('parameterStringToHash', function () { + jsc.property('returns objects', 'string', function (string) { + return typeof $.PrivateBin.helper.parameterStringToHash(string) === 'object'; + }); + jsc.property('decodes hashes generated with hashToParameterString', 'dict nestring', function (dict) { + var result = $.PrivateBin.helper.parameterStringToHash($.PrivateBin.helper.hashToParameterString(dict)); + // padding for URL shorteners + dict.p = 'p'; + return JSON.stringify(result) === JSON.stringify(dict); + }); + }); +}); + diff --git a/tst/README.md b/tst/README.md index 07564fcf..76e69ee1 100644 --- a/tst/README.md +++ b/tst/README.md @@ -1,20 +1,56 @@ -Running unit tests -================== +Running PHP unit tests +====================== In order to run these tests, you will need to install the following packages and its dependencies: * phpunit * php-gd * php-sqlite3 -* php-xdebug +* php-xdebug (for code coverage reports) Example for Debian and Ubuntu: -```sh -$ sudo aptitude install phpunit php-gd php-sqlite php-xdebug +```console +$ sudo apt install phpunit php-gd php-sqlite php-xdebug ``` To run the tests, just change into this directory and run phpunit: -```sh +```console $ cd PrivateBin/tst $ phpunit ``` + +Running JavaScript unit tests +============================= + +In order to run these tests, you will need to install the following packages +and its dependencies: +* npm + +Then you can use the node package manager to install the latest stable release +of mocha and istanbul (for code coverage reports) globally and jsVerify, jsdom +and jsdom-global locally: + +```console +$ npm install -g mocha istanbul +$ cd PrivateBin/js +$ npm install jsverify jsdom jsdom-global +``` + +Example for Debian and Ubuntu, including steps to allow current user to install +node modules globally: +```console +$ sudo apt install npm +$ sudo mkdir /usr/local/lib/node_modules +$ sudo chown -R $(whoami) $(npm config get prefix)/{lib/node_modules,bin,share} +$ ln -s /usr/bin/nodejs /usr/local/bin/node +$ npm install -g mocha istanbul +$ cd PrivateBin/js +$ npm install jsverify jsdom jsdom-global +``` + +To run the tests, just change into the `js` directory and run istanbul: +```console +$ cd PrivateBin/js +$ istanbul cover _mocha +``` + diff --git a/tst/phpunit.xml b/tst/phpunit.xml index 32cb66eb..cb5606b7 100644 --- a/tst/phpunit.xml +++ b/tst/phpunit.xml @@ -13,7 +13,7 @@ - + From d678f5dada0f4cb8555ebce50ca88f051b2fd1b3 Mon Sep 17 00:00:00 2001 From: El RIDO Date: Sun, 29 Jan 2017 14:32:55 +0100 Subject: [PATCH 0018/1638] fixing inconsistency found in unit test --- js/privatebin.js | 163 +++++++++++++++++++++++----------------------- tpl/bootstrap.php | 2 +- tpl/page.php | 2 +- 3 files changed, 85 insertions(+), 82 deletions(-) diff --git a/js/privatebin.js b/js/privatebin.js index 59417512..d9eeda17 100644 --- a/js/privatebin.js +++ b/js/privatebin.js @@ -83,7 +83,7 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { var parameterString = ''; for (var key in hashMap) { - if(parameterString === '') + if (parameterString === '') { parameterString = encodeURIComponent(key); parameterString += '=' + encodeURIComponent(hashMap[key]); @@ -95,7 +95,10 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { } } // padding for URL shorteners - parameterString += '&p=p'; + if (parameterString.length > 0) { + parameterString += '&'; + } + parameterString += 'p=p'; return parameterString; }, @@ -112,14 +115,15 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { { var parameterHash = {}; var parameterArray = parameterString.split('&'); - for (var i = 0; i < parameterArray.length; i++) - { - var pair = parameterArray[i].split('='); - var key = decodeURIComponent(pair[0]); - var value = decodeURIComponent(pair[1]); - parameterHash[key] = value; + if (parameterArray[0] != '') { + for (var i = 0; i < parameterArray.length; i++) + { + var pair = parameterArray[i].split('='); + var key = decodeURIComponent(pair[0]); + var value = decodeURIComponent(pair[1]); + parameterHash[key] = value; + } } - return parameterHash; }, @@ -602,14 +606,14 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { /** * PrivateBin logic * - * @name privatebin + * @name controller * @class */ - var privatebin = { + var controller = { /** * headers to send in AJAX requests * - * @name privatebin.headers + * @name controller.headers * @enum {Object} */ headers: {'X-Requested-With': 'JSONHttpRequest'}, @@ -617,7 +621,7 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { /** * URL shortners create address * - * @name privatebin.shortenerUrl + * @name controller.shortenerUrl * @prop {string} */ shortenerUrl: '', @@ -625,7 +629,7 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { /** * URL of newly created paste * - * @name privatebin.createdPasteUrl + * @name controller.createdPasteUrl * @prop {string} */ createdPasteUrl: '', @@ -634,7 +638,7 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { * get the current script location (without search or hash part of the URL), * eg. http://example.com/zero/?aaaa#bbbb --> http://example.com/zero/ * - * @name privatebin.scriptLocation + * @name controller.scriptLocation * @function * @return {string} current script location */ @@ -654,7 +658,7 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { * get the pastes unique identifier from the URL, * eg. http://example.com/zero/?c05354954c49a487#c05354954c49a487 returns c05354954c49a487 * - * @name privatebin.pasteID + * @name controller.pasteID * @function * @return {string} unique identifier */ @@ -666,7 +670,7 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { /** * return the deciphering key stored in anchor part of the URL * - * @name privatebin.pageKey + * @name controller.pageKey * @function * @return {string} key */ @@ -703,7 +707,7 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { /** * ask the user for the password and set it * - * @name privatebin.requestPassword + * @name controller.requestPassword * @function */ requestPassword: function() @@ -729,7 +733,7 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { /** * use given format on paste, defaults to plain text * - * @name privatebin.formatPaste + * @name controller.formatPaste * @function * @param {string} format * @param {string} text @@ -789,7 +793,7 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { /** * show decrypted text in the display area, including discussion (if open) * - * @name privatebin.displayMessages + * @name controller.displayMessages * @function * @param {Object} [paste] - (optional) object including comments to display (items = array with keys ('data','meta')) */ @@ -893,7 +897,7 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { headers: this.headers }) .fail(function() { - privatebin.showError(i18n._('Could not delete the paste, it was not stored in burn after reading mode.')); + controller.showError(i18n._('Could not delete the paste, it was not stored in burn after reading mode.')); }); helper.setMessage(this.remainingTime, i18n._( 'FOR YOUR EYES ONLY. Don\'t close this window, this message can\'t be displayed again.' @@ -970,7 +974,7 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { /** * open the comment entry when clicking the "Reply" button of a comment * - * @name privatebin.openReply + * @name controller.openReply * @function * @param {Event} event */ @@ -999,7 +1003,7 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { /** * send a reply in a discussion * - * @name privatebin.sendComment + * @name controller.sendComment * @function * @param {Event} event */ @@ -1040,51 +1044,51 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { { if (data.status === 0) { - privatebin.showStatus(i18n._('Comment posted.')); + controller.showStatus(i18n._('Comment posted.')); $.ajax({ type: 'GET', - url: privatebin.scriptLocation() + '?' + privatebin.pasteID(), + url: controller.scriptLocation() + '?' + controller.pasteID(), dataType: 'json', - headers: privatebin.headers, + headers: controller.headers, success: function(data) { if (data.status === 0) { - privatebin.displayMessages(data); + controller.displayMessages(data); } else if (data.status === 1) { - privatebin.showError(i18n._('Could not refresh display: %s', data.message)); + controller.showError(i18n._('Could not refresh display: %s', data.message)); } else { - privatebin.showError(i18n._('Could not refresh display: %s', i18n._('unknown status'))); + controller.showError(i18n._('Could not refresh display: %s', i18n._('unknown status'))); } } }) .fail(function() { - privatebin.showError(i18n._('Could not refresh display: %s', i18n._('server error or not responding'))); + controller.showError(i18n._('Could not refresh display: %s', i18n._('server error or not responding'))); }); } else if (data.status === 1) { - privatebin.showError(i18n._('Could not post comment: %s', data.message)); + controller.showError(i18n._('Could not post comment: %s', data.message)); } else { - privatebin.showError(i18n._('Could not post comment: %s', i18n._('unknown status'))); + controller.showError(i18n._('Could not post comment: %s', i18n._('unknown status'))); } } }) .fail(function() { - privatebin.showError(i18n._('Could not post comment: %s', i18n._('server error or not responding'))); + controller.showError(i18n._('Could not post comment: %s', i18n._('server error or not responding'))); }); }, /** * send a new paste to server * - * @name privatebin.sendData + * @name controller.sendData * @function * @param {Event} event */ @@ -1128,7 +1132,7 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { reader.onload = (function(theFile) { return function(e) { - privatebin.sendDataContinue( + controller.sendDataContinue( randomkey, filter.cipher(randomkey, password, e.target.result), filter.cipher(randomkey, password, theFile.name) @@ -1154,7 +1158,7 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { /** * send a new paste to server, step 2 * - * @name privatebin.sendDataContinue + * @name controller.sendDataContinue * @function * @param {string} randomkey * @param {string} cipherdata_attachment @@ -1187,49 +1191,49 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { success: function(data) { if (data.status === 0) { - privatebin.stateExistingPaste(); - var url = privatebin.scriptLocation() + '?' + data.id + '#' + randomkey; - var deleteUrl = privatebin.scriptLocation() + '?pasteid=' + data.id + '&deletetoken=' + data.deletetoken; - privatebin.showStatus(''); - privatebin.errorMessage.addClass('hidden'); + controller.stateExistingPaste(); + var url = controller.scriptLocation() + '?' + data.id + '#' + randomkey; + var deleteUrl = controller.scriptLocation() + '?pasteid=' + data.id + '&deletetoken=' + data.deletetoken; + controller.showStatus(''); + controller.errorMessage.addClass('hidden'); $('#pastelink').html( i18n._( 'Your paste is %s (Hit [Ctrl]+[c] to copy)', url, url - ) + privatebin.shortenUrl(url) + ) + controller.shortenUrl(url) ); var shortenButton = $('#shortenbutton'); if (shortenButton) { - shortenButton.click($.proxy(privatebin.sendToShortener, privatebin)); + shortenButton.click($.proxy(controller.sendToShortener, privatebin)); } $('#deletelink').html('' + i18n._('Delete data') + ''); - privatebin.pasteResult.removeClass('hidden'); + controller.pasteResult.removeClass('hidden'); // we pre-select the link so that the user only has to [Ctrl]+[c] the link helper.selectText('pasteurl'); - privatebin.showStatus(''); - privatebin.formatPaste(data_to_send.formatter, privatebin.message.val()); + controller.showStatus(''); + controller.formatPaste(data_to_send.formatter, controller.message.val()); } else if (data.status === 1) { - privatebin.showError(i18n._('Could not create paste: %s', data.message)); + controller.showError(i18n._('Could not create paste: %s', data.message)); } else { - privatebin.showError(i18n._('Could not create paste: %s', i18n._('unknown status'))); + controller.showError(i18n._('Could not create paste: %s', i18n._('unknown status'))); } } }) .fail(function() { - privatebin.showError(i18n._('Could not create paste: %s', i18n._('server error or not responding'))); + controller.showError(i18n._('Could not create paste: %s', i18n._('server error or not responding'))); }); }, /** * check if a URL shortener was defined and create HTML containing a link to it * - * @name privatebin.shortenUrl + * @name controller.shortenUrl * @function * @param {string} url * @return {string} html @@ -1248,7 +1252,7 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { /** * put the screen in "New paste" mode * - * @name privatebin.stateNewPaste + * @name controller.stateNewPaste * @function */ stateNewPaste: function() @@ -1278,7 +1282,7 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { /** * put the screen in "Existing paste" mode * - * @name privatebin.stateExistingPaste + * @name controller.stateExistingPaste * @function * @param {boolean} [preview=false] - (optional) tell if the preview tabs should be displayed, defaults to false */ @@ -1318,7 +1322,7 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { /** * when "burn after reading" is checked, disable discussion * - * @name privatebin.changeBurnAfterReading + * @name controller.changeBurnAfterReading * @function */ changeBurnAfterReading: function() @@ -1338,7 +1342,7 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { /** * when discussion is checked, disable "burn after reading" * - * @name privatebin.changeOpenDisc + * @name controller.changeOpenDisc * @function */ changeOpenDisc: function() @@ -1358,7 +1362,7 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { /** * forward to URL shortener * - * @name privatebin.sendToShortener + * @name controller.sendToShortener * @function * @param {Event} event */ @@ -1371,7 +1375,7 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { /** * reload the page * - * @name privatebin.reloadPage + * @name controller.reloadPage * @function * @param {Event} event */ @@ -1384,7 +1388,7 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { /** * return raw text * - * @name privatebin.rawText + * @name controller.rawText * @function * @param {Event} event */ @@ -1407,7 +1411,7 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { /** * clone the current paste * - * @name privatebin.clonePaste + * @name controller.clonePaste * @function * @param {Event} event */ @@ -1435,7 +1439,7 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { /** * set the expiration on bootstrap templates * - * @name privatebin.setExpiration + * @name controller.setExpiration * @function * @param {Event} event */ @@ -1450,7 +1454,7 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { /** * set the format on bootstrap templates * - * @name privatebin.setFormat + * @name controller.setFormat * @function * @param {Event} event */ @@ -1469,7 +1473,7 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { /** * set the language in a cookie and reload the page * - * @name privatebin.setLanguage + * @name controller.setLanguage * @function * @param {Event} event */ @@ -1482,7 +1486,7 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { /** * support input of tab character * - * @name privatebin.supportTabs + * @name controller.supportTabs * @function * @param {Event} event */ @@ -1508,7 +1512,7 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { /** * view the editor tab * - * @name privatebin.viewEditor + * @name controller.viewEditor * @function * @param {Event} event */ @@ -1524,7 +1528,7 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { /** * view the preview tab * - * @name privatebin.viewPreview + * @name controller.viewPreview * @function * @param {Event} event */ @@ -1541,7 +1545,7 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { /** * create a new paste * - * @name privatebin.newPaste + * @name controller.newPaste * @function */ newPaste: function() @@ -1556,7 +1560,7 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { /** * removes an attachment * - * @name privatebin.removeAttachment + * @name controller.removeAttachment * @function */ removeAttachment: function() @@ -1572,7 +1576,7 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { /** * decrypt using the password from the modal dialog * - * @name privatebin.decryptPasswordModal + * @name controller.decryptPasswordModal * @function */ decryptPasswordModal: function() @@ -1584,7 +1588,7 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { /** * submit a password in the modal dialog * - * @name privatebin.submitPasswordModal + * @name controller.submitPasswordModal * @function * @param {Event} event */ @@ -1598,7 +1602,7 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { * display an error message, * we use the same function for paste and reply to comments * - * @name privatebin.showError + * @name controller.showError * @function * @param {string} message - text to display */ @@ -1631,7 +1635,7 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { * display a status message, * we use the same function for paste and reply to comments * - * @name privatebin.showStatus + * @name controller.showStatus * @function * @param {string} message - text to display * @param {boolean} [spin=false] - (optional) tell if the "spinning" animation should be displayed, defaults to false @@ -1665,7 +1669,7 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { /** * bind events to DOM elements * - * @name privatebin.bindEvents + * @name controller.bindEvents * @function */ bindEvents: function() @@ -1698,7 +1702,7 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { /** * main application * - * @name privatebin.init + * @name controller.init * @function */ init: function() @@ -1782,17 +1786,16 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { } } + /** + * main application start, called when DOM is fully loaded + * runs privatebin when translations were loaded + */ + $(i18n.loadTranslations($.proxy(controller.init, controller))); + return { helper: helper, i18n: i18n, filter: filter, - privatebin: privatebin + controller: controller }; }(jQuery, sjcl, Base64, RawDeflate); - -/** - * main application start, called when DOM is fully loaded - * runs privatebin when translations were loaded - */ -jQuery(jQuery.PrivateBin.i18n.loadTranslations(jQuery.proxy(jQuery.PrivateBin.privatebin.init, jQuery.PrivateBin.privatebin))); - diff --git a/tpl/bootstrap.php b/tpl/bootstrap.php index 3d51379c..c6a56612 100644 --- a/tpl/bootstrap.php +++ b/tpl/bootstrap.php @@ -69,7 +69,7 @@ if ($MARKDOWN): - + diff --git a/tpl/page.php b/tpl/page.php index 40c3e124..98d7f328 100644 --- a/tpl/page.php +++ b/tpl/page.php @@ -47,7 +47,7 @@ if ($MARKDOWN): - + From cae5a7115136f68a5e07e2263d84a15397b42418 Mon Sep 17 00:00:00 2001 From: El RIDO Date: Sun, 29 Jan 2017 14:48:56 +0100 Subject: [PATCH 0019/1638] fix missing class renaming --- js/privatebin.js | 6 +++--- tpl/bootstrap.php | 2 +- tpl/page.php | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/js/privatebin.js b/js/privatebin.js index d9eeda17..e8ba71f9 100644 --- a/js/privatebin.js +++ b/js/privatebin.js @@ -1205,7 +1205,7 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { ); var shortenButton = $('#shortenbutton'); if (shortenButton) { - shortenButton.click($.proxy(controller.sendToShortener, privatebin)); + shortenButton.click($.proxy(controller.sendToShortener, controller)); } $('#deletelink').html('' + i18n._('Delete data') + ''); controller.pasteResult.removeClass('hidden'); @@ -1787,8 +1787,8 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { } /** - * main application start, called when DOM is fully loaded - * runs privatebin when translations were loaded + * main application start, called when DOM is fully loaded and + * runs controller initalization after translations are loaded */ $(i18n.loadTranslations($.proxy(controller.init, controller))); diff --git a/tpl/bootstrap.php b/tpl/bootstrap.php index c6a56612..69d0e011 100644 --- a/tpl/bootstrap.php +++ b/tpl/bootstrap.php @@ -69,7 +69,7 @@ if ($MARKDOWN): - + diff --git a/tpl/page.php b/tpl/page.php index 98d7f328..4860f8cf 100644 --- a/tpl/page.php +++ b/tpl/page.php @@ -47,7 +47,7 @@ if ($MARKDOWN): - + From f1df27f46ccd36470db933cab5ad11fd5171034c Mon Sep 17 00:00:00 2001 From: El RIDO Date: Sun, 29 Jan 2017 15:09:57 +0100 Subject: [PATCH 0020/1638] allowing for parameter strings starting with & --- js/privatebin.js | 6 +++--- js/test.js | 2 +- tpl/bootstrap.php | 2 +- tpl/page.php | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/js/privatebin.js b/js/privatebin.js index e8ba71f9..c80cd3eb 100644 --- a/js/privatebin.js +++ b/js/privatebin.js @@ -115,9 +115,9 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { { var parameterHash = {}; var parameterArray = parameterString.split('&'); - if (parameterArray[0] != '') { - for (var i = 0; i < parameterArray.length; i++) - { + for (var i = 0; i < parameterArray.length; i++) + { + if (parameterArray[i] != '') { var pair = parameterArray[i].split('='); var key = decodeURIComponent(pair[0]); var value = decodeURIComponent(pair[1]); diff --git a/js/test.js b/js/test.js index 399a8566..5208fd3d 100644 --- a/js/test.js +++ b/js/test.js @@ -4,7 +4,7 @@ var jsc = require('jsverify'); before(function () { this.jsdom = require('jsdom-global')(); global.$ = global.jQuery = require('./jquery-3.1.1'); - global.sjcl = require('./sjcl-1.0.4'); + global.sjcl = require('./sjcl-1.0.6'); global.Base64 = require('./base64-2.1.9'); global.RawDeflate = require('./rawdeflate-0.5'); require('./rawinflate-0.3'); diff --git a/tpl/bootstrap.php b/tpl/bootstrap.php index d875b207..a29324b5 100644 --- a/tpl/bootstrap.php +++ b/tpl/bootstrap.php @@ -69,7 +69,7 @@ if ($MARKDOWN): - + diff --git a/tpl/page.php b/tpl/page.php index d63dc09b..9ab9d59e 100644 --- a/tpl/page.php +++ b/tpl/page.php @@ -47,7 +47,7 @@ if ($MARKDOWN): - + From 339ab5e380d833194466c2d756799cd1e5698e6f Mon Sep 17 00:00:00 2001 From: El RIDO Date: Sun, 29 Jan 2017 15:11:04 +0100 Subject: [PATCH 0021/1638] ignoring composer.lock, so it isn't accidentally checked in (again) --- .gitignore | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 155fff73..e476e383 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,10 @@ data/ doc/* !doc/*.md +# Ignore developers composer status so it isn't accidentally checked in, +# see https://github.com/PrivateBin/PrivateBin/issues/84 +composer.lock + # Ignore vendor dir of Composer except PHP files vendor/*.* vendor/*/*.* @@ -23,7 +27,7 @@ vendor/**/tests vendor/**/build_phar.php !vendor/**/*.php -# Ignore unit testing logs, api docs and eclipse project files +# Ignore local node modules, unit testing logs, api docs and eclipse project files js/node_modules/ tst/log/ .settings From b76a73aa06b3a284684ba0356690758d6450c884 Mon Sep 17 00:00:00 2001 From: El RIDO Date: Sun, 29 Jan 2017 16:17:56 +0100 Subject: [PATCH 0022/1638] upgrading showdown --- js/showdown-1.6.0.js | 2 -- js/showdown-1.6.1.js | 1 + tpl/bootstrap.php | 4 ++-- tpl/page.php | 4 ++-- 4 files changed, 5 insertions(+), 6 deletions(-) delete mode 100644 js/showdown-1.6.0.js create mode 100644 js/showdown-1.6.1.js diff --git a/js/showdown-1.6.0.js b/js/showdown-1.6.0.js deleted file mode 100644 index 4bacdaaa..00000000 --- a/js/showdown-1.6.0.js +++ /dev/null @@ -1,2 +0,0 @@ -#!/usr/bin/env node -require('../src/cli/cli'); diff --git a/js/showdown-1.6.1.js b/js/showdown-1.6.1.js new file mode 100644 index 00000000..0cbc9e3a --- /dev/null +++ b/js/showdown-1.6.1.js @@ -0,0 +1 @@ +(function(){function a(a){"use strict";var b={omitExtraWLInCodeBlocks:{defaultValue:!1,describe:"Omit the default extra whiteline added to code blocks",type:"boolean"},noHeaderId:{defaultValue:!1,describe:"Turn on/off generated header id",type:"boolean"},prefixHeaderId:{defaultValue:!1,describe:"Specify a prefix to generated header ids",type:"string"},ghCompatibleHeaderId:{defaultValue:!1,describe:"Generate header ids compatible with github style (spaces are replaced with dashes, a bunch of non alphanumeric chars are removed)",type:"boolean"},headerLevelStart:{defaultValue:!1,describe:"The header blocks level start",type:"integer"},parseImgDimensions:{defaultValue:!1,describe:"Turn on/off image dimension parsing",type:"boolean"},simplifiedAutoLink:{defaultValue:!1,describe:"Turn on/off GFM autolink style",type:"boolean"},excludeTrailingPunctuationFromURLs:{defaultValue:!1,describe:"Excludes trailing punctuation from links generated with autoLinking",type:"boolean"},literalMidWordUnderscores:{defaultValue:!1,describe:"Parse midword underscores as literal underscores",type:"boolean"},strikethrough:{defaultValue:!1,describe:"Turn on/off strikethrough support",type:"boolean"},tables:{defaultValue:!1,describe:"Turn on/off tables support",type:"boolean"},tablesHeaderId:{defaultValue:!1,describe:"Add an id to table headers",type:"boolean"},ghCodeBlocks:{defaultValue:!0,describe:"Turn on/off GFM fenced code blocks support",type:"boolean"},tasklists:{defaultValue:!1,describe:"Turn on/off GFM tasklist support",type:"boolean"},smoothLivePreview:{defaultValue:!1,describe:"Prevents weird effects in live previews due to incomplete input",type:"boolean"},smartIndentationFix:{defaultValue:!1,description:"Tries to smartly fix indentation in es6 strings",type:"boolean"},disableForced4SpacesIndentedSublists:{defaultValue:!1,description:"Disables the requirement of indenting nested sublists by 4 spaces",type:"boolean"},simpleLineBreaks:{defaultValue:!1,description:"Parses simple line breaks as
(GFM Style)",type:"boolean"},requireSpaceBeforeHeadingText:{defaultValue:!1,description:"Makes adding a space between `#` and the header text mandatory (GFM Style)",type:"boolean"},ghMentions:{defaultValue:!1,description:"Enables github @mentions",type:"boolean"},encodeEmails:{defaultValue:!0,description:"Encode e-mail addresses through the use of Character Entities, transforming ASCII e-mail addresses into its equivalent decimal entities",type:"boolean"}};if(a===!1)return JSON.parse(JSON.stringify(b));var c={};for(var d in b)b.hasOwnProperty(d)&&(c[d]=b[d].defaultValue);return c}function b(){"use strict";var b=a(!0),c={};for(var d in b)b.hasOwnProperty(d)&&(c[d]=!0);return c}function c(a,b){"use strict";var c=b?"Error in "+b+" extension->":"Error in unnamed extension",d={valid:!0,error:""};e.helper.isArray(a)||(a=[a]);for(var f=0;f-1,l=new RegExp(b+"|"+c,"g"+j.replace(/g/g,"")),m=new RegExp(b,j.replace(/g/g,"")),n=[];do for(e=0;g=l.exec(a);)if(m.test(g[0]))e++||(f=l.lastIndex,h=f-g[0].length);else if(e&&!--e){i=g.index+g[0].length;var o={left:{start:h,end:f},match:{start:f,end:g.index},right:{start:g.index,end:i},wholeMatch:{start:h,end:i}};if(n.push(o),!k)return n}while(e&&(l.lastIndex=f));return n};e.helper.matchRecursiveRegExp=function(a,b,c,d){"use strict";for(var e=k(a,b,c,d),f=[],g=0;g0){var l=[];0!==h[0].wholeMatch.start&&l.push(a.slice(0,h[0].wholeMatch.start));for(var m=0;j>m;++m)l.push(b(a.slice(h[m].wholeMatch.start,h[m].wholeMatch.end),a.slice(h[m].match.start,h[m].match.end),a.slice(h[m].left.start,h[m].left.end),a.slice(h[m].right.start,h[m].right.end))),j-1>m&&l.push(a.slice(h[m].wholeMatch.end,h[m+1].wholeMatch.start));h[j-1].wholeMatch.end.9?b[2](a):c>.45?b[1](a):b[0](a)}return a})},"undefined"==typeof console&&(console={warn:function(a){"use strict";alert(a)},log:function(a){"use strict";alert(a)},error:function(a){"use strict";throw a}}),e.helper.regexes={asteriskAndDash:/([*_])/g},e.Converter=function(a){"use strict";function b(){a=a||{};for(var b in h)h.hasOwnProperty(b)&&(m[b]=h[b]);if("object"!=typeof a)throw Error("Converter expects the passed parameter to be an object, but "+typeof a+" was passed instead.");for(var c in a)a.hasOwnProperty(c)&&(m[c]=a[c]);m.extensions&&e.helper.forEach(m.extensions,d)}function d(a,b){if(b=b||null,e.helper.isString(a)){if(a=e.helper.stdExtName(a),b=a,e.extensions[a])return console.warn("DEPRECATION WARNING: "+a+" is an old extension that uses a deprecated loading method.Please inform the developer that the extension should be updated!"),void f(e.extensions[a],a);if(e.helper.isUndefined(g[a]))throw Error('Extension "'+a+'" could not be loaded. It was either not found or is not a valid extension.');a=g[a]}"function"==typeof a&&(a=a()),e.helper.isArray(a)||(a=[a]);var d=c(a,b);if(!d.valid)throw Error(d.error);for(var h=0;h-1))return a;m=""}else m=c.gUrls[l],e.helper.isUndefined(c.gTitles[l])||(n=c.gTitles[l]);m=m.replace(e.helper.regexes.asteriskAndDash,e.helper.escapeCharactersCallback);var o='"};return a=a.replace(/(\[((?:\[[^\]]*]|[^\[\]])*)][ ]?(?:\n[ ]*)?\[(.*?)])()()()()/g,d),a=a.replace(/(\[((?:\[[^\]]*]|[^\[\]])*)]\([ \t]*()?[ \t]*((['"])(.*?)\6[ \t]*)?\))/g,d),a=a.replace(/(\[([^\[\]]+)])()()()()()/g,d),b.ghMentions&&(a=a.replace(/(^|\s)(\\)?(@([a-z\d\-]+))(?=[.!?;,[\]()]|\s|$)/gim,function(a,b,c,d,e){return"\\"===c?b+d:b+''+d+""})),a=c.converter._dispatch("anchors.after",a,b,c)}),e.subParser("autoLinks",function(a,b,c){"use strict";function d(a,c,d,e,f){var g=c,h="";return/^www\./i.test(c)&&(c=c.replace(/^www\./i,"http://www.")),b.excludeTrailingPunctuationFromURLs&&f&&(h=f),''+g+""+h}function f(a,c,d){var f="mailto:";return c=c||"",d=e.subParser("unescapeSpecialChars")(d),b.encodeEmails?(d=e.helper.encodeEmailAddress(d),f=e.helper.encodeEmailAddress(f+d)):f+=d,c+''+d+""}a=c.converter._dispatch("autoLinks.before",a,b,c);var g=/\b(((https?|ftp|dict):\/\/|www\.)[^'">\s]+\.[^'">\s]+)()(?=\s|$)(?!["<>])/gi,h=/\b(((https?|ftp|dict):\/\/|www\.)[^'">\s]+\.[^'">\s]+?)([.!?()]?)(?=\s|$)(?!["<>])/gi,i=/<(((https?|ftp|dict):\/\/|www\.)[^'">\s]+)>/gi,j=/(^|\s)(?:mailto:)?([A-Za-z0-9!#$%&'*+-\/=?^_`{|}~.]+@[-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+)(?=$|\s)/gim,k=/<()(?:mailto:)?([-.\w]+@[-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+)>/gi;return a=a.replace(i,d),a=a.replace(k,f),b.simplifiedAutoLink&&(a=b.excludeTrailingPunctuationFromURLs?a.replace(h,d):a.replace(g,d),a=a.replace(j,f)),a=c.converter._dispatch("autoLinks.after",a,b,c)}),e.subParser("blockGamut",function(a,b,c){"use strict";return a=c.converter._dispatch("blockGamut.before",a,b,c),a=e.subParser("blockQuotes")(a,b,c),a=e.subParser("headers")(a,b,c),a=e.subParser("horizontalRule")(a,b,c),a=e.subParser("lists")(a,b,c),a=e.subParser("codeBlocks")(a,b,c),a=e.subParser("tables")(a,b,c),a=e.subParser("hashHTMLBlocks")(a,b,c),a=e.subParser("paragraphs")(a,b,c),a=c.converter._dispatch("blockGamut.after",a,b,c)}),e.subParser("blockQuotes",function(a,b,c){"use strict";return a=c.converter._dispatch("blockQuotes.before",a,b,c),a=a.replace(/((^ {0,3}>[ \t]?.+\n(.+\n)*\n*)+)/gm,function(a,d){var f=d;return f=f.replace(/^[ \t]*>[ \t]?/gm,"~0"),f=f.replace(/~0/g,""),f=f.replace(/^[ \t]+$/gm,""),f=e.subParser("githubCodeBlocks")(f,b,c),f=e.subParser("blockGamut")(f,b,c),f=f.replace(/(^|\n)/g,"$1 "),f=f.replace(/(\s*
[^\r]+?<\/pre>)/gm,function(a,b){var c=b;return c=c.replace(/^  /gm,"~0"),c=c.replace(/~0/g,"")}),e.subParser("hashBlock")("
\n"+f+"\n
",b,c)}),a=c.converter._dispatch("blockQuotes.after",a,b,c)}),e.subParser("codeBlocks",function(a,b,c){"use strict";a=c.converter._dispatch("codeBlocks.before",a,b,c),a+="~0";var d=/(?:\n\n|^)((?:(?:[ ]{4}|\t).*\n+)+)(\n*[ ]{0,3}[^ \t\n]|(?=~0))/g;return a=a.replace(d,function(a,d,f){var g=d,h=f,i="\n";return g=e.subParser("outdent")(g),g=e.subParser("encodeCode")(g),g=e.subParser("detab")(g),g=g.replace(/^\n+/g,""),g=g.replace(/\n+$/g,""),b.omitExtraWLInCodeBlocks&&(i=""),g="
"+g+i+"
",e.subParser("hashBlock")(g,b,c)+h}),a=a.replace(/~0/,""),a=c.converter._dispatch("codeBlocks.after",a,b,c)}),e.subParser("codeSpans",function(a,b,c){"use strict";return a=c.converter._dispatch("codeSpans.before",a,b,c),"undefined"==typeof a&&(a=""),a=a.replace(/(^|[^\\])(`+)([^\r]*?[^`])\2(?!`)/gm,function(a,b,c,d){var f=d;return f=f.replace(/^([ \t]*)/g,""),f=f.replace(/[ \t]*$/g,""),f=e.subParser("encodeCode")(f),b+""+f+""}),a=c.converter._dispatch("codeSpans.after",a,b,c)}),e.subParser("detab",function(a){"use strict";return a=a.replace(/\t(?=\t)/g," "),a=a.replace(/\t/g,"~A~B"),a=a.replace(/~B(.+?)~A/g,function(a,b){for(var c=b,d=4-c.length%4,e=0;d>e;e++)c+=" ";return c}),a=a.replace(/~A/g," "),a=a.replace(/~B/g,"")}),e.subParser("encodeAmpsAndAngles",function(a){"use strict";return a=a.replace(/&(?!#?[xX]?(?:[0-9a-fA-F]+|\w+);)/g,"&"),a=a.replace(/<(?![a-z\/?\$!])/gi,"<")}),e.subParser("encodeBackslashEscapes",function(a){"use strict";return a=a.replace(/\\(\\)/g,e.helper.escapeCharactersCallback),a=a.replace(/\\([`*_{}\[\]()>#+-.!])/g,e.helper.escapeCharactersCallback)}),e.subParser("encodeCode",function(a){"use strict";return a=a.replace(/&/g,"&").replace(//g,">").replace(/([*_{}\[\]\\])/g,e.helper.escapeCharactersCallback)}),e.subParser("escapeSpecialCharsWithinTagAttributes",function(a){"use strict";var b=/(<[a-z\/!$]("[^"]*"|'[^']*'|[^'">])*>|)/gi;return a=a.replace(b,function(a){return a.replace(/(.)<\/?code>(?=.)/g,"$1`").replace(/([\\`*_])/g,e.helper.escapeCharactersCallback)})}),e.subParser("githubCodeBlocks",function(a,b,c){"use strict";return b.ghCodeBlocks?(a=c.converter._dispatch("githubCodeBlocks.before",a,b,c),a+="~0",a=a.replace(/(?:^|\n)```(.*)\n([\s\S]*?)\n```/g,function(a,d,f){var g=b.omitExtraWLInCodeBlocks?"":"\n";return f=e.subParser("encodeCode")(f),f=e.subParser("detab")(f),f=f.replace(/^\n+/g,""),f=f.replace(/\n+$/g,""),f="
"+f+g+"
",f=e.subParser("hashBlock")(f,b,c),"\n\n~G"+(c.ghCodeBlocks.push({text:a,codeblock:f})-1)+"G\n\n"}),a=a.replace(/~0/,""),c.converter._dispatch("githubCodeBlocks.after",a,b,c)):a}),e.subParser("hashBlock",function(a,b,c){"use strict";return a=a.replace(/(^\n+|\n+$)/g,""),"\n\n~K"+(c.gHtmlBlocks.push(a)-1)+"K\n\n"}),e.subParser("hashElement",function(a,b,c){"use strict";return function(a,b){var d=b;return d=d.replace(/\n\n/g,"\n"),d=d.replace(/^\n/,""),d=d.replace(/\n+$/g,""),d="\n\n~K"+(c.gHtmlBlocks.push(d)-1)+"K\n\n"}}),e.subParser("hashHTMLBlocks",function(a,b,c){"use strict";for(var d=["pre","div","h1","h2","h3","h4","h5","h6","blockquote","table","dl","ol","ul","script","noscript","form","fieldset","iframe","math","style","section","header","footer","nav","article","aside","address","audio","canvas","figure","hgroup","output","video","p"],f=function(a,b,d,e){var f=a;return-1!==d.search(/\bmarkdown\b/)&&(f=d+c.converter.makeHtml(b)+e),"\n\n~K"+(c.gHtmlBlocks.push(f)-1)+"K\n\n"},g=0;g]*>","","gim");return a=a.replace(/(\n {0,3}(<(hr)\b([^<>])*?\/?>)[ \t]*(?=\n{2,}))/g,e.subParser("hashElement")(a,b,c)),a=e.helper.replaceRecursiveRegExp(a,function(a){return"\n\n~K"+(c.gHtmlBlocks.push(a)-1)+"K\n\n"},"^ {0,3}","gm"),a=a.replace(/(?:\n\n)( {0,3}(?:<([?%])[^\r]*?\2>)[ \t]*(?=\n{2,}))/g,e.subParser("hashElement")(a,b,c))}),e.subParser("hashHTMLSpans",function(a,b,c){"use strict";for(var d=e.helper.matchRecursiveRegExp(a,"]*>","","gi"),f=0;f]*>\\s*]*>","^ {0,3}\\s*
","gim")}),e.subParser("headers",function(a,b,c){"use strict";function d(a){var b,d;return d=h?a.replace(/ /g,"-").replace(/&/g,"").replace(/~T/g,"").replace(/~D/g,"").replace(/[&+$,\/:;=?@"#{}|^~\[\]`\\*)(%.!'<>]/g,"").toLowerCase():a.replace(/[^\w]/g,"").toLowerCase(),c.hashLinkCounts[d]?b=d+"-"+c.hashLinkCounts[d]++:(b=d,c.hashLinkCounts[d]=1),f===!0&&(f="section"),e.helper.isString(f)?f+b:b}a=c.converter._dispatch("headers.before",a,b,c);var f=b.prefixHeaderId,g=isNaN(parseInt(b.headerLevelStart))?1:parseInt(b.headerLevelStart),h=b.ghCompatibleHeaderId,i=b.smoothLivePreview?/^(.+)[ \t]*\n={2,}[ \t]*\n+/gm:/^(.+)[ \t]*\n=+[ \t]*\n+/gm,j=b.smoothLivePreview?/^(.+)[ \t]*\n-{2,}[ \t]*\n+/gm:/^(.+)[ \t]*\n-+[ \t]*\n+/gm;a=a.replace(i,function(a,f){var h=e.subParser("spanGamut")(f,b,c),i=b.noHeaderId?"":' id="'+d(f)+'"',j=g,k=""+h+"";return e.subParser("hashBlock")(k,b,c)}),a=a.replace(j,function(a,f){var h=e.subParser("spanGamut")(f,b,c),i=b.noHeaderId?"":' id="'+d(f)+'"',j=g+1,k=""+h+"";return e.subParser("hashBlock")(k,b,c)});var k=b.requireSpaceBeforeHeadingText?/^(#{1,6})[ \t]+(.+?)[ \t]*#*\n+/gm:/^(#{1,6})[ \t]*(.+?)[ \t]*#*\n+/gm;return a=a.replace(k,function(a,f,h){var i=e.subParser("spanGamut")(h,b,c),j=b.noHeaderId?"":' id="'+d(h)+'"',k=g-1+f.length,l=""+i+"";return e.subParser("hashBlock")(l,b,c)}),a=c.converter._dispatch("headers.after",a,b,c)}),e.subParser("horizontalRule",function(a,b,c){"use strict";a=c.converter._dispatch("horizontalRule.before",a,b,c);var d=e.subParser("hashBlock")("
",b,c);return a=a.replace(/^ {0,2}( ?-){3,}[ \t]*$/gm,d),a=a.replace(/^ {0,2}( ?\*){3,}[ \t]*$/gm,d),a=a.replace(/^ {0,2}( ?_){3,}[ \t]*$/gm,d),a=c.converter._dispatch("horizontalRule.after",a,b,c)}),e.subParser("images",function(a,b,c){"use strict";function d(a,b,d,f,g,h,i,j){var k=c.gUrls,l=c.gTitles,m=c.gDimensions;if(d=d.toLowerCase(),j||(j=""),""===f||null===f){if((""===d||null===d)&&(d=b.toLowerCase().replace(/ ?\n/g," ")),f="#"+d,e.helper.isUndefined(k[d]))return a;f=k[d],e.helper.isUndefined(l[d])||(j=l[d]),e.helper.isUndefined(m[d])||(g=m[d].width,h=m[d].height)}b=b.replace(/"/g,""").replace(e.helper.regexes.asteriskAndDash,e.helper.escapeCharactersCallback),f=f.replace(e.helper.regexes.asteriskAndDash,e.helper.escapeCharactersCallback);var n=''+b+'?(?: =([*\d]+[A-Za-z%]{0,4})x([*\d]+[A-Za-z%]{0,4}))?[ \t]*(?:(['"])(.*?)\6[ \t]*)?\)/g,g=/!\[([^\]]*?)] ?(?:\n *)?\[(.*?)]()()()()()/g;return a=a.replace(g,d),a=a.replace(f,d),a=c.converter._dispatch("images.after",a,b,c)}),e.subParser("italicsAndBold",function(a,b,c){"use strict";return a=c.converter._dispatch("italicsAndBold.before",a,b,c),b.literalMidWordUnderscores?(a=a.replace(/(^|\s|>|\b)__(?=\S)([\s\S]+?)__(?=\b|<|\s|$)/gm,"$1$2"),a=a.replace(/(^|\s|>|\b)_(?=\S)([\s\S]+?)_(?=\b|<|\s|$)/gm,"$1$2"),a=a.replace(/(\*\*)(?=\S)([^\r]*?\S[*]*)\1/g,"$2"),a=a.replace(/(\*)(?=\S)([^\r]*?\S)\1/g,"$2")):(a=a.replace(/(\*\*|__)(?=\S)([^\r]*?\S[*_]*)\1/g,"$2"),a=a.replace(/(\*|_)(?=\S)([^\r]*?\S)\1/g,"$2")),a=c.converter._dispatch("italicsAndBold.after",a,b,c)}),e.subParser("lists",function(a,b,c){"use strict";function d(a,d){c.gListLevel++,a=a.replace(/\n{2,}$/,"\n"),a+="~0";var f=/(\n)?(^ {0,3})([*+-]|\d+[.])[ \t]+((\[(x|X| )?])?[ \t]*[^\r]+?(\n{1,2}))(?=\n*(~0| {0,3}([*+-]|\d+[.])[ \t]+))/gm,g=/\n[ \t]*\n(?!~0)/.test(a);return b.disableForced4SpacesIndentedSublists&&(f=/(\n)?(^ {0,3})([*+-]|\d+[.])[ \t]+((\[(x|X| )?])?[ \t]*[^\r]+?(\n{1,2}))(?=\n*(~0|\2([*+-]|\d+[.])[ \t]+))/gm),a=a.replace(f,function(a,d,f,h,i,j,k){k=k&&""!==k.trim();var l=e.subParser("outdent")(i,b,c),m="";return j&&b.tasklists&&(m=' class="task-list-item" style="list-style-type: none;"',l=l.replace(/^[ \t]*\[(x|X| )?]/m,function(){var a='-1?(l=e.subParser("githubCodeBlocks")(l,b,c),l=e.subParser("blockGamut")(l,b,c)):(l=e.subParser("lists")(l,b,c),l=l.replace(/\n$/,""),l=e.subParser("hashHTMLBlocks")(l,b,c),l=l.replace(/\n\n+/g,"\n\n"),l=l.replace(/\n\n/g,"~B"),l=g?e.subParser("paragraphs")(l,b,c):e.subParser("spanGamut")(l,b,c),l=l.replace(/~B/g,"\n\n")),l=l.replace("~A",""),l=""+l+"\n"}),a=a.replace(/~0/g,""),c.gListLevel--,d&&(a=a.replace(/\s+$/,"")),a}function f(a,c,e){var f=b.disableForced4SpacesIndentedSublists?/^ ?\d+\.[ \t]/gm:/^ {0,3}\d+\.[ \t]/gm,g=b.disableForced4SpacesIndentedSublists?/^ ?[*+-][ \t]/gm:/^ {0,3}[*+-][ \t]/gm,h="ul"===c?f:g,i="";return-1!==a.search(h)?!function j(a){var b=a.search(h);-1!==b?(i+="\n<"+c+">\n"+d(a.slice(0,b),!!e)+"\n",c="ul"===c?"ol":"ul",h="ul"===c?f:g,j(a.slice(b))):i+="\n<"+c+">\n"+d(a,!!e)+"\n"}(a):i="\n<"+c+">\n"+d(a,!!e)+"\n",i}return a=c.converter._dispatch("lists.before",a,b,c),a+="~0",a=c.gListLevel?a.replace(/^(( {0,3}([*+-]|\d+[.])[ \t]+)[^\r]+?(~0|\n{2,}(?=\S)(?![ \t]*(?:[*+-]|\d+[.])[ \t]+)))/gm,function(a,b,c){var d=c.search(/[*+-]/g)>-1?"ul":"ol";return f(b,d,!0)}):a.replace(/(\n\n|^\n?)(( {0,3}([*+-]|\d+[.])[ \t]+)[^\r]+?(~0|\n{2,}(?=\S)(?![ \t]*(?:[*+-]|\d+[.])[ \t]+)))/gm,function(a,b,c,d){var e=d.search(/[*+-]/g)>-1?"ul":"ol";return f(c,e,!1)}),a=a.replace(/~0/,""),a=c.converter._dispatch("lists.after",a,b,c)}),e.subParser("outdent",function(a){"use strict";return a=a.replace(/^(\t|[ ]{1,4})/gm,"~0"),a=a.replace(/~0/g,"")}),e.subParser("paragraphs",function(a,b,c){"use strict";a=c.converter._dispatch("paragraphs.before",a,b,c),a=a.replace(/^\n+/g,""),a=a.replace(/\n+$/g,"");for(var d=a.split(/\n{2,}/g),f=[],g=d.length,h=0;g>h;h++){var i=d[h];i.search(/~(K|G)(\d+)\1/g)>=0?f.push(i):(i=e.subParser("spanGamut")(i,b,c),i=i.replace(/^([ \t]*)/g,"

"),i+="

",f.push(i))}for(g=f.length,h=0;g>h;h++){for(var j="",k=f[h],l=!1;k.search(/~(K|G)(\d+)\1/)>=0;){var m=RegExp.$1,n=RegExp.$2;j="K"===m?c.gHtmlBlocks[n]:l?e.subParser("encodeCode")(c.ghCodeBlocks[n].text):c.ghCodeBlocks[n].codeblock,j=j.replace(/\$/g,"$$$$"),k=k.replace(/(\n\n)?~(K|G)\d+\2(\n\n)?/,j),/^]*>\s*]*>/.test(k)&&(l=!0)}f[h]=k}return a=f.join("\n"),a=a.replace(/^\n+/g,""),a=a.replace(/\n+$/g,""),c.converter._dispatch("paragraphs.after",a,b,c)}),e.subParser("runExtension",function(a,b,c,d){"use strict";if(a.filter)b=a.filter(b,d.converter,c);else if(a.regex){var e=a.regex;!e instanceof RegExp&&(e=new RegExp(e,"g")),b=b.replace(e,a.replace)}return b}),e.subParser("spanGamut",function(a,b,c){"use strict";return a=c.converter._dispatch("spanGamut.before",a,b,c),a=e.subParser("codeSpans")(a,b,c),a=e.subParser("escapeSpecialCharsWithinTagAttributes")(a,b,c),a=e.subParser("encodeBackslashEscapes")(a,b,c),a=e.subParser("images")(a,b,c),a=e.subParser("anchors")(a,b,c),a=e.subParser("autoLinks")(a,b,c),a=e.subParser("encodeAmpsAndAngles")(a,b,c),a=e.subParser("italicsAndBold")(a,b,c),a=e.subParser("strikethrough")(a,b,c),a=b.simpleLineBreaks?a.replace(/\n/g,"
\n"):a.replace(/ +\n/g,"
\n"),a=c.converter._dispatch("spanGamut.after",a,b,c)}),e.subParser("strikethrough",function(a,b,c){"use strict";return b.strikethrough&&(a=c.converter._dispatch("strikethrough.before",a,b,c),a=a.replace(/(?:~T){2}([\s\S]+?)(?:~T){2}/g,"$1"),a=c.converter._dispatch("strikethrough.after",a,b,c)),a}),e.subParser("stripBlankLines",function(a){"use strict";return a.replace(/^[ \t]+$/gm,"")}),e.subParser("stripLinkDefinitions",function(a,b,c){"use strict";var d=/^ {0,3}\[(.+)]:[ \t]*\n?[ \t]*?(?: =([*\d]+[A-Za-z%]{0,4})x([*\d]+[A-Za-z%]{0,4}))?[ \t]*\n?[ \t]*(?:(\n*)["|'(](.+?)["|')][ \t]*)?(?:\n+|(?=~0))/gm;return a+="~0",a=a.replace(d,function(a,d,f,g,h,i,j){return d=d.toLowerCase(),c.gUrls[d]=e.subParser("encodeAmpsAndAngles")(f),i?i+j:(j&&(c.gTitles[d]=j.replace(/"|'/g,""")),b.parseImgDimensions&&g&&h&&(c.gDimensions[d]={width:g,height:h}),"")}),a=a.replace(/~0/,"")}),e.subParser("tables",function(a,b,c){"use strict";function d(a){return/^:[ \t]*--*$/.test(a)?' style="text-align:left;"':/^--*[ \t]*:[ \t]*$/.test(a)?' style="text-align:right;"':/^:[ \t]*--*[ \t]*:$/.test(a)?' style="text-align:center;"':""}function f(a,d){var f="";return a=a.trim(),b.tableHeaderId&&(f=' id="'+a.replace(/ /g,"_").toLowerCase()+'"'),a=e.subParser("spanGamut")(a,b,c),""+a+"\n"}function g(a,d){var f=e.subParser("spanGamut")(a,b,c);return""+f+"\n"}function h(a,b){for(var c="\n\n\n",d=a.length,e=0;d>e;++e)c+=a[e];for(c+="\n\n\n",e=0;e\n";for(var f=0;d>f;++f)c+=b[e][f];c+="\n"}return c+="\n
\n"}if(!b.tables)return a;var i=/^ {0,3}\|?.+\|.+\n[ \t]{0,3}\|?[ \t]*:?[ \t]*(?:-|=){2,}[ \t]*:?[ \t]*\|[ \t]*:?[ \t]*(?:-|=){2,}[\s\S]+?(?:\n\n|~0)/gm;return a=c.converter._dispatch("tables.before",a,b,c),a=a.replace(i,function(a){var b,c=a.split("\n");for(b=0;b - + - + diff --git a/tpl/page.php b/tpl/page.php index 9ab9d59e..b8c343b0 100644 --- a/tpl/page.php +++ b/tpl/page.php @@ -43,11 +43,11 @@ if ($SYNTAXHIGHLIGHTING): endif; if ($MARKDOWN): ?> - + - + From 368aa2305bf551a9b8ca3c88354c0131d1a1442d Mon Sep 17 00:00:00 2001 From: El RIDO Date: Sun, 29 Jan 2017 16:19:12 +0100 Subject: [PATCH 0023/1638] removing unused pieces of code (legacy?), resolves #165 --- js/privatebin.js | 87 +++--------------------------------------------- js/test.js | 18 ---------- 2 files changed, 5 insertions(+), 100 deletions(-) diff --git a/js/privatebin.js b/js/privatebin.js index c80cd3eb..fed80c3d 100644 --- a/js/privatebin.js +++ b/js/privatebin.js @@ -69,84 +69,6 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { return [v, 'month']; }, - /** - * converts an associative array to an encoded string - * for appending to the anchor - * - * @name helper.hashToParameterString - * @function - * @param {Object} hashMap - Object to be serialized - * @return {string} - */ - hashToParameterString: function(hashMap) - { - var parameterString = ''; - for (var key in hashMap) - { - if (parameterString === '') - { - parameterString = encodeURIComponent(key); - parameterString += '=' + encodeURIComponent(hashMap[key]); - } - else - { - parameterString += '&' + encodeURIComponent(key); - parameterString += '=' + encodeURIComponent(hashMap[key]); - } - } - // padding for URL shorteners - if (parameterString.length > 0) { - parameterString += '&'; - } - parameterString += 'p=p'; - - return parameterString; - }, - - /** - * converts a anchor string to an associative array - * - * @name helper.parameterStringToHash - * @function - * @param {string} parameterString - String containing parameters - * @return {Object} hash map - */ - parameterStringToHash: function(parameterString) - { - var parameterHash = {}; - var parameterArray = parameterString.split('&'); - for (var i = 0; i < parameterArray.length; i++) - { - if (parameterArray[i] != '') { - var pair = parameterArray[i].split('='); - var key = decodeURIComponent(pair[0]); - var value = decodeURIComponent(pair[1]); - parameterHash[key] = value; - } - } - return parameterHash; - }, - - /** - * get an associative array of the parameters found in the anchor - * - * @name helper.getParameterHash - * @function - * @return {Object} - */ - getParameterHash: function() - { - var hashIndex = window.location.href.indexOf('#'); - if (hashIndex >= 0) - { - return this.parameterStringToHash(window.location.href.substring(hashIndex + 1)); - } - else - { - return {}; - } - }, - /** * text range selection * @@ -747,10 +669,11 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { case 'markdown': if (typeof showdown === 'object') { - showdown.setOption('strikethrough', true); - showdown.setOption('tables', true); - showdown.setOption('tablesHeaderId', true); - var converter = new showdown.Converter(); + var converter = new showdown.Converter({ + strikethrough: true, + tables: true, + tablesHeaderId: true + }); this.clearText.html( converter.makeHtml(text) ); diff --git a/js/test.js b/js/test.js index 5208fd3d..b979534f 100644 --- a/js/test.js +++ b/js/test.js @@ -56,23 +56,5 @@ describe('helper', function () { return $.PrivateBin.helper.secondsToHuman(number)[1] === 'month'; }); }); - - describe('hashToParameterString', function () { - jsc.property('returns strings', 'dict nestring', function (dict) { - return typeof $.PrivateBin.helper.hashToParameterString(dict) === 'string'; - }); - }); - - describe('parameterStringToHash', function () { - jsc.property('returns objects', 'string', function (string) { - return typeof $.PrivateBin.helper.parameterStringToHash(string) === 'object'; - }); - jsc.property('decodes hashes generated with hashToParameterString', 'dict nestring', function (dict) { - var result = $.PrivateBin.helper.parameterStringToHash($.PrivateBin.helper.hashToParameterString(dict)); - // padding for URL shorteners - dict.p = 'p'; - return JSON.stringify(result) === JSON.stringify(dict); - }); - }); }); From 4bbfd5045e94b86eb2d73ad3efb39c144d291318 Mon Sep 17 00:00:00 2001 From: El RIDO Date: Mon, 30 Jan 2017 20:29:04 +0100 Subject: [PATCH 0024/1638] ensure that JS is *really* only initialized after the DOM is fully loaded, resolves #166 --- js/privatebin.js | 13 ++++++------- tpl/bootstrap.php | 2 +- tpl/page.php | 2 +- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/js/privatebin.js b/js/privatebin.js index fed80c3d..6ed88f03 100644 --- a/js/privatebin.js +++ b/js/privatebin.js @@ -397,27 +397,26 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { }, /** - * load translations into cache, then execute callback function + * load translations into cache, then trigger controller initialization * * @name i18n.loadTranslations * @function - * @param {Function} callback */ - loadTranslations: function(callback) + loadTranslations: function() { var selectedLang = helper.getCookie('lang'); var language = selectedLang.length > 0 ? selectedLang : (navigator.language || navigator.userLanguage).substring(0, 2); // note that 'en' is built in, so no translation is necessary - if (this.supportedLanguages.indexOf(language) === -1) + if (i18n.supportedLanguages.indexOf(language) === -1) { - callback(); + controller.init(); } else { $.getJSON('i18n/' + language + '.json', function(data) { i18n.language = language; i18n.translations = data; - callback(); + controller.init(); }); } }, @@ -1713,7 +1712,7 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { * main application start, called when DOM is fully loaded and * runs controller initalization after translations are loaded */ - $(i18n.loadTranslations($.proxy(controller.init, controller))); + $(i18n.loadTranslations); return { helper: helper, diff --git a/tpl/bootstrap.php b/tpl/bootstrap.php index 723b3e9b..8938112c 100644 --- a/tpl/bootstrap.php +++ b/tpl/bootstrap.php @@ -69,7 +69,7 @@ if ($MARKDOWN): - + diff --git a/tpl/page.php b/tpl/page.php index b8c343b0..66a814eb 100644 --- a/tpl/page.php +++ b/tpl/page.php @@ -47,7 +47,7 @@ if ($MARKDOWN): - + From 920f02e43d04968b45c5b08401c3857f4e396566 Mon Sep 17 00:00:00 2001 From: El RIDO Date: Mon, 30 Jan 2017 20:36:38 +0100 Subject: [PATCH 0025/1638] fixing composer package versions, thanks @jelhan (#84) --- composer.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/composer.json b/composer.json index 1a80bc5f..b92f4629 100644 --- a/composer.json +++ b/composer.json @@ -18,9 +18,9 @@ } ], "require": { - "php": "^5.2.6 || ^7.0", - "paragonie/random_compat": "^2.0", - "yzalis/identicon": "^1.1" + "php": "^5.3.0 || ^7.0", + "paragonie/random_compat": "2.0.4", + "yzalis/identicon": "1.1.0" }, "require-dev": { "codacy/coverage": "dev-master", From e9b10f9e2daa2c0e69f39ca5af8ebf4585337dbb Mon Sep 17 00:00:00 2001 From: rugk Date: Wed, 1 Feb 2017 18:34:13 +0100 Subject: [PATCH 0026/1638] Add CSP sandbox Fixes https://github.com/PrivateBin/PrivateBin/issues/168 Alos needed to run some Composer stuff, no idea why my diff was different. --- cfg/conf.ini.sample | 3 ++- lib/Configuration.php | 2 +- vendor/composer/autoload_psr4.php | 1 + vendor/composer/autoload_static.php | 8 ++++++++ 4 files changed, 12 insertions(+), 2 deletions(-) diff --git a/cfg/conf.ini.sample b/cfg/conf.ini.sample index 1343df00..0d251c18 100644 --- a/cfg/conf.ini.sample +++ b/cfg/conf.ini.sample @@ -63,7 +63,8 @@ languageselection = false ; custom scripts from third-party domains to your templates, e.g. tracking ; scripts or run your site behind certain DDoS-protection services. ; Check the documentation at https://content-security-policy.com/ -; cspheader = "default-src 'none'; manifest-src 'self'; connect-src *; script-src 'self'; style-src 'self'; font-src 'self'; img-src 'self' data:; referrer no-referrer;" +; Note: If you use a bootstrap theme, you can remove the allow-popups from the sandbox restrictions. +; cspheader = "default-src 'none'; manifest-src 'self'; connect-src *; script-src 'self'; style-src 'self'; font-src 'self'; img-src 'self' data:; referrer no-referrer; sandbox allow-same-origin allow-scripts allow-forms allow-popups" ; stay compatible with PrivateBin Alpha 0.19, less secure ; if enabled will use base64.js version 1.7 instead of 2.1.9 and sha1 instead of diff --git a/lib/Configuration.php b/lib/Configuration.php index 800ea75d..4130a22e 100644 --- a/lib/Configuration.php +++ b/lib/Configuration.php @@ -51,7 +51,7 @@ class Configuration 'languagedefault' => '', 'urlshortener' => '', 'icon' => 'identicon', - 'cspheader' => 'default-src \'none\'; manifest-src \'self\'; connect-src *; script-src \'self\'; style-src \'self\'; font-src \'self\'; img-src \'self\' data:; referrer no-referrer;', + 'cspheader' => 'default-src \'none\'; manifest-src \'self\'; connect-src *; script-src \'self\'; style-src \'self\'; font-src \'self\'; img-src \'self\' data:; referrer no-referrer; sandbox allow-same-origin allow-scripts allow-forms allow-popups', 'zerobincompatibility' => false, ), 'expire' => array( diff --git a/vendor/composer/autoload_psr4.php b/vendor/composer/autoload_psr4.php index 26f0cedc..1bddfbd3 100644 --- a/vendor/composer/autoload_psr4.php +++ b/vendor/composer/autoload_psr4.php @@ -7,4 +7,5 @@ $baseDir = dirname($vendorDir); return array( 'PrivateBin\\' => array($baseDir . '/lib'), + 'CodeClimate\\PhpTestReporter\\' => array($vendorDir . '/codeclimate/php-test-reporter/src'), ); diff --git a/vendor/composer/autoload_static.php b/vendor/composer/autoload_static.php index 001a248f..81b8716e 100644 --- a/vendor/composer/autoload_static.php +++ b/vendor/composer/autoload_static.php @@ -15,6 +15,10 @@ class ComposerStaticInitDontChange array ( 'PrivateBin\\' => 11, ), + 'C' => + array ( + 'CodeClimate\\PhpTestReporter\\' => 28, + ), ); public static $prefixDirsPsr4 = array ( @@ -22,6 +26,10 @@ class ComposerStaticInitDontChange array ( 0 => __DIR__ . '/../..' . '/lib', ), + 'CodeClimate\\PhpTestReporter\\' => + array ( + 0 => __DIR__ . '/..' . '/codeclimate/php-test-reporter/src', + ), ); public static $prefixesPsr0 = array ( From ca51a808035e2b29c5a3168ec5c8afb875b53269 Mon Sep 17 00:00:00 2001 From: rugk Date: Wed, 1 Feb 2017 19:24:56 +0100 Subject: [PATCH 0027/1638] Update the history when a paste is created Fixes https://github.com/PrivateBin/PrivateBin/issues/167 --- js/privatebin.js | 23 +++++++++++++++++++++++ tpl/bootstrap.php | 2 +- 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/js/privatebin.js b/js/privatebin.js index 6ed88f03..5963592a 100644 --- a/js/privatebin.js +++ b/js/privatebin.js @@ -1118,6 +1118,7 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { var deleteUrl = controller.scriptLocation() + '?pasteid=' + data.id + '&deletetoken=' + data.deletetoken; controller.showStatus(''); controller.errorMessage.addClass('hidden'); + history.pushState({type: 'newpaste'}, '', url); $('#pastelink').html( i18n._( @@ -1152,6 +1153,26 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { }); }, + /** + * handle history (pop) state changes + * + * currently this does only handle redirects to the home page. + * + * @name controller.historyChange + * @function + * @param {Event} event + */ + historyChange: function(event) + { + if (event.originalEvent.state === null && // no state object passed + this.scriptLocation() === event.originalEvent.target.location.href && // target location is home page + this.scriptLocation() === window.location.href // and we are not already on the home page + ) { + // redirect to home page + window.location.href = this.scriptLocation(); + } + }, + /** * check if a URL shortener was defined and create HTML containing a link to it * @@ -1619,6 +1640,8 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { this.passwordModal.on('shown.bs.modal', $.proxy(this.passwordDecrypt.focus, this)); this.passwordModal.on('hidden.bs.modal', $.proxy(this.decryptPasswordModal, this)); this.passwordForm.submit($.proxy(this.submitPasswordModal, this)); + + $(window).on('popstate', $.proxy(this.historyChange, this)); }, /** diff --git a/tpl/bootstrap.php b/tpl/bootstrap.php index 8938112c..aa61a02b 100644 --- a/tpl/bootstrap.php +++ b/tpl/bootstrap.php @@ -69,7 +69,7 @@ if ($MARKDOWN): - + From 5442af6e20ba6ba2ad234a582625e52147c31a24 Mon Sep 17 00:00:00 2001 From: El RIDO Date: Sun, 5 Feb 2017 14:47:03 +0100 Subject: [PATCH 0028/1638] slight JS refactoring --- js/privatebin.js | 314 ++++++++++++++++++++++++---------------------- tpl/bootstrap.php | 2 +- tpl/page.php | 2 +- 3 files changed, 168 insertions(+), 150 deletions(-) diff --git a/js/privatebin.js b/js/privatebin.js index 5963592a..e8d8acb2 100644 --- a/js/privatebin.js +++ b/js/privatebin.js @@ -115,8 +115,8 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { { // For IE<10: Doesn't support white-space:pre-wrap; so we have to do this... if ($('#oldienotice').is(':visible')) { - var html = this.htmlEntities(text).replace(/\n/ig,'\r\n
'); - element.html('
'+html+'
'); + var html = this.htmlEntities(text).replace(/\n/ig, '\r\n
'); + element.html('
' + html + '
'); } // for other (sane) browsers: else @@ -230,11 +230,14 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { * @return {string} */ getCookie: function(cname) { - var name = cname + '='; - var ca = document.cookie.split(';'); + var name = cname + '=', + ca = document.cookie.split(';'); for (var i = 0; i < ca.length; ++i) { var c = ca[i]; - while (c.charAt(0) === ' ') c = c.substring(1); + while (c.charAt(0) === ' ') + { + c = c.substring(1); + } if (c.indexOf(name) === 0) { return c.substring(name.length, c.length); @@ -243,6 +246,77 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { return ''; }, + /** + * get the current script location (without search or hash part of the URL), + * eg. http://example.com/path/?aaaa#bbbb --> http://example.com/path/ + * + * @name helper.scriptLocation + * @function + * @return {string} current script location + */ + scriptLocation: function() + { + var scriptLocation = window.location.href.substring( + 0, + window.location.href.length - window.location.search.length - window.location.hash.length + ), hashIndex = scriptLocation.indexOf('#'); + if (hashIndex !== -1) + { + scriptLocation = scriptLocation.substring(0, hashIndex); + } + return scriptLocation; + }, + + /** + * get the pastes unique identifier from the URL, + * eg. http://example.com/path/?c05354954c49a487#c05354954c49a487 returns c05354954c49a487 + * + * @name helper.pasteId + * @function + * @return {string} unique identifier + */ + pasteId: function() + { + return window.location.search.substring(1); + }, + + /** + * return the deciphering key stored in anchor part of the URL + * + * @name helper.pageKey + * @function + * @return {string} key + */ + pageKey: function() + { + // Some web 2.0 services and redirectors add data AFTER the anchor + // (such as &utm_source=...). We will strip any additional data. + + var key = window.location.hash.substring(1), // Get key + i = key.indexOf('='); + + // First, strip everything after the equal sign (=) which signals end of base64 string. + if (i > -1) + { + key = key.substring(0, i + 1); + } + + // If the equal sign was not present, some parameters may remain: + i = key.indexOf('&'); + if (i > -1) + { + key = key.substring(0, i); + } + + // Then add trailing equal sign if it's missing + if (key.charAt(key.length - 1) !== '=') + { + key += '='; + } + + return key; + }, + /** * convert all applicable characters to HTML entities * @@ -404,8 +478,11 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { */ loadTranslations: function() { - var selectedLang = helper.getCookie('lang'); - var language = selectedLang.length > 0 ? selectedLang : (navigator.language || navigator.userLanguage).substring(0, 2); + var language = helper.getCookie('lang'); + if (language.length === 0) + { + language = (navigator.language || navigator.userLanguage).substring(0, 2); + } // note that 'en' is built in, so no translation is necessary if (i18n.supportedLanguages.indexOf(language) === -1) { @@ -555,76 +632,6 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { */ createdPasteUrl: '', - /** - * get the current script location (without search or hash part of the URL), - * eg. http://example.com/zero/?aaaa#bbbb --> http://example.com/zero/ - * - * @name controller.scriptLocation - * @function - * @return {string} current script location - */ - scriptLocation: function() - { - var scriptLocation = window.location.href.substring(0,window.location.href.length - - window.location.search.length - window.location.hash.length), - hashIndex = scriptLocation.indexOf('#'); - if (hashIndex !== -1) - { - scriptLocation = scriptLocation.substring(0, hashIndex); - } - return scriptLocation; - }, - - /** - * get the pastes unique identifier from the URL, - * eg. http://example.com/zero/?c05354954c49a487#c05354954c49a487 returns c05354954c49a487 - * - * @name controller.pasteID - * @function - * @return {string} unique identifier - */ - pasteID: function() - { - return window.location.search.substring(1); - }, - - /** - * return the deciphering key stored in anchor part of the URL - * - * @name controller.pageKey - * @function - * @return {string} key - */ - pageKey: function() - { - // Some web 2.0 services and redirectors add data AFTER the anchor - // (such as &utm_source=...). We will strip any additional data. - - var key = window.location.hash.substring(1), // Get key - i = key.indexOf('='); - - // First, strip everything after the equal sign (=) which signals end of base64 string. - if (i > -1) - { - key = key.substring(0, i + 1); - } - - // If the equal sign was not present, some parameters may remain: - i = key.indexOf('&'); - if (i > -1) - { - key = key.substring(0, i); - } - - // Then add trailing equal sign if it's missing - if (key.charAt(key.length - 1) !== '=') - { - key += '='; - } - - return key; - }, - /** * ask the user for the password and set it * @@ -722,8 +729,8 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { displayMessages: function(paste) { paste = paste || $.parseJSON(this.cipherData.text()); - var key = this.pageKey(); - var password = this.passwordInput.val(); + var key = helper.pageKey(), + password = this.passwordInput.val(); if (!this.prettyPrint.hasClass('prettyprinted')) { // Try to decrypt the paste. try @@ -813,7 +820,7 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { // unfortunately many web servers don't support DELETE (and PUT) out of the box $.ajax({ type: 'POST', - url: this.scriptLocation() + '?' + this.pasteID(), + url: helper.scriptLocation() + '?' + helper.pasteId(), data: {deletetoken: 'burnafterreading'}, dataType: 'json', headers: this.headers @@ -838,24 +845,27 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { // iterate over comments for (var i = 0; i < paste.comments.length; ++i) { - var place = this.comments; - var comment = paste.comments[i]; - var commenttext = filter.decipher(key, password, comment.data); - // if parent comment exists, display below (CSS will automatically shift it to the right) - var cname = '#comment_' + comment.parentid; + var place = this.comments, + comment = paste.comments[i], + commenttext = filter.decipher(key, password, comment.data), + // if parent comment exists, display below (CSS will automatically shift it to the right) + cname = '#comment_' + comment.parentid, + divComment = $('
' + + '
' + + '
' + + '
'), + divCommentData = divComment.find('div.commentdata'); // if the element exists in page if ($(cname).length) { place = $(cname); } - var divComment = $('
' - + '
' - + '' - + '
'); divComment.find('button').click({commentid: comment.id}, $.proxy(this.openReply, this)); - helper.setElementText(divComment.find('div.commentdata'), commenttext); - helper.urls2links(divComment.find('div.commentdata')); + helper.setElementText(divCommentData, commenttext); + helper.urls2links(divCommentData); // try to get optional nickname var nick = filter.decipher(key, password, comment.meta.nickname); @@ -887,7 +897,7 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { '
' ); - divComment.find('button').click({commentid: this.pasteID()}, $.proxy(this.openReply, this)); + divComment.find('button').click({commentid: helper.pasteId()}, $.proxy(this.openReply, this)); this.comments.append(divComment); this.discussion.removeClass('hidden'); } @@ -903,20 +913,26 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { openReply: function(event) { event.preventDefault(); - var source = $(event.target), - commentid = event.data.commentid, - hint = i18n._('Optional nickname...'); // remove any other reply area $('div.reply').remove(); - var reply = $( - '
' + - '' + - '' + - '
' + + var source = $(event.target), + commentid = event.data.commentid, + hint = i18n._('Optional nickname...'), + reply = $( + '

' + + '
' + ); + reply.find('button').click( + {parentid: commentid}, + $.proxy(this.sendComment, this) ); - reply.find('button').click({parentid: commentid}, $.proxy(this.sendComment, this)); source.after(reply); this.replyStatus = $('#replystatus'); $('#replymessage').focus(); @@ -941,24 +957,25 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { } this.showStatus(i18n._('Sending comment...'), true); - var parentid = event.data.parentid; - var cipherdata = filter.cipher(this.pageKey(), this.passwordInput.val(), replyMessage.val()); - var ciphernickname = ''; - var nick = $('#nickname').val(); - if (nick !== '') + var parentid = event.data.parentid, + key = helper.pageKey(), + cipherdata = filter.cipher(key, this.passwordInput.val(), replyMessage.val()), + ciphernickname = '', + nick = $('#nickname').val(); + if (nick.length > 0) { - ciphernickname = filter.cipher(this.pageKey(), this.passwordInput.val(), nick); + ciphernickname = filter.cipher(key, this.passwordInput.val(), nick); } var data_to_send = { data: cipherdata, parentid: parentid, - pasteid: this.pasteID(), + pasteid: helper.pasteId(), nickname: ciphernickname }; $.ajax({ type: 'POST', - url: this.scriptLocation(), + url: helper.scriptLocation(), data: data_to_send, dataType: 'json', headers: this.headers, @@ -969,7 +986,7 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { controller.showStatus(i18n._('Comment posted.')); $.ajax({ type: 'GET', - url: controller.scriptLocation() + '?' + controller.pasteID(), + url: helper.scriptLocation() + '?' + helper.pasteId(), dataType: 'json', headers: controller.headers, success: function(data) @@ -1040,8 +1057,8 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { this.password.addClass('hidden'); this.showStatus(i18n._('Sending paste...'), true); - var randomkey = sjcl.codec.base64.fromBits(sjcl.random.randomWords(8, 0), 0); - var password = this.passwordInput.val(); + var randomkey = sjcl.codec.base64.fromBits(sjcl.random.randomWords(8, 0), 0), + password = this.passwordInput.val(); if(files && files[0]) { if(typeof FileReader === undefined) @@ -1088,14 +1105,14 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { */ sendDataContinue: function(randomkey, cipherdata_attachment, cipherdata_attachment_name) { - var cipherdata = filter.cipher(randomkey, this.passwordInput.val(), this.message.val()); - var data_to_send = { - data: cipherdata, - expire: $('#pasteExpiration').val(), - formatter: $('#pasteFormatter').val(), - burnafterreading: this.burnAfterReading.is(':checked') ? 1 : 0, - opendiscussion: this.openDiscussion.is(':checked') ? 1 : 0 - }; + var cipherdata = filter.cipher(randomkey, this.passwordInput.val(), this.message.val()), + data_to_send = { + data: cipherdata, + expire: $('#pasteExpiration').val(), + formatter: $('#pasteFormatter').val(), + burnafterreading: this.burnAfterReading.is(':checked') ? 1 : 0, + opendiscussion: this.openDiscussion.is(':checked') ? 1 : 0 + }; if (cipherdata_attachment.length > 0) { data_to_send.attachment = cipherdata_attachment; @@ -1106,7 +1123,7 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { } $.ajax({ type: 'POST', - url: this.scriptLocation(), + url: helper.scriptLocation(), data: data_to_send, dataType: 'json', headers: this.headers, @@ -1114,8 +1131,8 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { { if (data.status === 0) { controller.stateExistingPaste(); - var url = controller.scriptLocation() + '?' + data.id + '#' + randomkey; - var deleteUrl = controller.scriptLocation() + '?pasteid=' + data.id + '&deletetoken=' + data.deletetoken; + var url = helper.scriptLocation() + '?' + data.id + '#' + randomkey, + deleteUrl = helper.scriptLocation() + '?pasteid=' + data.id + '&deletetoken=' + data.deletetoken; controller.showStatus(''); controller.errorMessage.addClass('hidden'); history.pushState({type: 'newpaste'}, '', url); @@ -1153,26 +1170,6 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { }); }, - /** - * handle history (pop) state changes - * - * currently this does only handle redirects to the home page. - * - * @name controller.historyChange - * @function - * @param {Event} event - */ - historyChange: function(event) - { - if (event.originalEvent.state === null && // no state object passed - this.scriptLocation() === event.originalEvent.target.location.href && // target location is home page - this.scriptLocation() === window.location.href // and we are not already on the home page - ) { - // redirect to home page - window.location.href = this.scriptLocation(); - } - }, - /** * check if a URL shortener was defined and create HTML containing a link to it * @@ -1325,7 +1322,7 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { reloadPage: function(event) { event.preventDefault(); - window.location.href = this.scriptLocation(); + window.location.href = helper.scriptLocation(); }, /** @@ -1341,8 +1338,8 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { var paste = $('#pasteFormatter').val() === 'markdown' ? this.prettyPrint.text() : this.clearText.text(); history.pushState( - null, document.title, this.scriptLocation() + '?' + - this.pasteID() + '#' + this.pageKey() + null, document.title, helper.scriptLocation() + '?' + + helper.pasteId() + '#' + helper.pageKey() ); // we use text/html instead of text/plain to avoid a bug when // reloading the raw text view (it reverts to type text/html) @@ -1364,7 +1361,7 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { this.stateNewPaste(); // erase the id and the key in url - history.replaceState(null, document.title, this.scriptLocation()); + history.replaceState(null, document.title, helper.scriptLocation()); this.showStatus(''); if (this.attachmentLink.attr('href')) @@ -1485,6 +1482,27 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { this.formatPaste($('#pasteFormatter').val(), this.message.val()); }, + /** + * handle history (pop) state changes + * + * currently this does only handle redirects to the home page. + * + * @name controller.historyChange + * @function + * @param {Event} event + */ + historyChange: function(event) + { + var currentLocation = helper.scriptLocation(); + if (event.originalEvent.state === null && // no state object passed + event.originalEvent.target.location.href === currentLocation && // target location is home page + window.location.href === currentLocation // and we are not already on the home page + ) { + // redirect to home page + window.location.href = currentLocation; + } + }, + /** * create a new paste * diff --git a/tpl/bootstrap.php b/tpl/bootstrap.php index aa61a02b..041b5698 100644 --- a/tpl/bootstrap.php +++ b/tpl/bootstrap.php @@ -69,7 +69,7 @@ if ($MARKDOWN): - + diff --git a/tpl/page.php b/tpl/page.php index 66a814eb..3aab3d69 100644 --- a/tpl/page.php +++ b/tpl/page.php @@ -47,7 +47,7 @@ if ($MARKDOWN): - + From 80f7baa6048384a33b7b4e3d586d3a1e95c39914 Mon Sep 17 00:00:00 2001 From: El RIDO Date: Sun, 5 Feb 2017 16:45:11 +0100 Subject: [PATCH 0029/1638] writing test for scriptLocation function, fixing non-removed query separator bug --- js/privatebin.js | 3 ++- js/test.js | 52 ++++++++++++++++++++++++++++++++++-------------- 2 files changed, 39 insertions(+), 16 deletions(-) diff --git a/js/privatebin.js b/js/privatebin.js index e8d8acb2..708b1855 100644 --- a/js/privatebin.js +++ b/js/privatebin.js @@ -259,7 +259,8 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { var scriptLocation = window.location.href.substring( 0, window.location.href.length - window.location.search.length - window.location.hash.length - ), hashIndex = scriptLocation.indexOf('#'); + ), + hashIndex = scriptLocation.indexOf('?'); if (hashIndex !== -1) { scriptLocation = scriptLocation.substring(0, hashIndex); diff --git a/js/test.js b/js/test.js index b979534f..58a7fdbb 100644 --- a/js/test.js +++ b/js/test.js @@ -1,22 +1,20 @@ 'use strict'; -var jsc = require('jsverify'); - -before(function () { - this.jsdom = require('jsdom-global')(); - global.$ = global.jQuery = require('./jquery-3.1.1'); - global.sjcl = require('./sjcl-1.0.6'); - global.Base64 = require('./base64-2.1.9'); - global.RawDeflate = require('./rawdeflate-0.5'); - require('./rawinflate-0.3'); - require('./privatebin'); -}) - -after(function () { - this.jsdom(); -}) +var jsc = require('jsverify'), + jsdom = require('jsdom-global'), + cleanup = jsdom(); +global.$ = global.jQuery = require('./jquery-3.1.1'); +global.sjcl = require('./sjcl-1.0.6'); +global.Base64 = require('./base64-2.1.9'); +global.RawDeflate = require('./rawdeflate-0.5'); +require('./rawinflate-0.3'); +require('./privatebin'); describe('helper', function () { describe('secondsToHuman', function () { + after(function () { + cleanup(); + }); + jsc.property('returns an array with a number and a word', 'integer', function (number) { var result = $.PrivateBin.helper.secondsToHuman(number); return Array.isArray(result) && @@ -56,5 +54,29 @@ describe('helper', function () { return $.PrivateBin.helper.secondsToHuman(number)[1] === 'month'; }); }); + + describe('scriptLocation', function () { + after(function () { + cleanup(); + }); + + var a2zString = ['a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z'], + alnumString = a2zString.concat(['0','1','2','3','4','5','6','7','8','9']), + queryString = alnumString.concat(['+','%','&','.','*','-','_']); + jsc.property( + 'returns the URL without query & fragment', + jsc.nearray(jsc.elements(a2zString)), + jsc.nearray(jsc.elements(a2zString)), + jsc.array(jsc.elements(queryString)), + 'string', + function (schema, address, query, fragment) { + var expected = schema.join('') + '://' + address.join('') + '/', + clean = jsdom('', {url: expected + '?' + query.join('') + '#' + fragment}), + result = $.PrivateBin.helper.scriptLocation(); + clean(); + return expected === result; + } + ); + }); }); From a97b94640e6f653ff358e1e4ba975187c825d2a4 Mon Sep 17 00:00:00 2001 From: El RIDO Date: Sun, 5 Feb 2017 16:58:58 +0100 Subject: [PATCH 0030/1638] writing test for pasteId function --- js/test.js | 35 +++++++++++++++++++++++++++-------- 1 file changed, 27 insertions(+), 8 deletions(-) diff --git a/js/test.js b/js/test.js index 58a7fdbb..000362c5 100644 --- a/js/test.js +++ b/js/test.js @@ -1,7 +1,13 @@ 'use strict'; var jsc = require('jsverify'), jsdom = require('jsdom-global'), - cleanup = jsdom(); + cleanup = jsdom(), + + a2zString = ['a','b','c','d','e','f','g','h','i','j','k','l','m', + 'n','o','p','q','r','s','t','u','v','w','x','y','z'], + alnumString = a2zString.concat(['0','1','2','3','4','5','6','7','8','9']), + queryString = alnumString.concat(['+','%','&','.','*','-','_']); + global.$ = global.jQuery = require('./jquery-3.1.1'); global.sjcl = require('./sjcl-1.0.6'); global.Base64 = require('./base64-2.1.9'); @@ -56,13 +62,6 @@ describe('helper', function () { }); describe('scriptLocation', function () { - after(function () { - cleanup(); - }); - - var a2zString = ['a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z'], - alnumString = a2zString.concat(['0','1','2','3','4','5','6','7','8','9']), - queryString = alnumString.concat(['+','%','&','.','*','-','_']); jsc.property( 'returns the URL without query & fragment', jsc.nearray(jsc.elements(a2zString)), @@ -78,5 +77,25 @@ describe('helper', function () { } ); }); + + describe('pasteId', function () { + jsc.property( + 'returns the query string without separator, if any', + jsc.nearray(jsc.elements(a2zString)), + jsc.nearray(jsc.elements(a2zString)), + jsc.array(jsc.elements(queryString)), + 'string', + function (schema, address, query, fragment) { + var query = query.join(''), + clean = jsdom('', { + url: schema.join('') + '://' + address.join('') + + '/?' + query + '#' + fragment + }), + result = $.PrivateBin.helper.pasteId(); + clean(); + return query === result; + } + ); + }); }); From 67f71f4dd60ce27c93fa0a73473ac289649b9e36 Mon Sep 17 00:00:00 2001 From: El RIDO Date: Sun, 5 Feb 2017 18:03:42 +0100 Subject: [PATCH 0031/1638] writing tests for pageKey function, fixing always added padding bug --- js/privatebin.js | 21 +++------------------ js/test.js | 45 ++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 47 insertions(+), 19 deletions(-) diff --git a/js/privatebin.js b/js/privatebin.js index 708b1855..31fa44f9 100644 --- a/js/privatebin.js +++ b/js/privatebin.js @@ -290,31 +290,16 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { */ pageKey: function() { + var key = window.location.hash.substring(1), + i = key.indexOf('&'); + // Some web 2.0 services and redirectors add data AFTER the anchor // (such as &utm_source=...). We will strip any additional data. - - var key = window.location.hash.substring(1), // Get key - i = key.indexOf('='); - - // First, strip everything after the equal sign (=) which signals end of base64 string. - if (i > -1) - { - key = key.substring(0, i + 1); - } - - // If the equal sign was not present, some parameters may remain: - i = key.indexOf('&'); if (i > -1) { key = key.substring(0, i); } - // Then add trailing equal sign if it's missing - if (key.charAt(key.length - 1) !== '=') - { - key += '='; - } - return key; }, diff --git a/js/test.js b/js/test.js index 000362c5..a283275f 100644 --- a/js/test.js +++ b/js/test.js @@ -6,7 +6,12 @@ var jsc = require('jsverify'), a2zString = ['a','b','c','d','e','f','g','h','i','j','k','l','m', 'n','o','p','q','r','s','t','u','v','w','x','y','z'], alnumString = a2zString.concat(['0','1','2','3','4','5','6','7','8','9']), - queryString = alnumString.concat(['+','%','&','.','*','-','_']); + queryString = alnumString.concat(['+','%','&','.','*','-','_']), + base64String = alnumString.concat(['+','/','=']).concat( + a2zString.map(function(c) { + return c.toUpperCase(); + }) + ); global.$ = global.jQuery = require('./jquery-3.1.1'); global.sjcl = require('./sjcl-1.0.6'); @@ -97,5 +102,43 @@ describe('helper', function () { } ); }); + + describe('pageKey', function () { + jsc.property( + 'returns the fragment of the URL', + jsc.nearray(jsc.elements(a2zString)), + jsc.nearray(jsc.elements(a2zString)), + jsc.array(jsc.elements(queryString)), + jsc.array(jsc.elements(base64String)), + function (schema, address, query, fragment) { + var fragment = fragment.join(''), + clean = jsdom('', { + url: schema.join('') + '://' + address.join('') + + '/?' + query.join('') + '#' + fragment + }), + result = $.PrivateBin.helper.pageKey(); + clean(); + return fragment === result; + } + ); + jsc.property( + 'returns the fragment stripped of trailing query parts', + jsc.nearray(jsc.elements(a2zString)), + jsc.nearray(jsc.elements(a2zString)), + jsc.array(jsc.elements(queryString)), + jsc.array(jsc.elements(base64String)), + jsc.array(jsc.elements(queryString)), + function (schema, address, query, fragment, trail) { + var fragment = fragment.join(''), + clean = jsdom('', { + url: schema.join('') + '://' + address.join('') + '/?' + + query.join('') + '#' + fragment + '&' + trail.join('') + }), + result = $.PrivateBin.helper.pageKey(); + clean(); + return fragment === result; + } + ); + }); }); From f699ca6cd42a788f84be87321d2065e4f6a23fc1 Mon Sep 17 00:00:00 2001 From: El RIDO Date: Sun, 5 Feb 2017 18:46:24 +0100 Subject: [PATCH 0032/1638] writing tests for htmlEntities function --- js/test.js | 15 +++++++++++++++ tpl/bootstrap.php | 2 +- tpl/page.php | 2 +- 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/js/test.js b/js/test.js index a283275f..5e4d3c39 100644 --- a/js/test.js +++ b/js/test.js @@ -140,5 +140,20 @@ describe('helper', function () { } ); }); + + describe('htmlEntities', function () { + after(function () { + cleanup(); + }); + + jsc.property( + 'removes all HTML entities from any given string', + 'string', + function (string) { + var result = $.PrivateBin.helper.htmlEntities(string); + return !(/[<>"'`=\/]/.test(result)) && !(string.indexOf('&') > -1 && !(/&/.test(result))); + } + ); + }); }); diff --git a/tpl/bootstrap.php b/tpl/bootstrap.php index 041b5698..668b2e91 100644 --- a/tpl/bootstrap.php +++ b/tpl/bootstrap.php @@ -69,7 +69,7 @@ if ($MARKDOWN): - + diff --git a/tpl/page.php b/tpl/page.php index 3aab3d69..362e8878 100644 --- a/tpl/page.php +++ b/tpl/page.php @@ -47,7 +47,7 @@ if ($MARKDOWN): - + From 366b61c32d43d8652bf2ae085deab72f717bcd32 Mon Sep 17 00:00:00 2001 From: El RIDO Date: Sun, 5 Feb 2017 18:53:57 +0100 Subject: [PATCH 0033/1638] adding document title in new history state --- js/privatebin.js | 2 +- tpl/bootstrap.php | 2 +- tpl/page.php | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/js/privatebin.js b/js/privatebin.js index 31fa44f9..850dfc93 100644 --- a/js/privatebin.js +++ b/js/privatebin.js @@ -1121,7 +1121,7 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { deleteUrl = helper.scriptLocation() + '?pasteid=' + data.id + '&deletetoken=' + data.deletetoken; controller.showStatus(''); controller.errorMessage.addClass('hidden'); - history.pushState({type: 'newpaste'}, '', url); + history.pushState({type: 'newpaste'}, document.title, url); $('#pastelink').html( i18n._( diff --git a/tpl/bootstrap.php b/tpl/bootstrap.php index 668b2e91..b4572219 100644 --- a/tpl/bootstrap.php +++ b/tpl/bootstrap.php @@ -69,7 +69,7 @@ if ($MARKDOWN): - + diff --git a/tpl/page.php b/tpl/page.php index 362e8878..491be5da 100644 --- a/tpl/page.php +++ b/tpl/page.php @@ -47,7 +47,7 @@ if ($MARKDOWN): - + From c96dd0836bc9fa88bfd2a436aa5b77eb5a943320 Mon Sep 17 00:00:00 2001 From: rugk Date: Sun, 5 Feb 2017 21:22:09 +0100 Subject: [PATCH 0034/1638] Make link clickable again We need to emulate the click and manually trigger a reload if the hash is already shown in the URL. --- js/privatebin.js | 27 +++++++++++++++++++++++++++ js/test.js | 19 +++++++++---------- tpl/bootstrap.php | 2 +- tpl/page.php | 2 +- 4 files changed, 38 insertions(+), 12 deletions(-) diff --git a/js/privatebin.js b/js/privatebin.js index 850dfc93..4887e8c4 100644 --- a/js/privatebin.js +++ b/js/privatebin.js @@ -1121,6 +1121,7 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { deleteUrl = helper.scriptLocation() + '?pasteid=' + data.id + '&deletetoken=' + data.deletetoken; controller.showStatus(''); controller.errorMessage.addClass('hidden'); + // show new URL in browser bar history.pushState({type: 'newpaste'}, document.title, url); $('#pastelink').html( @@ -1129,6 +1130,11 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { url, url ) + controller.shortenUrl(url) ); + // save newly created element + controller.pasteUrl = $('#pasteurl'); + // and add click event + controller.pasteUrl.click($.proxy(controller.pasteLinkClick, controller)); + var shortenButton = $('#shortenbutton'); if (shortenButton) { shortenButton.click($.proxy(controller.sendToShortener, controller)); @@ -1489,6 +1495,25 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { } }, + /** + * Forces opening the paste if the link does not do this automatically. + * + * This is necessary as browsers will not reload the page when it is + * already loaded (which is fake as it is set via history.pushState()). + * + * @name controller.pasteLinkClick + * @function + * @param {Event} event + */ + pasteLinkClick: function(event) + { + // check if location is (already) correctly shown in URL bar + if (window.location.href === this.pasteUrl.attr('href')) { + // if so we need to load link by reloading the site + location.reload(true); + } + }, + /** * create a new paste * @@ -1689,6 +1714,8 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { this.passwordForm = $('#passwordform'); this.passwordDecrypt = $('#passworddecrypt'); this.pasteResult = $('#pasteresult'); + // this.pasteUrl is saved in sendDataContinue() if/after it is + // actually created this.prettyMessage = $('#prettymessage'); this.prettyPrint = $('#prettyprint'); this.preview = $('#preview'); diff --git a/js/test.js b/js/test.js index 5e4d3c39..b2d5f005 100644 --- a/js/test.js +++ b/js/test.js @@ -91,14 +91,14 @@ describe('helper', function () { jsc.array(jsc.elements(queryString)), 'string', function (schema, address, query, fragment) { - var query = query.join(''), + var queryString = query.join(''), clean = jsdom('', { url: schema.join('') + '://' + address.join('') + - '/?' + query + '#' + fragment + '/?' + queryString + '#' + fragment }), result = $.PrivateBin.helper.pasteId(); clean(); - return query === result; + return queryString === result; } ); }); @@ -111,14 +111,14 @@ describe('helper', function () { jsc.array(jsc.elements(queryString)), jsc.array(jsc.elements(base64String)), function (schema, address, query, fragment) { - var fragment = fragment.join(''), + var fragmentString = fragment.join(''), clean = jsdom('', { url: schema.join('') + '://' + address.join('') + - '/?' + query.join('') + '#' + fragment + '/?' + query.join('') + '#' + fragmentString }), result = $.PrivateBin.helper.pageKey(); clean(); - return fragment === result; + return fragmentString === result; } ); jsc.property( @@ -129,14 +129,14 @@ describe('helper', function () { jsc.array(jsc.elements(base64String)), jsc.array(jsc.elements(queryString)), function (schema, address, query, fragment, trail) { - var fragment = fragment.join(''), + var fragmentString = fragment.join(''), clean = jsdom('', { url: schema.join('') + '://' + address.join('') + '/?' + - query.join('') + '#' + fragment + '&' + trail.join('') + query.join('') + '#' + fragmentString + '&' + trail.join('') }), result = $.PrivateBin.helper.pageKey(); clean(); - return fragment === result; + return fragmentString === result; } ); }); @@ -156,4 +156,3 @@ describe('helper', function () { ); }); }); - diff --git a/tpl/bootstrap.php b/tpl/bootstrap.php index b4572219..cf418712 100644 --- a/tpl/bootstrap.php +++ b/tpl/bootstrap.php @@ -69,7 +69,7 @@ if ($MARKDOWN): - + diff --git a/tpl/page.php b/tpl/page.php index 491be5da..042bc42a 100644 --- a/tpl/page.php +++ b/tpl/page.php @@ -47,7 +47,7 @@ if ($MARKDOWN): - + From 5c603d09788049be222c8fb4f39c3cc16cdfb476 Mon Sep 17 00:00:00 2001 From: rugk Date: Sun, 5 Feb 2017 21:35:28 +0100 Subject: [PATCH 0035/1638] Improve comment --- js/privatebin.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/privatebin.js b/js/privatebin.js index 4887e8c4..3070aad7 100644 --- a/js/privatebin.js +++ b/js/privatebin.js @@ -1507,7 +1507,7 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { */ pasteLinkClick: function(event) { - // check if location is (already) correctly shown in URL bar + // check if location is (already) shown in URL bar if (window.location.href === this.pasteUrl.attr('href')) { // if so we need to load link by reloading the site location.reload(true); From edb546de543b46b34465678d4a0d8c16415562b8 Mon Sep 17 00:00:00 2001 From: rugk Date: Sun, 5 Feb 2017 22:09:46 +0100 Subject: [PATCH 0036/1638] Add loading indicator Fixes https://github.com/PrivateBin/PrivateBin/issues/172 --- js/privatebin.js | 44 ++++++++++++++++++++++++++++++++++++++++++++ tpl/.editorconfig | 9 +++++++++ tpl/bootstrap.php | 6 +++++- tpl/page.php | 2 +- 4 files changed, 59 insertions(+), 2 deletions(-) create mode 100644 tpl/.editorconfig diff --git a/js/privatebin.js b/js/privatebin.js index 6ed88f03..756b20b3 100644 --- a/js/privatebin.js +++ b/js/privatebin.js @@ -1040,12 +1040,16 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { this.password.addClass('hidden'); this.showStatus(i18n._('Sending paste...'), true); + this.stateSubmittingPaste(); + var randomkey = sjcl.codec.base64.fromBits(sjcl.random.randomWords(8, 0), 0); var password = this.passwordInput.val(); if(files && files[0]) { if(typeof FileReader === undefined) { + // revert loading status… + this.stateNewPaste(); this.showError(i18n._('Your browser does not support uploading encrypted files. Please use a newer browser.')); return; } @@ -1138,16 +1142,22 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { } else if (data.status === 1) { + // revert loading status… + controller.stateNewPaste(); controller.showError(i18n._('Could not create paste: %s', data.message)); } else { + // revert loading status… + controller.stateNewPaste(); controller.showError(i18n._('Could not create paste: %s', i18n._('unknown status'))); } } }) .fail(function() { + // revert loading status… + this.stateNewPaste(); controller.showError(i18n._('Could not create paste: %s', i18n._('server error or not responding'))); }); }, @@ -1188,6 +1198,7 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { this.clearText.addClass('hidden'); this.discussion.addClass('hidden'); this.prettyMessage.addClass('hidden'); + this.loadingIndicator.addClass('hidden'); this.sendButton.removeClass('hidden'); this.expiration.removeClass('hidden'); this.formatter.removeClass('hidden'); @@ -1201,6 +1212,37 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { this.message.focus(); }, + /** + * put the screen in mode after submitting a paste + * + * @name controller.stateSubmittingPaste + * @function + */ + stateSubmittingPaste: function() + { + this.message.text(''); + this.attachment.addClass('hidden'); + this.cloneButton.addClass('hidden'); + this.rawTextButton.addClass('hidden'); + this.remainingTime.addClass('hidden'); + this.pasteResult.addClass('hidden'); + this.clearText.addClass('hidden'); + this.discussion.addClass('hidden'); + this.prettyMessage.addClass('hidden'); + this.sendButton.addClass('hidden'); + this.expiration.addClass('hidden'); + this.formatter.addClass('hidden'); + this.burnAfterReadingOption.addClass('hidden'); + this.openDisc.addClass('hidden'); + this.newButton.addClass('hidden'); + this.password.addClass('hidden'); + this.attach.addClass('hidden'); + this.message.addClass('hidden'); + this.preview.addClass('hidden'); + + this.loadingIndicator.removeClass('hidden'); + }, + /** * put the screen in "Existing paste" mode * @@ -1239,6 +1281,7 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { this.message.addClass('hidden'); this.clearText.addClass('hidden'); this.prettyMessage.addClass('hidden'); + this.loadingIndicator.addClass('hidden'); }, /** @@ -1650,6 +1693,7 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { this.fileWrap = $('#filewrap'); this.formatter = $('#formatter'); this.image = $('#image'); + this.loadingIndicator = $('#loadingindicator'); this.message = $('#message'); this.messageEdit = $('#messageedit'); this.messagePreview = $('#messagepreview'); diff --git a/tpl/.editorconfig b/tpl/.editorconfig new file mode 100644 index 00000000..30c7ad2f --- /dev/null +++ b/tpl/.editorconfig @@ -0,0 +1,9 @@ +# editorconfig.org + +root = false + +# special format for PHP templates +[*.php] +indent_style = tab +indent_size = 4 + diff --git a/tpl/bootstrap.php b/tpl/bootstrap.php index 8938112c..23a9550d 100644 --- a/tpl/bootstrap.php +++ b/tpl/bootstrap.php @@ -69,7 +69,7 @@ if ($MARKDOWN): - + @@ -120,6 +120,10 @@ endif; -
+ if ($isCpct): + ?> +
+
- + - + - + - - - - - -
-
-
- - -
+
+
+ + + + +

+
+
+
+ - -

- -
-
-
+
+ +
+
+
+

-

+

+

+ in the browser using 256 bits AES. More information on the project page.', I18n::_($NAME)), PHP_EOL; ?> +

+
+
+ + +if ($isCpct): +?>
- +
@@ -465,12 +464,18 @@ endif;
diff --git a/tpl/page.php b/tpl/page.php index 4ae0b6a7..d7043bcf 100644 --- a/tpl/page.php +++ b/tpl/page.php @@ -47,7 +47,7 @@ if ($MARKDOWN): - + @@ -125,7 +125,7 @@ endif; - #s', + '#]*id="status"[^>]*>.*Paste was properly deleted\.assertRegExp( - '#]*id="errormessage"[^>]*>.*Invalid paste ID\.#', + '#]*id="errormessage"[^>]*>.*Invalid paste ID\.assertRegExp( - '#]*id="errormessage"[^>]*>.*Paste does not exist[^<]*#', + '#]*id="errormessage"[^>]*>.*Paste does not exist, has expired or has been deleted\.assertRegExp( - '#]*id="errormessage"[^>]*>.*Wrong deletion token[^<]*#', + '#]*id="errormessage"[^>]*>.*Wrong deletion token\. Paste was not deleted\.assertRegExp( - '#]*id="errormessage"[^>]*>.*Paste does not exist[^<]*#', + '#]*id="errormessage"[^>]*>.*Paste does not exist, has expired or has been deleted\.assertRegExp( - '#]*id="status"[^>]*>.*Paste was properly deleted[^<]*#s', + '#]*id="status"[^>]*>.*Paste was properly deleted\.assertRegExp( - '#]+id="errormessage"[^>]*>.*' . self::$error . '#', + '#]+id="errormessage"[^>]*>.*' . self::$error . ' Date: Sun, 5 Mar 2017 11:02:18 +0100 Subject: [PATCH 0084/1638] credited Tulio for the portuguese translation, updated SRI hashes --- CHANGELOG.md | 2 +- CREDITS.md | 1 + js/privatebin.js | 2 +- lib/I18n.php | 2 +- tpl/bootstrap.php | 2 +- tpl/page.php | 2 +- 6 files changed, 6 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2dc2e3c7..b10a3a39 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,7 @@ # PrivateBin version history * **next (not yet released)** - * ADDED: Translations for Spanish, Occitan and Norwegian + * ADDED: Translations for Spanish, Occitan, Norwegian and Portuguese * ADDED: Option in configuration to change the default "PrivateBin" title of the site * CHANGED: Cleanup of bootstrap template variants and moved icons to `img` directory * **1.1 (2016-12-26)** diff --git a/CREDITS.md b/CREDITS.md index dfb2d83e..1c7ec3cc 100644 --- a/CREDITS.md +++ b/CREDITS.md @@ -35,3 +35,4 @@ Sébastien Sauvage - original idea and main developer * Alfredo Fabián Altamirano Tena - Spanish * Quent-in - Occitan * idarlund - Norwegian +* Tulio Leao - Portuguese diff --git a/js/privatebin.js b/js/privatebin.js index 2fc88278..6508bec4 100644 --- a/js/privatebin.js +++ b/js/privatebin.js @@ -450,7 +450,7 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { return (n % 10 === 1 && n % 100 !== 11 ? 0 : (n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 10 || n % 100 >= 20) ? 1 : 2)); case 'sl': return (n % 100 === 1 ? 1 : (n % 100 === 2 ? 2 : (n % 100 === 3 || n % 100 === 4 ? 3 : 0))); - // de, en, es, it, no + // de, en, es, it, no, pt default: return (n !== 1 ? 1 : 0); } diff --git a/lib/I18n.php b/lib/I18n.php index 4c59ef57..d35bcf01 100644 --- a/lib/I18n.php +++ b/lib/I18n.php @@ -304,7 +304,7 @@ class I18n return $n % 10 == 1 && $n % 100 != 11 ? 0 : ($n % 10 >= 2 && $n % 10 <= 4 && ($n % 100 < 10 || $n % 100 >= 20) ? 1 : 2); case 'sl': return $n % 100 == 1 ? 1 : ($n % 100 == 2 ? 2 : ($n % 100 == 3 || $n % 100 == 4 ? 3 : 0)); - // de, en, es, it, no + // de, en, es, it, no, pt default: return $n != 1 ? 1 : 0; } diff --git a/tpl/bootstrap.php b/tpl/bootstrap.php index 4bf3ca1d..04f2726b 100644 --- a/tpl/bootstrap.php +++ b/tpl/bootstrap.php @@ -69,7 +69,7 @@ if ($MARKDOWN): - + diff --git a/tpl/page.php b/tpl/page.php index 4ae0b6a7..2bdbf913 100644 --- a/tpl/page.php +++ b/tpl/page.php @@ -47,7 +47,7 @@ if ($MARKDOWN): - + From bd32a73d21579cc547eb39f7340d96f12d7d40f7 Mon Sep 17 00:00:00 2001 From: El RIDO Date: Sun, 5 Mar 2017 11:10:52 +0100 Subject: [PATCH 0085/1638] remove Safari link on bootstrap template, too --- tpl/bootstrap.php | 3 +-- tpl/page.php | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/tpl/bootstrap.php b/tpl/bootstrap.php index 04f2726b..cdd45258 100644 --- a/tpl/bootstrap.php +++ b/tpl/bootstrap.php @@ -422,8 +422,7 @@ endif; Date: Mon, 13 Mar 2017 21:15:52 +0100 Subject: [PATCH 0098/1638] comply with codacys suggestion --- js/privatebin.js | 2 +- tpl/bootstrap.php | 2 +- tpl/page.php | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/js/privatebin.js b/js/privatebin.js index a7abee7a..10b6a3a7 100644 --- a/js/privatebin.js +++ b/js/privatebin.js @@ -3610,7 +3610,7 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { password = Prompt.getPassword(); // if password is there, re-try - if (password.length == 0) { + if (password.length === 0) { password = Prompt.requestPassword(); } // recursive diff --git a/tpl/bootstrap.php b/tpl/bootstrap.php index 87def4ca..a80e175c 100644 --- a/tpl/bootstrap.php +++ b/tpl/bootstrap.php @@ -69,7 +69,7 @@ if ($MARKDOWN): - + diff --git a/tpl/page.php b/tpl/page.php index 90c63850..00906305 100644 --- a/tpl/page.php +++ b/tpl/page.php @@ -47,7 +47,7 @@ if ($MARKDOWN): - + From 8f13dffd7c1b1b8c40cc0698390549e882ac46e2 Mon Sep 17 00:00:00 2001 From: Kyodev Date: Fri, 17 Mar 2017 22:23:01 +0100 Subject: [PATCH 0099/1638] fr translation completed --- i18n/fr.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/i18n/fr.json b/i18n/fr.json index 97c4e245..61e0a3c6 100644 --- a/i18n/fr.json +++ b/i18n/fr.json @@ -149,12 +149,12 @@ "Editor": "Éditer", "Preview": "Prévisualiser", "%s requires the PATH to end in a \"%s\". Please update the PATH in your index.php.": - "%s requires the PATH to end in a \"%s\". Please update the PATH in your index.php.", + "%s requiert que le PATH se termine dans un \"%s\". Veuillez mettre à jour le PATH dans votre index.php.", "Decrypt": - "Decrypt", + "Déchiffrer", "Enter password": "Entrez le mot de passe", - "Loading…": "Loading…", + "Loading…": "Chargement…", "In case this message never disappears please have a look at this FAQ for information to troubleshoot.": - "In case this message never disappears please have a look at this FAQ for information to troubleshoot (in English)." + "Si ce message ne disparaîssait pas, jetez un oeil à cette FAQ pour des idées de résolution (en Anglais)." } From 21df49c7cd0d1c74cb670fa5fa96d38aeea65df0 Mon Sep 17 00:00:00 2001 From: Simone Esposito Date: Tue, 21 Mar 2017 20:44:46 +0100 Subject: [PATCH 0100/1638] README: Fix some grammar mistakes --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 0086f461..a259cb5d 100644 --- a/README.md +++ b/README.md @@ -16,10 +16,10 @@ Data is encrypted/decrypted in the browser using 256bit AES in [Galois Counter m This is a fork of ZeroBin, originally developed by [Sébastien Sauvage](https://github.com/sebsauvage/ZeroBin). It was refactored -to allow easier and cleaner extensions and has now much more features than the +to allow easier and cleaner extensions and has now many more features than the original. It is however still fully compatible to the original ZeroBin 0.19 data storage scheme. Therefore such installations can be upgraded to this fork -without loosing any data. +without losing any data. ## What PrivateBin provides From 037a312b8f1e5fef890d176cbb7a0ac9b3570df1 Mon Sep 17 00:00:00 2001 From: Stefano Martinelli Date: Wed, 22 Mar 2017 20:27:19 +0100 Subject: [PATCH 0101/1638] Italian translation update New message translated, couple of minor errors fixed and translation of row 70 reverted to a more faithful meaning. --- i18n/it.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/i18n/it.json b/i18n/it.json index df5aee2a..06bfb237 100644 --- a/i18n/it.json +++ b/i18n/it.json @@ -67,7 +67,7 @@ "Never": "Mai", "Note: This is a test service: Data may be deleted anytime. Kittens will die if you abuse this service.": - "Nota: questo è un servizio di prova, i dati possono essere cancellati in qualsiasi momento. Ti preghiamo di non abusare di questo servizio, grazie.", + "Nota: questo è un servizio di prova, i messaggi salvati possono essere cancellati in qualsiasi momento. Moriranno dei gattini se abuserai di questo servizio.", "This document will expire in %d seconds.": ["Questo documento scadrà tra un secondo.", "Questo documento scadrà in %d secondi."], "This document will expire in %d minutes.": @@ -87,13 +87,13 @@ "FOR YOUR EYES ONLY. Don't close this window, this message can't be displayed again.": "FOR YOUR EYES ONLY. Non chiudere questa finestra, il messaggio non può essere visualizzato una seconda volta.", "Could not decrypt comment; Wrong key?": - "Non riesco a decifrari il commento (Chiave errata?)", + "Non riesco a decifrare il commento (Chiave errata?)", "Reply": "Rispondi", "Anonymous": "Anonimo", "Anonymous avatar (Vizhash of the IP address)": - "Avatar Anonino (Vizhash dell'indirizzo IP)", + "Avatar Anonimo (Vizhash dell'indirizzo IP)", "Add comment": "Aggiungi un commento", "Optional nickname…": @@ -147,5 +147,5 @@ "Inserisci la password", "Loading…": "Loading…", "In case this message never disappears please have a look at this FAQ for information to troubleshoot.": - "In case this message never disappears please have a look at this FAQ for information to troubleshoot (in English)." + "Nel caso questo messaggio non scompaia, controlla questa FAQ per trovare informazioni su come risolvere il problema (in Inglese)." } From e58261b3f8f0e0bc2a7fe70ba48a1dfabfbeb523 Mon Sep 17 00:00:00 2001 From: R4SAS Date: Thu, 23 Mar 2017 18:36:08 +0300 Subject: [PATCH 0102/1638] update ru translation --- i18n/ru.json | 34 ++++++++++++++++++++++------------ 1 file changed, 22 insertions(+), 12 deletions(-) diff --git a/i18n/ru.json b/i18n/ru.json index 7e92da78..82ae2d10 100644 --- a/i18n/ru.json +++ b/i18n/ru.json @@ -7,8 +7,8 @@ "en": "ru", "Paste does not exist, has expired or has been deleted.": "Запись не существует, просрочена или была удалена.", - "%s requires php 5.3.0 or above to work. Sorry.": - "Для работы %s требуется PHP 5.3.0 или выше. Извините.", + "%s requires php 5.4.0 or above to work. Sorry.": + "Для работы %s требуется PHP 5.4.0 или выше. Извините.", "%s requires configuration section [%s] to be present in configuration file.": "%s необходимо наличие секции [%s] в конфигурационном файле.", "Please wait %d seconds between each post.": @@ -32,7 +32,7 @@ "Paste was properly deleted.": "Запись была успешно удалена.", "JavaScript is required for %s to work.
Sorry for the inconvenience.": - "Для работы %s требуется включенный JavaScript.
Приносим извинения за неудобства..", + "Для работы %s требуется включенный JavaScript.
Приносим извинения за неудобства.", "%s requires a modern browser to work.": "Для работы %s требуется более современный браузер.", "Still using Internet Explorer? Do yourself a favor, switch to a modern browser:": @@ -97,25 +97,25 @@ "Add comment": "Добавить комментарий", "Optional nickname…": - "Опциональный никнейм…", + "Опциональный никнейм...", "Post comment": "Отправить комментарий", "Sending comment…": - "Отправка комментария…", + "Отправка комментария...", "Comment posted.": "Комментарий опубликован.", - "Could not refresh display: %s": - "Невозможно обновить данные: %s", "unknown status": "неизвестная причина", "server error or not responding": "ошибка сервера или нет ответа", + "unknown error": + "неизвестная ошибка", "Could not post comment: %s": "Не удалось опубликовать комментарий: %s", - "Sending paste (Please move your mouse for more entropy)…": - "Отправка записи (Пожалуйста двигайте мышкой для большей энтропии)…", + "Please move your mouse for more entropy…": + "Пожалуйста двигайте мышкой для большей энтропии...", "Sending paste…": - "Отправка записи…", + "Отправка записи...", "Your paste is %s (Hit [Ctrl]+[c] to copy)": "Ссылка на запись %s (Нажмите [Ctrl]+[c] чтобы скопировать ссылку)", "Delete data": @@ -138,7 +138,10 @@ "Source Code": "Исходный код", "Markdown": "Язык разметки", "Download attachment": "Скачать прикрепленный файл", - "Cloned file attached.": "Дубль файла прикреплен.", + "Cloned file attached.": "Дубликат файла прикреплен.", + "Cloned: '%s'": "Дублировано: '%s'", + "Дубликат файла '%s' был прикреплен к этой записи.": + "Die geklonte Datei '%s' wurde angehängt.", "Attach a file": "Прикрепить файл", "Remove attachment": "Удалить вложение", "Your browser does not support uploading encrypted files. Please use a newer browser.": @@ -155,5 +158,12 @@ "Enter password": "Введите пароль", "Uploading paste… Please wait.": - "Отправка записи… Пожалуйста подождите." + "Отправка записи... Пожалуйста подождите.", + "Loading…": "Загрузка...", + "Decrypting paste…": "Расшифровка записи...", + "Preparing new paste…": "Подготовка новой записи...", + "In case this message never disappears please have a look at this FAQ for information to troubleshoot.": + "Если данное сообщение не исчезает длительное время, посмотрите этот FAQ с информацией о возможном решении проблемы.", + "+++ no paste text +++": + "+++ в записи нет текста +++" } From 39bb2fa389b4e95fdc1c2a97410849d05bfd6b8f Mon Sep 17 00:00:00 2001 From: Stefano Martinelli Date: Thu, 23 Mar 2017 17:36:13 +0100 Subject: [PATCH 0103/1638] php minimum version updated As per instructions, minimum version updated from 5.3.0 to 5.4.0 --- i18n/it.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/i18n/it.json b/i18n/it.json index 06bfb237..30a80e97 100644 --- a/i18n/it.json +++ b/i18n/it.json @@ -7,8 +7,8 @@ "en": "it", "Paste does not exist, has expired or has been deleted.": "Questo messaggio non esiste, è scaduto o è stato cancellato.", - "%s requires php 5.3.0 or above to work. Sorry.": - "%s richiede PHP 5.3.0 o superiore.", + "%s requires php 5.4.0 or above to work. Sorry.": + "%s richiede php 5.4.0 o superiore per funzionare. Ci spiace.", "%s requires configuration section [%s] to be present in configuration file.": "%s richiede la presenza della sezione [%s] nei file di configurazione.", "Please wait %d seconds between each post.": From 01701efd56569bd102cbc3f867a00acb99923feb Mon Sep 17 00:00:00 2001 From: R4SAS Date: Thu, 23 Mar 2017 21:20:52 +0300 Subject: [PATCH 0104/1638] changed three dots to unicode symbol, added note about english --- i18n/ru.json | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/i18n/ru.json b/i18n/ru.json index 82ae2d10..707a41b1 100644 --- a/i18n/ru.json +++ b/i18n/ru.json @@ -97,11 +97,11 @@ "Add comment": "Добавить комментарий", "Optional nickname…": - "Опциональный никнейм...", + "Опциональный никнейм…", "Post comment": "Отправить комментарий", "Sending comment…": - "Отправка комментария...", + "Отправка комментария…", "Comment posted.": "Комментарий опубликован.", "unknown status": @@ -113,9 +113,9 @@ "Could not post comment: %s": "Не удалось опубликовать комментарий: %s", "Please move your mouse for more entropy…": - "Пожалуйста двигайте мышкой для большей энтропии...", + "Пожалуйста двигайте мышкой для большей энтропии…", "Sending paste…": - "Отправка записи...", + "Отправка записи…", "Your paste is %s (Hit [Ctrl]+[c] to copy)": "Ссылка на запись %s (Нажмите [Ctrl]+[c] чтобы скопировать ссылку)", "Delete data": @@ -158,12 +158,12 @@ "Enter password": "Введите пароль", "Uploading paste… Please wait.": - "Отправка записи... Пожалуйста подождите.", - "Loading…": "Загрузка...", - "Decrypting paste…": "Расшифровка записи...", - "Preparing new paste…": "Подготовка новой записи...", + "Отправка записи… Пожалуйста подождите.", + "Loading…": "Загрузка…", + "Decrypting paste…": "Расшифровка записи…", + "Preparing new paste…": "Подготовка новой записи…", "In case this message never disappears please have a look at this FAQ for information to troubleshoot.": - "Если данное сообщение не исчезает длительное время, посмотрите этот FAQ с информацией о возможном решении проблемы.", + "Если данное сообщение не исчезает длительное время, посмотрите этот FAQ с информацией о возможном решении проблемы (на английском).", "+++ no paste text +++": "+++ в записи нет текста +++" } From 9a788d63ee999706d42a3ca6a330e596552b9c6c Mon Sep 17 00:00:00 2001 From: R4SAS Date: Thu, 23 Mar 2017 21:25:44 +0300 Subject: [PATCH 0105/1638] fixed wrong translated line --- i18n/ru.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/i18n/ru.json b/i18n/ru.json index 707a41b1..ae60abbd 100644 --- a/i18n/ru.json +++ b/i18n/ru.json @@ -140,8 +140,8 @@ "Download attachment": "Скачать прикрепленный файл", "Cloned file attached.": "Дубликат файла прикреплен.", "Cloned: '%s'": "Дублировано: '%s'", - "Дубликат файла '%s' был прикреплен к этой записи.": - "Die geklonte Datei '%s' wurde angehängt.", + "The cloned file '%s' was attached to this paste.": + "Дубликат файла '%s' был прикреплен к этой записи.", "Attach a file": "Прикрепить файл", "Remove attachment": "Удалить вложение", "Your browser does not support uploading encrypted files. Please use a newer browser.": From 02e0b8655d622394cbd1398fa43b5fc3d78516ba Mon Sep 17 00:00:00 2001 From: TMs Date: Fri, 24 Mar 2017 11:32:12 +0100 Subject: [PATCH 0106/1638] update zh translation --- i18n/zh.json | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/i18n/zh.json b/i18n/zh.json index 41efcc45..d779c04e 100644 --- a/i18n/zh.json +++ b/i18n/zh.json @@ -7,8 +7,8 @@ "en": "zh", "Paste does not exist, has expired or has been deleted.": "粘贴不存在,已过期或者已被删除。", - "%s requires php 5.3.0 or above to work. Sorry.": - "%s需要工作于PHP 5.3.0及以上版本,抱歉。", + "%s requires php 5.4.0 or above to work. Sorry.": + "%s需要工作于PHP 5.4.0及以上版本,抱歉。", "%s requires configuration section [%s] to be present in configuration file.": "%s需要设置配置文件中 [%s] 的部分。", "Please wait %d seconds between each post.": @@ -92,8 +92,8 @@ "回复", "Anonymous": "匿名", - "Anonymous avatar (Vizhash of the IP address)": - "匿名头像 (由IP地址生成Vizhash)", + "Avatar generated from IP address": + "由IP生成的头像", "Add comment": "添加评论", "Optional nickname…": @@ -112,8 +112,8 @@ "服务器错误或无回应", "Could not post comment: %s": "无法发送评论: %s", - "Sending paste (Please move your mouse for more entropy)…": - "粘贴提交中 (请移动鼠标以产生更多熵)…", + "Please move your mouse for more entropy…": + "请移动鼠标增加随机性…", "Sending paste…": "粘贴提交中…", "Your paste is %s (Hit [Ctrl]+[c] to copy)": @@ -129,7 +129,8 @@ "Source Code": "源代码", "Markdown": "Markdown", "Download attachment": "下载附件", - "Cloned file attached.": "已附加克隆的文件", + "Cloned: '%s'": "克隆: '%s'", + "The cloned file '%s' was attached to this paste.": "克隆文件 '%s' 已附加到此粘贴。", "Attach a file": "添加一个附件", "Remove attachment": "移除附件", "Your browser does not support uploading encrypted files. Please use a newer browser.": @@ -146,6 +147,9 @@ "Enter password": "输入密码", "Loading…": "载入中…", + "Decrypting paste…": "正在解密", + "Preparing new paste…": "正在准备新的粘贴", "In case this message never disappears please have a look at this FAQ for information to troubleshoot.": - "如果这个消息一直不消失,请参考 这里的 FAQ 进行故障排除 (英文版)。" + "如果这个消息一直不消失,请参考 这里的 FAQ 进行故障排除 (英文版)。", + "+++ no paste text +++": "+++ 没有粘贴内容 +++" } From a6b9bc68798a7f03c2410c59749f914507673d41 Mon Sep 17 00:00:00 2001 From: R4SAS Date: Fri, 24 Mar 2017 17:50:53 +0300 Subject: [PATCH 0107/1638] replace php version to constant, #186 #201 --- i18n/ru.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/i18n/ru.json b/i18n/ru.json index ae60abbd..ca4c707b 100644 --- a/i18n/ru.json +++ b/i18n/ru.json @@ -7,8 +7,8 @@ "en": "ru", "Paste does not exist, has expired or has been deleted.": "Запись не существует, просрочена или была удалена.", - "%s requires php 5.4.0 or above to work. Sorry.": - "Для работы %s требуется PHP 5.4.0 или выше. Извините.", + "%s requires php %s or above to work. Sorry.": + "Для работы %s требуется php %s или выше. Извините.", "%s requires configuration section [%s] to be present in configuration file.": "%s необходимо наличие секции [%s] в конфигурационном файле.", "Please wait %d seconds between each post.": From 88b02d866e779b9b88c22a71554723aee50115e7 Mon Sep 17 00:00:00 2001 From: El RIDO Date: Fri, 24 Mar 2017 19:20:34 +0100 Subject: [PATCH 0108/1638] fixes #186 for good --- INSTALL.md | 2 +- composer.json | 2 +- i18n/de.json | 4 ++-- i18n/es.json | 4 ++-- i18n/fr.json | 4 ++-- i18n/it.json | 4 ++-- i18n/no.json | 4 ++-- i18n/oc.json | 4 ++-- i18n/pl.json | 4 ++-- i18n/pt.json | 4 ++-- i18n/sl.json | 4 ++-- i18n/zh.json | 4 ++-- lib/PrivateBin.php | 11 +++++++++-- 13 files changed, 31 insertions(+), 24 deletions(-) diff --git a/INSTALL.md b/INSTALL.md index 5dbc5095..b627bc9e 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -10,7 +10,7 @@ check the options and adjust them as you see fit. ### Requirements -- PHP version 5.3 or above +- PHP version 5.4 or above - _one_ of the following sources of cryptographically safe randomness is required: - PHP 7 or higher - [Libsodium](https://download.libsodium.org/libsodium/content/installation/) and it's [PHP extension](https://paragonie.com/book/pecl-libsodium/read/00-intro.md#installing-libsodium) diff --git a/composer.json b/composer.json index 632bf2b7..4248b5f4 100644 --- a/composer.json +++ b/composer.json @@ -18,7 +18,7 @@ } ], "require": { - "php": "^5.3.0 || ^7.0", + "php": "^5.4.0 || ^7.0", "paragonie/random_compat": "2.0.4", "yzalis/identicon": "1.1.0" }, diff --git a/i18n/de.json b/i18n/de.json index 9959e717..7974d773 100644 --- a/i18n/de.json +++ b/i18n/de.json @@ -7,8 +7,8 @@ "en": "de", "Paste does not exist, has expired or has been deleted.": "Diesen Text gibt es nicht, er ist abgelaufen oder wurde gelöscht.", - "%s requires php 5.3.0 or above to work. Sorry.": - "%s benötigt PHP 5.3.0 oder höher, um zu funktionieren. Sorry.", + "%s requires php %s or above to work. Sorry.": + "%s benötigt PHP %s oder höher, um zu funktionieren. Sorry.", "%s requires configuration section [%s] to be present in configuration file.": "%s benötigt den Konfigurationsabschnitt [%s] in der Konfigurationsdatei um zu funktionieren.", "Please wait %d seconds between each post.": diff --git a/i18n/es.json b/i18n/es.json index 1e2fd48f..e957c8f8 100644 --- a/i18n/es.json +++ b/i18n/es.json @@ -7,8 +7,8 @@ "en": "es", "Paste does not exist, has expired or has been deleted.": "El texto no existe, ha caducado o ha sido eliminado.", - "%s requires php 5.3.0 or above to work. Sorry.": - "%s requiere php 5.3.0 o superior para funcionar. Lo siento.", + "%s requires php %s or above to work. Sorry.": + "%s requiere php %s o superior para funcionar. Lo siento.", "%s requires configuration section [%s] to be present in configuration file.": "%s requiere que la sección de configuración [%s] esté presente en el archivo de configuración.", "Please wait %d seconds between each post.": diff --git a/i18n/fr.json b/i18n/fr.json index 61e0a3c6..35cfd659 100644 --- a/i18n/fr.json +++ b/i18n/fr.json @@ -7,8 +7,8 @@ "en": "fr", "Paste does not exist, has expired or has been deleted.": "Le paste n'existe pas, a expiré, ou a été supprimé.", - "%s requires php 5.3.0 or above to work. Sorry.": - "Désolé, %s nécessite php 5.3.0 ou supérieur pour fonctionner.", + "%s requires php %s or above to work. Sorry.": + "Désolé, %s nécessite php %s ou supérieur pour fonctionner.", "%s requires configuration section [%s] to be present in configuration file.": "%s a besoin de la section de configuration [%s] dans le fichier de configuration pour fonctionner.", "Please wait %d seconds between each post.": diff --git a/i18n/it.json b/i18n/it.json index 30a80e97..dc56fa75 100644 --- a/i18n/it.json +++ b/i18n/it.json @@ -7,8 +7,8 @@ "en": "it", "Paste does not exist, has expired or has been deleted.": "Questo messaggio non esiste, è scaduto o è stato cancellato.", - "%s requires php 5.4.0 or above to work. Sorry.": - "%s richiede php 5.4.0 o superiore per funzionare. Ci spiace.", + "%s requires php %s or above to work. Sorry.": + "%s richiede php %s o superiore per funzionare. Ci spiace.", "%s requires configuration section [%s] to be present in configuration file.": "%s richiede la presenza della sezione [%s] nei file di configurazione.", "Please wait %d seconds between each post.": diff --git a/i18n/no.json b/i18n/no.json index 4d92cc83..fd56583d 100644 --- a/i18n/no.json +++ b/i18n/no.json @@ -7,8 +7,8 @@ "en": "no", "Paste does not exist, has expired or has been deleted.": "Innlegget eksisterer ikke, er utløpt eller har blitt slettet.", - "%s requires php 5.3.0 or above to work. Sorry.": - "Beklager, %s krever php 5.3.0 eller nyere for å kjøre.", + "%s requires php %s or above to work. Sorry.": + "Beklager, %s krever php %s eller nyere for å kjøre.", "%s requires configuration section [%s] to be present in configuration file.": "%s krever konfigurasjonsdel [%s] å være til stede i konfigurasjonsfilen .", "Please wait %d seconds between each post.": diff --git a/i18n/oc.json b/i18n/oc.json index a29bce03..b6b449de 100644 --- a/i18n/oc.json +++ b/i18n/oc.json @@ -7,8 +7,8 @@ "en": "oc", "Paste does not exist, has expired or has been deleted.": "Lo tèxte existís pas, a expirat, o es estat suprimit.", - "%s requires php 5.3.0 or above to work. Sorry.": - "O planhèm, %s necessita php 5.3.0 o superior per foncionar.", + "%s requires php %s or above to work. Sorry.": + "O planhèm, %s necessita php %s o superior per foncionar.", "%s requires configuration section [%s] to be present in configuration file.": "%s fa besonh de la seccion de configuracion [%s] dins lo fichièr de configuracion per foncionar.", "Please wait %d seconds between each post.": diff --git a/i18n/pl.json b/i18n/pl.json index b9cc8f2a..99554981 100644 --- a/i18n/pl.json +++ b/i18n/pl.json @@ -7,8 +7,8 @@ "en": "pl", "Paste does not exist, has expired or has been deleted.": "Wklejka nie istnieje, wygasła albo została usunięta.", - "%s requires php 5.3.0 or above to work. Sorry.": - "%s wymaga PHP w wersji 5.3.0 lub nowszej, sorry.", + "%s requires php %s or above to work. Sorry.": + "%s wymaga PHP w wersji %s lub nowszej, sorry.", "%s requires configuration section [%s] to be present in configuration file.": "%s wymaga obecności sekcji [%s] w pliku konfiguracyjnym.", "Please wait %d seconds between each post.": diff --git a/i18n/pt.json b/i18n/pt.json index e00a4a15..ab459524 100644 --- a/i18n/pt.json +++ b/i18n/pt.json @@ -7,8 +7,8 @@ "en": "pt", "Paste does not exist, has expired or has been deleted.": "A cópia não existe, expirou ou já foi excluída.", - "%s requires php 5.3.0 or above to work. Sorry.": - "%s requer php 5.3.0 ou superior para funcionar. Desculpa.", + "%s requires php %s or above to work. Sorry.": + "%s requer php %s ou superior para funcionar. Desculpa.", "%s requires configuration section [%s] to be present in configuration file.": "%s requer que a seção de configuração [% s] esteja no arquivo de configuração.", "Please wait %d seconds between each post.": diff --git a/i18n/sl.json b/i18n/sl.json index 2df26087..00b09702 100644 --- a/i18n/sl.json +++ b/i18n/sl.json @@ -7,8 +7,8 @@ "en": "sl", "Paste does not exist, has expired or has been deleted.": "Prilepek ne obstaja, mu je potekla življenjska doba, ali pa je izbrisan.", - "%s requires php 5.3.0 or above to work. Sorry.": - "Oprosti, %s za delovanje potrebuje vsaj php 5.3.0.", + "%s requires php %s or above to work. Sorry.": + "Oprosti, %s za delovanje potrebuje vsaj php %s.", "%s requires configuration section [%s] to be present in configuration file.": "%s potrebuje sekcijo konfiguracij [%s] v konfiguracijski datoteki.", "Please wait %d seconds between each post.": diff --git a/i18n/zh.json b/i18n/zh.json index d779c04e..5fcaf3db 100644 --- a/i18n/zh.json +++ b/i18n/zh.json @@ -7,8 +7,8 @@ "en": "zh", "Paste does not exist, has expired or has been deleted.": "粘贴不存在,已过期或者已被删除。", - "%s requires php 5.4.0 or above to work. Sorry.": - "%s需要工作于PHP 5.4.0及以上版本,抱歉。", + "%s requires php %s or above to work. Sorry.": + "%s需要工作于PHP %s及以上版本,抱歉。", "%s requires configuration section [%s] to be present in configuration file.": "%s需要设置配置文件中 [%s] 的部分。", "Please wait %d seconds between each post.": diff --git a/lib/PrivateBin.php b/lib/PrivateBin.php index fb3e523f..e874015f 100644 --- a/lib/PrivateBin.php +++ b/lib/PrivateBin.php @@ -30,6 +30,13 @@ class PrivateBin */ const VERSION = '1.1'; + /** + * minimal required PHP version + * + * @const string + */ + const MIN_PHP_VERSION = '5.4.0'; + /** * show the same error message if the paste expired or does not exist * @@ -120,8 +127,8 @@ class PrivateBin */ public function __construct() { - if (version_compare(PHP_VERSION, '5.4.0') < 0) { - throw new Exception(I18n::_('%s requires php 5.4.0 or above to work. Sorry.', I18n::_('PrivateBin')), 1); + if (version_compare(PHP_VERSION, self::MIN_PHP_VERSION) < 0) { + throw new Exception(I18n::_('%s requires php %s or above to work. Sorry.', I18n::_('PrivateBin'), self::MIN_PHP_VERSION), 1); } if (strlen(PATH) < 0 && substr(PATH, -1) !== DIRECTORY_SEPARATOR) { throw new Exception(I18n::_('%s requires the PATH to end in a "%s". Please update the PATH in your index.php.', I18n::_('PrivateBin'), DIRECTORY_SEPARATOR), 5); From 850b28931f1c3edbda41dce0c078fcf4b554d83f Mon Sep 17 00:00:00 2001 From: Kyodev Date: Fri, 24 Mar 2017 19:56:52 +0100 Subject: [PATCH 0109/1638] php minimum version updated --- i18n/fr.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/i18n/fr.json b/i18n/fr.json index 61e0a3c6..c889eaf3 100644 --- a/i18n/fr.json +++ b/i18n/fr.json @@ -7,8 +7,8 @@ "en": "fr", "Paste does not exist, has expired or has been deleted.": "Le paste n'existe pas, a expiré, ou a été supprimé.", - "%s requires php 5.3.0 or above to work. Sorry.": - "Désolé, %s nécessite php 5.3.0 ou supérieur pour fonctionner.", + "%s requires php 5.4.0 or above to work. Sorry.": + "Désolé, %s nécessite php 5.4.0 ou supérieur pour fonctionner.", "%s requires configuration section [%s] to be present in configuration file.": "%s a besoin de la section de configuration [%s] dans le fichier de configuration pour fonctionner.", "Please wait %d seconds between each post.": From 1cb1c1ced8d7ea68e2e49fc825d4673d625615c3 Mon Sep 17 00:00:00 2001 From: Kyodev Date: Fri, 24 Mar 2017 20:34:35 +0100 Subject: [PATCH 0110/1638] php minimum version + constant version --- i18n/fr.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/i18n/fr.json b/i18n/fr.json index c889eaf3..35cfd659 100644 --- a/i18n/fr.json +++ b/i18n/fr.json @@ -7,8 +7,8 @@ "en": "fr", "Paste does not exist, has expired or has been deleted.": "Le paste n'existe pas, a expiré, ou a été supprimé.", - "%s requires php 5.4.0 or above to work. Sorry.": - "Désolé, %s nécessite php 5.4.0 ou supérieur pour fonctionner.", + "%s requires php %s or above to work. Sorry.": + "Désolé, %s nécessite php %s ou supérieur pour fonctionner.", "%s requires configuration section [%s] to be present in configuration file.": "%s a besoin de la section de configuration [%s] dans le fichier de configuration pour fonctionner.", "Please wait %d seconds between each post.": From ce92bfa934fd4df5d517216f31fe8c6aa97d51ff Mon Sep 17 00:00:00 2001 From: El RIDO Date: Fri, 24 Mar 2017 21:30:08 +0100 Subject: [PATCH 0111/1638] updated .htaccess format, refactored .htaccess creation logic and improving code coverage, fixes #194 --- .gitignore | 1 + lib/Data/Filesystem.php | 30 ++++++++------ lib/Persistence/AbstractPersistence.php | 9 ++--- lib/PrivateBin.php | 11 ----- tst/.gitignore | 1 - tst/.htaccess | 2 - tst/Data/FilesystemTest.php | 33 +++++++++++++++ tst/ModelTest.php | 53 +++++++++++++++++++++++++ tst/PrivateBinTest.php | 21 +++++----- tst/SjclTest.php | 2 + 10 files changed, 119 insertions(+), 44 deletions(-) delete mode 100644 tst/.gitignore delete mode 100644 tst/.htaccess diff --git a/.gitignore b/.gitignore index 9f09f532..a752f8cc 100644 --- a/.gitignore +++ b/.gitignore @@ -31,6 +31,7 @@ vendor/**/build_phar.php # Ignore local node modules, unit testing logs, api docs and eclipse project files js/node_modules/ tst/log/ +tst/ConfigurationCombinationsTest.php .settings .buildpath .project diff --git a/lib/Data/Filesystem.php b/lib/Data/Filesystem.php index ca9befb3..393d3d80 100644 --- a/lib/Data/Filesystem.php +++ b/lib/Data/Filesystem.php @@ -12,6 +12,7 @@ namespace PrivateBin\Data; +use Exception; use PrivateBin\Json; use PrivateBin\Model\Paste; @@ -41,16 +42,16 @@ class Filesystem extends AbstractData */ public static function getInstance($options = null) { + // if needed initialize the singleton + if (!(self::$_instance instanceof self)) { + self::$_instance = new self; + } // if given update the data directory if ( is_array($options) && array_key_exists('dir', $options) ) { self::$_dir = $options['dir'] . DIRECTORY_SEPARATOR; - } - // if needed initialize the singleton - if (!(self::$_instance instanceof self)) { - self::$_instance = new self; self::_init(); } return self::$_instance; @@ -293,7 +294,7 @@ class Filesystem extends AbstractData } /** - * initialize privatebin + * Initialize data store * * @access private * @static @@ -303,15 +304,20 @@ class Filesystem extends AbstractData { // Create storage directory if it does not exist. if (!is_dir(self::$_dir)) { - mkdir(self::$_dir, 0700); + if (!@mkdir(self::$_dir, 0700)) { + throw new Exception('unable to create directory ' . self::$_dir, 10); + } } - // Create .htaccess file if it does not exist. - if (!is_file(self::$_dir . '.htaccess')) { - file_put_contents( - self::$_dir . '.htaccess', - 'Allow from none' . PHP_EOL . - 'Deny from all' . PHP_EOL + $file = self::$_dir . DIRECTORY_SEPARATOR . '.htaccess'; + if (!is_file($file)) { + $writtenBytes = @file_put_contents( + $file, + 'Require all denied' . PHP_EOL, + LOCK_EX ); + if ($writtenBytes === false || $writtenBytes < 19) { + throw new Exception('unable to write to file ' . $file, 11); + } } } diff --git a/lib/Persistence/AbstractPersistence.php b/lib/Persistence/AbstractPersistence.php index 9aaa70bc..68f148fd 100644 --- a/lib/Persistence/AbstractPersistence.php +++ b/lib/Persistence/AbstractPersistence.php @@ -86,21 +86,18 @@ abstract class AbstractPersistence { // Create storage directory if it does not exist. if (!is_dir(self::$_path)) { - if (!@mkdir(self::$_path)) { + if (!@mkdir(self::$_path, 0700)) { throw new Exception('unable to create directory ' . self::$_path, 10); } } - - // Create .htaccess file if it does not exist. $file = self::$_path . DIRECTORY_SEPARATOR . '.htaccess'; if (!is_file($file)) { $writtenBytes = @file_put_contents( $file, - 'Allow from none' . PHP_EOL . - 'Deny from all' . PHP_EOL, + 'Require all denied' . PHP_EOL, LOCK_EX ); - if ($writtenBytes === false || $writtenBytes < 30) { + if ($writtenBytes === false || $writtenBytes < 19) { throw new Exception('unable to write to file ' . $file, 11); } } diff --git a/lib/PrivateBin.php b/lib/PrivateBin.php index e874015f..92072ea4 100644 --- a/lib/PrivateBin.php +++ b/lib/PrivateBin.php @@ -175,17 +175,6 @@ class PrivateBin */ private function _init() { - foreach (array('cfg', 'lib') as $dir) { - if (!is_file(PATH . $dir . DIRECTORY_SEPARATOR . '.htaccess')) { - file_put_contents( - PATH . $dir . DIRECTORY_SEPARATOR . '.htaccess', - 'Allow from none' . PHP_EOL . - 'Deny from all' . PHP_EOL, - LOCK_EX - ); - } - } - $this->_conf = new Configuration; $this->_model = new Model($this->_conf); $this->_request = new Request; diff --git a/tst/.gitignore b/tst/.gitignore deleted file mode 100644 index 39ef6b92..00000000 --- a/tst/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/ConfigurationCombinationsTest.php diff --git a/tst/.htaccess b/tst/.htaccess deleted file mode 100644 index b584d98c..00000000 --- a/tst/.htaccess +++ /dev/null @@ -1,2 +0,0 @@ -Allow from none -Deny from all diff --git a/tst/Data/FilesystemTest.php b/tst/Data/FilesystemTest.php index 95029217..7cf5ee81 100644 --- a/tst/Data/FilesystemTest.php +++ b/tst/Data/FilesystemTest.php @@ -8,16 +8,26 @@ class FilesystemTest extends PHPUnit_Framework_TestCase private $_path; + private $_invalidPath; + public function setUp() { /* Setup Routine */ $this->_path = sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'privatebin_data'; + $this->_invalidPath = $this->_path . DIRECTORY_SEPARATOR . 'bar'; $this->_model = Filesystem::getInstance(array('dir' => $this->_path)); + if (!is_dir($this->_path)) { + mkdir($this->_path); + } + if (!is_dir($this->_invalidPath)) { + mkdir($this->_invalidPath); + } } public function tearDown() { /* Tear Down Routine */ + chmod($this->_invalidPath, 0700); Helper::rmDir($this->_path); } @@ -37,6 +47,7 @@ class FilesystemTest extends PHPUnit_Framework_TestCase $this->assertFalse($this->_model->existsComment(Helper::getPasteId(), Helper::getPasteId(), Helper::getCommentId()), 'comment does not yet exist'); $this->assertTrue($this->_model->createComment(Helper::getPasteId(), Helper::getPasteId(), Helper::getCommentId(), Helper::getComment()), 'store comment'); $this->assertTrue($this->_model->existsComment(Helper::getPasteId(), Helper::getPasteId(), Helper::getCommentId()), 'comment exists after storing it'); + $this->assertFalse($this->_model->createComment(Helper::getPasteId(), Helper::getPasteId(), Helper::getCommentId(), Helper::getComment()), 'unable to store the same comment twice'); $comment = json_decode(json_encode(Helper::getComment())); $comment->id = Helper::getCommentId(); $comment->parentid = Helper::getPasteId(); @@ -127,4 +138,26 @@ class FilesystemTest extends PHPUnit_Framework_TestCase $this->assertFalse($this->_model->createComment(Helper::getPasteId(), Helper::getPasteId(), Helper::getCommentId(), $comment), 'unable to store broken comment'); $this->assertFalse($this->_model->existsComment(Helper::getPasteId(), Helper::getPasteId(), Helper::getCommentId()), 'comment does still not exist'); } + + /** + * @expectedException Exception + * @expectedExceptionCode 10 + */ + public function testPermissionShenanigans() + { + // try creating an invalid path + chmod($this->_invalidPath, 0000); + Filesystem::getInstance(array('dir' => $this->_invalidPath . DIRECTORY_SEPARATOR . 'baz')); + } + + /** + * @expectedException Exception + * @expectedExceptionCode 11 + */ + public function testPathShenanigans() + { + // try setting an invalid path + chmod($this->_invalidPath, 0000); + Filesystem::getInstance(array('dir' => $this->_invalidPath)); + } } diff --git a/tst/ModelTest.php b/tst/ModelTest.php index 8f7a40b0..ac4e92fc 100644 --- a/tst/ModelTest.php +++ b/tst/ModelTest.php @@ -82,6 +82,7 @@ class ModelTest extends PHPUnit_Framework_TestCase $comment = $paste->getComment(Helper::getPasteId()); $comment->setData($commentData['data']); $comment->setNickname($commentData['meta']['nickname']); + $comment->getParentId(); $comment->store(); $comment = $paste->getComment(Helper::getPasteId(), Helper::getCommentId()); @@ -189,6 +190,27 @@ class ModelTest extends PHPUnit_Framework_TestCase $this->assertFalse(Paste::isValidId('../bar/baz'), 'path attack'); } + /** + * @expectedException Exception + * @expectedExceptionCode 64 + */ + public function testInvalidPaste() + { + $this->_model->getPaste(Helper::getPasteId())->delete(); + $paste = $this->_model->getPaste(Helper::getPasteId()); + $paste->get(); + } + + /** + * @expectedException Exception + * @expectedExceptionCode 61 + */ + public function testInvalidData() + { + $paste = $this->_model->getPaste(); + $paste->setData(''); + } + /** * @expectedException Exception * @expectedExceptionCode 62 @@ -199,6 +221,37 @@ class ModelTest extends PHPUnit_Framework_TestCase $paste->getComment(Helper::getPasteId()); } + /** + * @expectedException Exception + * @expectedExceptionCode 67 + */ + public function testInvalidCommentDeletedPaste() + { + $pasteData = Helper::getPaste(); + $paste = $this->_model->getPaste(Helper::getPasteId()); + $paste->setData($pasteData['data']); + $paste->store(); + + $comment = $paste->getComment(Helper::getPasteId()); + $paste->delete(); + $comment->store(); + } + + /** + * @expectedException Exception + * @expectedExceptionCode 68 + */ + public function testInvalidCommentData() + { + $pasteData = Helper::getPaste(); + $paste = $this->_model->getPaste(Helper::getPasteId()); + $paste->setData($pasteData['data']); + $paste->store(); + + $comment = $paste->getComment(Helper::getPasteId()); + $comment->store(); + } + public function testExpiration() { $pasteData = Helper::getPaste(); diff --git a/tst/PrivateBinTest.php b/tst/PrivateBinTest.php index fbf5b603..a8aad11a 100644 --- a/tst/PrivateBinTest.php +++ b/tst/PrivateBinTest.php @@ -140,21 +140,18 @@ class PrivateBinTest extends PHPUnit_Framework_TestCase public function testHtaccess() { $this->reset(); - $dirs = array('cfg', 'lib'); - foreach ($dirs as $dir) { - $file = PATH . $dir . DIRECTORY_SEPARATOR . '.htaccess'; - @unlink($file); - } + $file = $this->_path . DIRECTORY_SEPARATOR . '.htaccess'; + @unlink($file); + + $_POST = Helper::getPaste(); + $_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest'; + $_SERVER['REQUEST_METHOD'] = 'POST'; + $_SERVER['REMOTE_ADDR'] = '::1'; ob_start(); new PrivateBin; ob_end_clean(); - foreach ($dirs as $dir) { - $file = PATH . $dir . DIRECTORY_SEPARATOR . '.htaccess'; - $this->assertFileExists( - $file, - "$dir htaccess recreated" - ); - } + + $this->assertFileExists($file, 'htaccess recreated'); } /** diff --git a/tst/SjclTest.php b/tst/SjclTest.php index 54cc30fc..a9d947ec 100644 --- a/tst/SjclTest.php +++ b/tst/SjclTest.php @@ -1,11 +1,13 @@ assertTrue(Sjcl::isValid($paste['data']), 'valid sjcl'); $this->assertTrue(Sjcl::isValid($paste['attachment']), 'valid sjcl'); From 6db9dae66b405e841a71eb1ffd4405fdf66befbe Mon Sep 17 00:00:00 2001 From: El RIDO Date: Fri, 24 Mar 2017 21:35:50 +0100 Subject: [PATCH 0112/1638] applying styleCI recommendations --- tst/Data/FilesystemTest.php | 4 ++-- tst/ModelTest.php | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tst/Data/FilesystemTest.php b/tst/Data/FilesystemTest.php index 7cf5ee81..fe012c41 100644 --- a/tst/Data/FilesystemTest.php +++ b/tst/Data/FilesystemTest.php @@ -13,9 +13,9 @@ class FilesystemTest extends PHPUnit_Framework_TestCase public function setUp() { /* Setup Routine */ - $this->_path = sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'privatebin_data'; + $this->_path = sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'privatebin_data'; $this->_invalidPath = $this->_path . DIRECTORY_SEPARATOR . 'bar'; - $this->_model = Filesystem::getInstance(array('dir' => $this->_path)); + $this->_model = Filesystem::getInstance(array('dir' => $this->_path)); if (!is_dir($this->_path)) { mkdir($this->_path); } diff --git a/tst/ModelTest.php b/tst/ModelTest.php index ac4e92fc..4d314f78 100644 --- a/tst/ModelTest.php +++ b/tst/ModelTest.php @@ -228,7 +228,7 @@ class ModelTest extends PHPUnit_Framework_TestCase public function testInvalidCommentDeletedPaste() { $pasteData = Helper::getPaste(); - $paste = $this->_model->getPaste(Helper::getPasteId()); + $paste = $this->_model->getPaste(Helper::getPasteId()); $paste->setData($pasteData['data']); $paste->store(); @@ -244,7 +244,7 @@ class ModelTest extends PHPUnit_Framework_TestCase public function testInvalidCommentData() { $pasteData = Helper::getPaste(); - $paste = $this->_model->getPaste(Helper::getPasteId()); + $paste = $this->_model->getPaste(Helper::getPasteId()); $paste->setData($pasteData['data']); $paste->store(); From f7853cf439df613838d7c3fe710f423956f943a2 Mon Sep 17 00:00:00 2001 From: El RIDO Date: Fri, 24 Mar 2017 23:42:11 +0100 Subject: [PATCH 0113/1638] removing duplicate code, cleanup of temporary test files --- lib/Data/Filesystem.php | 74 +++++++++-------------------------- lib/Persistence/DataStore.php | 48 +++++++++++++++++++++++ tst/Bootstrap.php | 28 +++++++------ tst/Data/FilesystemTest.php | 30 -------------- tst/JsonApiTest.php | 2 + tst/RequestTest.php | 1 + 6 files changed, 85 insertions(+), 98 deletions(-) create mode 100644 lib/Persistence/DataStore.php diff --git a/lib/Data/Filesystem.php b/lib/Data/Filesystem.php index 393d3d80..09844052 100644 --- a/lib/Data/Filesystem.php +++ b/lib/Data/Filesystem.php @@ -12,9 +12,8 @@ namespace PrivateBin\Data; -use Exception; -use PrivateBin\Json; use PrivateBin\Model\Paste; +use PrivateBin\Persistence\DataStore; /** * Filesystem @@ -23,15 +22,6 @@ use PrivateBin\Model\Paste; */ class Filesystem extends AbstractData { - /** - * directory where data is stored - * - * @access private - * @static - * @var string - */ - private static $_dir = 'data/'; - /** * get instance of singleton * @@ -51,8 +41,7 @@ class Filesystem extends AbstractData is_array($options) && array_key_exists('dir', $options) ) { - self::$_dir = $options['dir'] . DIRECTORY_SEPARATOR; - self::_init(); + DataStore::setPath($options['dir']); } return self::$_instance; } @@ -63,19 +52,19 @@ class Filesystem extends AbstractData * @access public * @param string $pasteid * @param array $paste - * @throws Exception * @return bool */ public function create($pasteid, $paste) { $storagedir = self::_dataid2path($pasteid); - if (is_file($storagedir . $pasteid)) { + $file = $storagedir . $pasteid; + if (is_file($file)) { return false; } if (!is_dir($storagedir)) { mkdir($storagedir, 0700, true); } - return (bool) file_put_contents($storagedir . $pasteid, Json::encode($paste)); + return DataStore::store($file, $paste); } /** @@ -156,20 +145,19 @@ class Filesystem extends AbstractData * @param string $parentid * @param string $commentid * @param array $comment - * @throws Exception * @return bool */ public function createComment($pasteid, $parentid, $commentid, $comment) { $storagedir = self::_dataid2discussionpath($pasteid); - $filename = $pasteid . '.' . $commentid . '.' . $parentid; - if (is_file($storagedir . $filename)) { + $file = $storagedir . $pasteid . '.' . $commentid . '.' . $parentid; + if (is_file($file)) { return false; } if (!is_dir($storagedir)) { mkdir($storagedir, 0700, true); } - return (bool) file_put_contents($storagedir . $filename, Json::encode($comment)); + return DataStore::store($file, $comment); } /** @@ -238,8 +226,9 @@ class Filesystem extends AbstractData protected function _getExpiredPastes($batchsize) { $pastes = array(); + $mainpath = DataStore::getPath(); $firstLevel = array_filter( - scandir(self::$_dir), + scandir($mainpath), 'self::_isFirstLevelDir' ); if (count($firstLevel) > 0) { @@ -247,7 +236,7 @@ class Filesystem extends AbstractData for ($i = 0, $max = $batchsize * 10; $i < $max; ++$i) { $firstKey = array_rand($firstLevel); $secondLevel = array_filter( - scandir(self::$_dir . $firstLevel[$firstKey]), + scandir($mainpath . DIRECTORY_SEPARATOR . $firstLevel[$firstKey]), 'self::_isSecondLevelDir' ); @@ -258,8 +247,9 @@ class Filesystem extends AbstractData } $secondKey = array_rand($secondLevel); - $path = self::$_dir . $firstLevel[$firstKey] . - DIRECTORY_SEPARATOR . $secondLevel[$secondKey]; + $path = $mainpath . DIRECTORY_SEPARATOR . + $firstLevel[$firstKey] . DIRECTORY_SEPARATOR . + $secondLevel[$secondKey]; if (!is_dir($path)) { continue; } @@ -293,34 +283,6 @@ class Filesystem extends AbstractData return $pastes; } - /** - * Initialize data store - * - * @access private - * @static - * @return void - */ - private static function _init() - { - // Create storage directory if it does not exist. - if (!is_dir(self::$_dir)) { - if (!@mkdir(self::$_dir, 0700)) { - throw new Exception('unable to create directory ' . self::$_dir, 10); - } - } - $file = self::$_dir . DIRECTORY_SEPARATOR . '.htaccess'; - if (!is_file($file)) { - $writtenBytes = @file_put_contents( - $file, - 'Require all denied' . PHP_EOL, - LOCK_EX - ); - if ($writtenBytes === false || $writtenBytes < 19) { - throw new Exception('unable to write to file ' . $file, 11); - } - } - } - /** * Convert paste id to storage path. * @@ -338,8 +300,10 @@ class Filesystem extends AbstractData */ private static function _dataid2path($dataid) { - return self::$_dir . substr($dataid, 0, 2) . DIRECTORY_SEPARATOR . - substr($dataid, 2, 2) . DIRECTORY_SEPARATOR; + return DataStore::getPath( + substr($dataid, 0, 2) . DIRECTORY_SEPARATOR . + substr($dataid, 2, 2) . DIRECTORY_SEPARATOR + ); } /** @@ -369,7 +333,7 @@ class Filesystem extends AbstractData private static function _isFirstLevelDir($element) { return self::_isSecondLevelDir($element) && - is_dir(self::$_dir . DIRECTORY_SEPARATOR . $element); + is_dir(DataStore::getPath($element)); } /** diff --git a/lib/Persistence/DataStore.php b/lib/Persistence/DataStore.php new file mode 100644 index 00000000..27c7131d --- /dev/null +++ b/lib/Persistence/DataStore.php @@ -0,0 +1,48 @@ +read())) { - if ($file != '.' && $file != '..') { - if (is_dir($path . $file)) { - self::rmDir($path . $file); - } elseif (is_file($path . $file)) { - if (!unlink($path . $file)) { - throw new Exception('Error deleting file "' . $path . $file . '".'); + if (is_dir($path)) { + $path .= DIRECTORY_SEPARATOR; + $dir = dir($path); + while (false !== ($file = $dir->read())) { + if ($file != '.' && $file != '..') { + if (is_dir($path . $file)) { + self::rmDir($path . $file); + } elseif (is_file($path . $file)) { + if (!unlink($path . $file)) { + throw new Exception('Error deleting file "' . $path . $file . '".'); + } } } } - } - $dir->close(); - if (!rmdir($path)) { - throw new Exception('Error deleting directory "' . $path . '".'); + $dir->close(); + if (!rmdir($path)) { + throw new Exception('Error deleting directory "' . $path . '".'); + } } } diff --git a/tst/Data/FilesystemTest.php b/tst/Data/FilesystemTest.php index fe012c41..e7e6dc82 100644 --- a/tst/Data/FilesystemTest.php +++ b/tst/Data/FilesystemTest.php @@ -110,10 +110,6 @@ class FilesystemTest extends PHPUnit_Framework_TestCase } } - /** - * @expectedException Exception - * @expectedExceptionCode 90 - */ public function testErrorDetection() { $this->_model->delete(Helper::getPasteId()); @@ -123,10 +119,6 @@ class FilesystemTest extends PHPUnit_Framework_TestCase $this->assertFalse($this->_model->exists(Helper::getPasteId()), 'paste does still not exist'); } - /** - * @expectedException Exception - * @expectedExceptionCode 90 - */ public function testCommentErrorDetection() { $this->_model->delete(Helper::getPasteId()); @@ -138,26 +130,4 @@ class FilesystemTest extends PHPUnit_Framework_TestCase $this->assertFalse($this->_model->createComment(Helper::getPasteId(), Helper::getPasteId(), Helper::getCommentId(), $comment), 'unable to store broken comment'); $this->assertFalse($this->_model->existsComment(Helper::getPasteId(), Helper::getPasteId(), Helper::getCommentId()), 'comment does still not exist'); } - - /** - * @expectedException Exception - * @expectedExceptionCode 10 - */ - public function testPermissionShenanigans() - { - // try creating an invalid path - chmod($this->_invalidPath, 0000); - Filesystem::getInstance(array('dir' => $this->_invalidPath . DIRECTORY_SEPARATOR . 'baz')); - } - - /** - * @expectedException Exception - * @expectedExceptionCode 11 - */ - public function testPathShenanigans() - { - // try setting an invalid path - chmod($this->_invalidPath, 0000); - Filesystem::getInstance(array('dir' => $this->_invalidPath)); - } } diff --git a/tst/JsonApiTest.php b/tst/JsonApiTest.php index 5cf13608..a5928893 100644 --- a/tst/JsonApiTest.php +++ b/tst/JsonApiTest.php @@ -98,6 +98,7 @@ class JsonApiTest extends PHPUnit_Framework_TestCase new PrivateBin; $content = ob_get_contents(); ob_end_clean(); + unlink($file); $response = json_decode($content, true); $this->assertEquals(0, $response['status'], 'outputs status'); $this->assertEquals(Helper::getPasteId(), $response['id'], 'outputted paste ID matches input'); @@ -132,6 +133,7 @@ class JsonApiTest extends PHPUnit_Framework_TestCase new PrivateBin; $content = ob_get_contents(); ob_end_clean(); + unlink($file); $response = json_decode($content, true); $this->assertEquals(0, $response['status'], 'outputs status'); $this->assertFalse($this->_model->exists(Helper::getPasteId()), 'paste successfully deleted'); diff --git a/tst/RequestTest.php b/tst/RequestTest.php index f20209f5..29b0dade 100644 --- a/tst/RequestTest.php +++ b/tst/RequestTest.php @@ -63,6 +63,7 @@ class RequestTest extends PHPUnit_Framework_TestCase file_put_contents($file, 'data=foo'); Request::setInputStream($file); $request = new Request; + unlink($file); $this->assertTrue($request->isJsonApiCall(), 'is JSON Api call'); $this->assertEquals('create', $request->getOperation()); $this->assertEquals('foo', $request->getParam('data')); From 18315e7de0f3fbebf2e5c03abda8c24fb5563004 Mon Sep 17 00:00:00 2001 From: El RIDO Date: Fri, 24 Mar 2017 23:45:10 +0100 Subject: [PATCH 0114/1638] removing unused class --- lib/Persistence/DataStore.php | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/Persistence/DataStore.php b/lib/Persistence/DataStore.php index 27c7131d..26c8cebe 100644 --- a/lib/Persistence/DataStore.php +++ b/lib/Persistence/DataStore.php @@ -13,7 +13,6 @@ namespace PrivateBin\Persistence; use Exception; -use PrivateBin\Configuration; use PrivateBin\Json; /** From 9b2af0abf567ee33cb1205ef84f2a698b2c9b990 Mon Sep 17 00:00:00 2001 From: El RIDO Date: Fri, 24 Mar 2017 23:54:37 +0100 Subject: [PATCH 0115/1638] fixing documentation --- lib/Persistence/DataStore.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Persistence/DataStore.php b/lib/Persistence/DataStore.php index 26c8cebe..56dde1a7 100644 --- a/lib/Persistence/DataStore.php +++ b/lib/Persistence/DataStore.php @@ -28,7 +28,7 @@ class DataStore extends AbstractPersistence * @access public * @static * @param string $filename - * @param string $data + * @param array $data * @return bool */ public static function store($filename, $data) From 3be736fa1daa1a2b4d6d429612b1c6599f689041 Mon Sep 17 00:00:00 2001 From: Tulio Leao Date: Fri, 24 Mar 2017 20:03:08 -0300 Subject: [PATCH 0116/1638] Update pt.json to reflect latest string changes Just a minimal change on the translation. --- i18n/pt.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/i18n/pt.json b/i18n/pt.json index ab459524..06f72c51 100644 --- a/i18n/pt.json +++ b/i18n/pt.json @@ -93,7 +93,7 @@ "Anonymous": "Anônimo", "Avatar generated from IP address": - "Avatar (do endereço IP)", + "Avatar gerado à partir do endereço IP", "Add comment": "Adicionar comentário", "Optional nickname…": From bbcc3e167bb52065cd80ddc7becb6a00eae63460 Mon Sep 17 00:00:00 2001 From: El RIDO Date: Sat, 25 Mar 2017 00:58:59 +0100 Subject: [PATCH 0117/1638] implementing recommendations of scrutinizer --- js/privatebin.js | 60 ++++++++++--------------- lib/Data/AbstractData.php | 4 +- lib/Data/Database.php | 14 ++---- lib/Data/Filesystem.php | 1 - lib/I18n.php | 9 ++-- lib/Model.php | 3 -- lib/Model/AbstractModel.php | 5 --- lib/Model/Comment.php | 5 --- lib/Model/Paste.php | 8 ---- lib/Persistence/AbstractPersistence.php | 2 - lib/Persistence/PurgeLimiter.php | 2 - lib/Persistence/ServerSalt.php | 1 - lib/Persistence/TrafficLimiter.php | 2 - lib/PrivateBin.php | 7 --- lib/Request.php | 21 +++++---- lib/View.php | 2 - lib/Vizhash16x16.php | 2 - tpl/bootstrap.php | 2 +- tpl/page.php | 2 +- 19 files changed, 45 insertions(+), 107 deletions(-) diff --git a/js/privatebin.js b/js/privatebin.js index 10b6a3a7..9fd4626f 100644 --- a/js/privatebin.js +++ b/js/privatebin.js @@ -179,7 +179,7 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { * * @name Helper.urls2links * @function - * @param {Object} element - a jQuery DOM element + * @param {Object} $element - a jQuery DOM element */ me.urls2links = function($element) { @@ -675,7 +675,7 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { * @function * @return {string} func */ - me.getSymmetricKey = function(func) + me.getSymmetricKey = function() { return sjcl.codec.base64.fromBits(sjcl.random.randomWords(8, 0), 0); } @@ -903,8 +903,6 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { me.isVisible = function($element) { var elementTop = $element.offset().top; - var elementBottom = elementTop + $element.outerHeight(); - var viewportTop = $(window).scrollTop(); var viewportBottom = viewportTop + $(window).height(); @@ -985,11 +983,9 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { * Alert/error manager * * @name Alert - * @param {object} window - * @param {object} document * @class */ - var Alert = (function (window, document) { + var Alert = (function () { var me = {}; var $errorMessage, @@ -1249,17 +1245,16 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { } return me; - })(window, document); + })(); /** * handles paste status/result * * @name PasteStatus * @param {object} window - * @param {object} document * @class */ - var PasteStatus = (function (window, document) { + var PasteStatus = (function (window) { var me = {}; var $pasteSuccess, @@ -1402,17 +1397,15 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { } return me; - })(window, document); + })(window); /** * password prompt * * @name Prompt - * @param {object} window - * @param {object} document * @class */ - var Prompt = (function (window, document) { + var Prompt = (function () { var me = {}; var $passwordDecrypt, @@ -1512,7 +1505,7 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { } return me; - })(window, document); + })(); /** * Manage paste/message input, and preview tab @@ -1520,11 +1513,9 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { * Note that the actual preview is handled by PasteViewer. * * @name Editor - * @param {object} window - * @param {object} document * @class */ - var Editor = (function (window, document) { + var Editor = (function () { var me = {}; var $editorTabs, @@ -1728,17 +1719,15 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { } return me; - })(window, document); + })(); /** * (view) Parse and show paste. * * @name PasteViewer - * @param {object} window - * @param {object} document * @class */ - var PasteViewer = (function (window, document) { + var PasteViewer = (function () { var me = {}; var $placeholder, @@ -1904,7 +1893,7 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { * @function * @return {string} */ - me.getText = function(newText) + me.getText = function() { return text; } @@ -1981,7 +1970,7 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { } return me; - })(window, document); + })(); /** * (view) Show attachment and preview if possible @@ -1998,8 +1987,7 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { $attachmentPreview, $attachment; - var attachmentChanged = false, - attachmentHasPreview = false; + var attachmentHasPreview = false; /** * sets the attachment but does not yet show it @@ -2027,8 +2015,6 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { ); attachmentHasPreview = true; } - - attachmentChanged = true; } /** @@ -3043,7 +3029,7 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { * @private * @function * @param {int} status - * @param {int} data - optional + * @param {int} result - optional */ function success(status, result) { @@ -3063,7 +3049,7 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { * @private * @function * @param {int} status - internal code - * @param {int} data - original error code + * @param {int} result - original error code */ function fail(status, result) { @@ -3107,7 +3093,7 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { * * @name Uploader.setUrl * @function - * @param {function} func + * @param {function} newUrl */ me.setUrl = function(newUrl) { @@ -3236,17 +3222,18 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { * @return {array} */ me.parseUploadError = function(status, data, doThisThing) { - var errorArray = ['Error while parsing error message.']; + var errorArray; switch (status) { - case Uploader.error['custom']: + case me.error['custom']: errorArray = ['Could not ' + doThisThing + ': %s', data.message]; break; - case Uploader.error['unknown']: + case me.error['unknown']: errorArray = ['Could not ' + doThisThing + ': %s', I18n._('unknown status')]; break; - case Uploader.error['serverError']: - errorArray = ['Could not ' + doThisThing + ': %s', I18n._('server error or not responding')]; break; + case me.error['serverError']: + errorArray = ['Could not ' + doThisThing + ': %s', I18n._('server error or not responding')]; + break; default: errorArray = ['Could not ' + doThisThing + ': %s', I18n._('unknown error')]; break; @@ -3884,7 +3871,6 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { // show proper elements on screen PasteDecrypter.run(); - return; } /** diff --git a/lib/Data/AbstractData.php b/lib/Data/AbstractData.php index c5eae217..41260f89 100644 --- a/lib/Data/AbstractData.php +++ b/lib/Data/AbstractData.php @@ -58,7 +58,7 @@ abstract class AbstractData * @access public * @static * @param array $options - * @return privatebin_abstract + * @return AbstractData */ public static function getInstance($options) { @@ -88,7 +88,6 @@ abstract class AbstractData * * @access public * @param string $pasteid - * @return void */ abstract public function delete($pasteid); @@ -147,7 +146,6 @@ abstract class AbstractData * * @access public * @param int $batchsize - * @return void */ public function purge($batchsize) { diff --git a/lib/Data/Database.php b/lib/Data/Database.php index 66744848..c35df3b0 100644 --- a/lib/Data/Database.php +++ b/lib/Data/Database.php @@ -282,7 +282,6 @@ class Database extends AbstractData * * @access public * @param string $pasteid - * @return void */ public function delete($pasteid) { @@ -375,11 +374,10 @@ class Database extends AbstractData $comments[$i]->data = $row['data']; $comments[$i]->meta = new stdClass; $comments[$i]->meta->postdate = (int) $row['postdate']; - if (array_key_exists('nickname', $row) && !empty($row['nickname'])) { - $comments[$i]->meta->nickname = $row['nickname']; - } - if (array_key_exists('vizhash', $row) && !empty($row['vizhash'])) { - $comments[$i]->meta->vizhash = $row['vizhash']; + foreach (array('nickname', 'vizhash') as $key) { + if (array_key_exists($key, $row) && !empty($row[$key])) { + $comments[$i]->meta->$key = $row[$key]; + } } } ksort($comments); @@ -564,7 +562,6 @@ class Database extends AbstractData * * @access private * @static - * @return void */ private static function _createPasteTable() { @@ -589,7 +586,6 @@ class Database extends AbstractData * * @access private * @static - * @return void */ private static function _createCommentTable() { @@ -616,7 +612,6 @@ class Database extends AbstractData * * @access private * @static - * @return void */ private static function _createConfigTable() { @@ -651,7 +646,6 @@ class Database extends AbstractData * @access private * @static * @param string $oldversion - * @return void */ private static function _upgradeDatabase($oldversion) { diff --git a/lib/Data/Filesystem.php b/lib/Data/Filesystem.php index 09844052..4100e291 100644 --- a/lib/Data/Filesystem.php +++ b/lib/Data/Filesystem.php @@ -98,7 +98,6 @@ class Filesystem extends AbstractData * * @access public * @param string $pasteid - * @return void */ public function delete($pasteid) { diff --git a/lib/I18n.php b/lib/I18n.php index d35bcf01..2bee73ec 100644 --- a/lib/I18n.php +++ b/lib/I18n.php @@ -135,15 +135,17 @@ class I18n * * @access public * @static - * @return void */ public static function loadTranslations() { $availableLanguages = self::getAvailableLanguages(); // check if the lang cookie was set and that language exists - if (array_key_exists('lang', $_COOKIE) && in_array($_COOKIE['lang'], $availableLanguages)) { - $match = $_COOKIE['lang']; + if ( + array_key_exists('lang', $_COOKIE) && + ($key = array_search($_COOKIE['lang'], $availableLanguages)) !== false + ) { + $match = $availableLanguages[$key]; } // find a translation file matching the browsers language preferences else { @@ -256,7 +258,6 @@ class I18n * @access public * @static * @param string $lang - * @return void */ public static function setLanguageFallback($lang) { diff --git a/lib/Model.php b/lib/Model.php index fc795691..d1011f12 100644 --- a/lib/Model.php +++ b/lib/Model.php @@ -40,7 +40,6 @@ class Model * Factory constructor. * * @param configuration $conf - * @return void */ public function __construct(Configuration $conf) { @@ -64,8 +63,6 @@ class Model /** * Checks if a purge is necessary and triggers it if yes. - * - * @return void */ public function purge() { diff --git a/lib/Model/AbstractModel.php b/lib/Model/AbstractModel.php index 3dd48a83..55956b7a 100644 --- a/lib/Model/AbstractModel.php +++ b/lib/Model/AbstractModel.php @@ -63,7 +63,6 @@ abstract class AbstractModel * @access public * @param Configuration $configuration * @param AbstractData $storage - * @return void */ public function __construct(Configuration $configuration, AbstractData $storage) { @@ -90,7 +89,6 @@ abstract class AbstractModel * @access public * @param string $id * @throws Exception - * @return void */ public function setId($id) { @@ -106,7 +104,6 @@ abstract class AbstractModel * @access public * @param string $data * @throws Exception - * @return void */ public function setData($data) { @@ -133,7 +130,6 @@ abstract class AbstractModel * * @access public * @throws Exception - * @return void */ abstract public function store(); @@ -142,7 +138,6 @@ abstract class AbstractModel * * @access public * @throws Exception - * @return void */ abstract public function delete(); diff --git a/lib/Model/Comment.php b/lib/Model/Comment.php index 86f4ffa1..b67742d2 100644 --- a/lib/Model/Comment.php +++ b/lib/Model/Comment.php @@ -61,7 +61,6 @@ class Comment extends AbstractModel * * @access public * @throws Exception - * @return void */ public function store() { @@ -101,7 +100,6 @@ class Comment extends AbstractModel * * @access public * @throws Exception - * @return void */ public function delete() { @@ -129,7 +127,6 @@ class Comment extends AbstractModel * @access public * @param Paste $paste * @throws Exception - * @return void */ public function setPaste(Paste $paste) { @@ -154,7 +151,6 @@ class Comment extends AbstractModel * @access public * @param string $id * @throws Exception - * @return void */ public function setParentId($id) { @@ -184,7 +180,6 @@ class Comment extends AbstractModel * @access public * @param string $nickname * @throws Exception - * @return void */ public function setNickname($nickname) { diff --git a/lib/Model/Paste.php b/lib/Model/Paste.php index 038bfbc8..8f171fe1 100644 --- a/lib/Model/Paste.php +++ b/lib/Model/Paste.php @@ -75,7 +75,6 @@ class Paste extends AbstractModel * * @access public * @throws Exception - * @return void */ public function store() { @@ -103,7 +102,6 @@ class Paste extends AbstractModel * * @access public * @throws Exception - * @return void */ public function delete() { @@ -183,7 +181,6 @@ class Paste extends AbstractModel * @access public * @param string $attachment * @throws Exception - * @return void */ public function setAttachment($attachment) { @@ -199,7 +196,6 @@ class Paste extends AbstractModel * @access public * @param string $attachmentname * @throws Exception - * @return void */ public function setAttachmentName($attachmentname) { @@ -214,7 +210,6 @@ class Paste extends AbstractModel * * @access public * @param string $expiration - * @return void */ public function setExpiration($expiration) { @@ -236,7 +231,6 @@ class Paste extends AbstractModel * @access public * @param string $burnafterreading * @throws Exception - * @return void */ public function setBurnafterreading($burnafterreading = '1') { @@ -257,7 +251,6 @@ class Paste extends AbstractModel * @access public * @param string $opendiscussion * @throws Exception - * @return void */ public function setOpendiscussion($opendiscussion = '1') { @@ -281,7 +274,6 @@ class Paste extends AbstractModel * @access public * @param string $format * @throws Exception - * @return void */ public function setFormatter($format) { diff --git a/lib/Persistence/AbstractPersistence.php b/lib/Persistence/AbstractPersistence.php index 68f148fd..64fb530c 100644 --- a/lib/Persistence/AbstractPersistence.php +++ b/lib/Persistence/AbstractPersistence.php @@ -36,7 +36,6 @@ abstract class AbstractPersistence * @access public * @static * @param string $path - * @return void */ public static function setPath($path) { @@ -80,7 +79,6 @@ abstract class AbstractPersistence * @access protected * @static * @throws Exception - * @return void */ protected static function _initialize() { diff --git a/lib/Persistence/PurgeLimiter.php b/lib/Persistence/PurgeLimiter.php index a3830078..2eb0b52c 100644 --- a/lib/Persistence/PurgeLimiter.php +++ b/lib/Persistence/PurgeLimiter.php @@ -36,7 +36,6 @@ class PurgeLimiter extends AbstractPersistence * @access public * @static * @param int $limit - * @return void */ public static function setLimit($limit) { @@ -49,7 +48,6 @@ class PurgeLimiter extends AbstractPersistence * @access public * @static * @param Configuration $conf - * @return void */ public static function setConfiguration(Configuration $conf) { diff --git a/lib/Persistence/ServerSalt.php b/lib/Persistence/ServerSalt.php index 451fbd68..129a0992 100644 --- a/lib/Persistence/ServerSalt.php +++ b/lib/Persistence/ServerSalt.php @@ -95,7 +95,6 @@ class ServerSalt extends AbstractPersistence * @access public * @static * @param string $path - * @return void */ public static function setPath($path) { diff --git a/lib/Persistence/TrafficLimiter.php b/lib/Persistence/TrafficLimiter.php index a908a825..914450a9 100644 --- a/lib/Persistence/TrafficLimiter.php +++ b/lib/Persistence/TrafficLimiter.php @@ -45,7 +45,6 @@ class TrafficLimiter extends AbstractPersistence * @access public * @static * @param int $limit - * @return void */ public static function setLimit($limit) { @@ -58,7 +57,6 @@ class TrafficLimiter extends AbstractPersistence * @access public * @static * @param Configuration $conf - * @return void */ public static function setConfiguration(Configuration $conf) { diff --git a/lib/PrivateBin.php b/lib/PrivateBin.php index 92072ea4..c817445c 100644 --- a/lib/PrivateBin.php +++ b/lib/PrivateBin.php @@ -123,7 +123,6 @@ class PrivateBin * * @access public * @throws Exception - * @return void */ public function __construct() { @@ -171,7 +170,6 @@ class PrivateBin * initialize privatebin * * @access private - * @return void */ private function _init() { @@ -320,7 +318,6 @@ class PrivateBin * @access private * @param string $dataid * @param string $deletetoken - * @return void */ private function _delete($dataid, $deletetoken) { @@ -364,7 +361,6 @@ class PrivateBin * * @access private * @param string $dataid - * @return void */ private function _read($dataid) { @@ -397,7 +393,6 @@ class PrivateBin * Display PrivateBin frontend. * * @access private - * @return void */ private function _view() { @@ -461,7 +456,6 @@ class PrivateBin * * @access private * @param string $type - * @return void */ private function _jsonld($type) { @@ -494,7 +488,6 @@ class PrivateBin * @param int $status * @param string $message * @param array $other - * @return void */ private function _return_message($status, $message, $other = array()) { diff --git a/lib/Request.php b/lib/Request.php index e6c1c749..37c0bca7 100644 --- a/lib/Request.php +++ b/lib/Request.php @@ -41,7 +41,7 @@ class Request const MIME_XHTML = 'application/xhtml+xml'; /** - * Input stream to use for PUT parameter parsing. + * Input stream to use for PUT parameter parsing * * @access private * @var string @@ -49,7 +49,7 @@ class Request private static $_inputStream = 'php://input'; /** - * Operation to perform. + * Operation to perform * * @access private * @var string @@ -57,7 +57,7 @@ class Request private $_operation = 'view'; /** - * Request parameters. + * Request parameters * * @access private * @var array @@ -65,7 +65,7 @@ class Request private $_params = array(); /** - * If we are in a JSON API context. + * If we are in a JSON API context * * @access private * @var bool @@ -73,10 +73,9 @@ class Request private $_isJsonApi = false; /** - * Constructor. + * Constructor * * @access public - * @return void */ public function __construct() { @@ -122,7 +121,7 @@ class Request } /** - * Get current operation. + * Get current operation * * @access public * @return string @@ -133,7 +132,7 @@ class Request } /** - * Get a request parameter. + * Get a request parameter * * @access public * @param string $param @@ -146,7 +145,7 @@ class Request } /** - * If we are in a JSON API context. + * If we are in a JSON API context * * @access public * @return bool @@ -157,7 +156,7 @@ class Request } /** - * Override the default input stream source, used for unit testing. + * Override the default input stream source, used for unit testing * * @param string $input */ @@ -167,7 +166,7 @@ class Request } /** - * detect the clients supported media type and decide if its a JSON API call or not + * Detect the clients supported media type and decide if its a JSON API call or not * * Adapted from: https://stackoverflow.com/questions/3770513/detect-browser-language-in-php#3771447 * diff --git a/lib/View.php b/lib/View.php index d7ecaa21..6c04e47f 100644 --- a/lib/View.php +++ b/lib/View.php @@ -35,7 +35,6 @@ class View * @access public * @param string $name * @param mixed $value - * @return void */ public function assign($name, $value) { @@ -48,7 +47,6 @@ class View * @access public * @param string $template * @throws Exception - * @return void */ public function draw($template) { diff --git a/lib/Vizhash16x16.php b/lib/Vizhash16x16.php index 604c86ef..e9bd5d0b 100644 --- a/lib/Vizhash16x16.php +++ b/lib/Vizhash16x16.php @@ -61,7 +61,6 @@ class Vizhash16x16 * constructor * * @access public - * @return void */ public function __construct() { @@ -210,7 +209,6 @@ class Vizhash16x16 * @param resource $image * @param int $action * @param int $color - * @return void */ private function drawshape($image, $action, $color) { diff --git a/tpl/bootstrap.php b/tpl/bootstrap.php index a80e175c..7ee8d0de 100644 --- a/tpl/bootstrap.php +++ b/tpl/bootstrap.php @@ -69,7 +69,7 @@ if ($MARKDOWN): - + diff --git a/tpl/page.php b/tpl/page.php index 00906305..2fd511fa 100644 --- a/tpl/page.php +++ b/tpl/page.php @@ -47,7 +47,7 @@ if ($MARKDOWN): - + From 9d2e282772e609683aa1c5dbf9c47d3963082ce8 Mon Sep 17 00:00:00 2001 From: El RIDO Date: Sat, 25 Mar 2017 09:17:04 +0100 Subject: [PATCH 0118/1638] removing unused function --- js/privatebin.js | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/js/privatebin.js b/js/privatebin.js index c55a0271..28e08e44 100644 --- a/js/privatebin.js +++ b/js/privatebin.js @@ -107,19 +107,6 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { return [v, 'month']; } - /** - * checks if a string is valid text (and not onyl whitespace) - * - * @name Helper.isValidText - * @function - * @param {string} string - * @return {bool} - */ - me.isValidText = function(string) - { - return (string.length > 0 && $.trim(string) !== '') - } - /** * text range selection * From 2a19b42b15906cfbe9e3bb7a43bf14cac0c03625 Mon Sep 17 00:00:00 2001 From: El RIDO Date: Sat, 25 Mar 2017 09:41:24 +0100 Subject: [PATCH 0119/1638] making I18n class testable, adding minimal test --- js/privatebin.js | 13 ++++++++++++- js/test.js | 24 +++++++++++++++++++++++- 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/js/privatebin.js b/js/privatebin.js index 28e08e44..74b2211e 100644 --- a/js/privatebin.js +++ b/js/privatebin.js @@ -538,6 +538,18 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { }); } + /** + * resets state, used for unit testing + * + * @name I18n.reset + * @function + */ + me.reset = function() + { + language = null; + translations = {}; + } + return me; })(window, document); @@ -810,7 +822,6 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { $cipherData = $templates = id = symmetricKey = null; } - /** * init navigation manager * diff --git a/js/test.js b/js/test.js index 2d5a544c..3999158a 100644 --- a/js/test.js +++ b/js/test.js @@ -270,7 +270,8 @@ describe('Helper', function () { cookieArray = [], count = 0; labels.forEach(function(item, i) { - var key = item.replace(/[\s;,=]/g, 'x'), + // deliberatly using a non-ascii key for replacing invalid characters + var key = item.replace(/[\s;,=]/g, '£'), value = (values[i] || values[0]).replace(/[\s;,=]/g, ''); cookieArray.push(key + '=' + value); if (Math.random() < 1 / i) @@ -325,6 +326,27 @@ describe('Helper', function () { }); }); +describe('I18n', function () { + describe('translate', function () { + before(function () { + $.PrivateBin.I18n.reset(); + }); + + jsc.property( + 'returns message ID unchanged if no translation found', + 'string', + function (messageId) { + var result = $.PrivateBin.I18n.translate(messageId); + $.PrivateBin.I18n.reset(); + var alias = $.PrivateBin.I18n._(messageId); + $.PrivateBin.I18n.reset(); + return messageId === result && + messageId === alias; + } + ); + }); +}); + describe('Model', function () { describe('getPasteId', function () { before(function () { From e15e86ac3f37beb90b3f7ca4aaf48f418f451434 Mon Sep 17 00:00:00 2001 From: El RIDO Date: Sat, 25 Mar 2017 10:18:28 +0100 Subject: [PATCH 0120/1638] improving coverage of existing tests --- js/test.js | 66 ++++++++++++++++++++++++++++++++++++++++++++--- tpl/bootstrap.php | 2 +- tpl/page.php | 2 +- 3 files changed, 65 insertions(+), 5 deletions(-) diff --git a/js/test.js b/js/test.js index 3999158a..b2e41073 100644 --- a/js/test.js +++ b/js/test.js @@ -271,7 +271,7 @@ describe('Helper', function () { count = 0; labels.forEach(function(item, i) { // deliberatly using a non-ascii key for replacing invalid characters - var key = item.replace(/[\s;,=]/g, '£'), + var key = item.replace(/[\s;,=]/g, Array(i+2).join('£')), value = (values[i] || values[0]).replace(/[\s;,=]/g, ''); cookieArray.push(key + '=' + value); if (Math.random() < 1 / i) @@ -340,8 +340,24 @@ describe('I18n', function () { $.PrivateBin.I18n.reset(); var alias = $.PrivateBin.I18n._(messageId); $.PrivateBin.I18n.reset(); - return messageId === result && - messageId === alias; + return messageId === result && messageId === alias; + } + ); + jsc.property( + 'replaces %s in strings with first given parameter', + 'string', + '(small nearray) string', + 'string', + function (prefix, params, postfix) { + var prefix = prefix.replace(/%(s|d)/g, '%%'), + postfix = postfix.replace(/%(s|d)/g, '%%'), + translation = prefix + params[0] + postfix; + params.unshift(prefix + '%s' + postfix); + var result = $.PrivateBin.I18n.translate.apply(this, params); + $.PrivateBin.I18n.reset(); + var alias = $.PrivateBin.I18n._.apply(this, params); + $.PrivateBin.I18n.reset(); + return translation === result && translation === alias; } ); }); @@ -371,6 +387,28 @@ describe('Model', function () { return queryString === result; } ); + jsc.property( + 'throws exception on empty query string', + jsc.nearray(jsc.elements(a2zString)), + jsc.nearray(jsc.elements(a2zString)), + 'string', + function (schema, address, fragment) { + var clean = jsdom('', { + url: schema.join('') + '://' + address.join('') + + '/#' + fragment + }), + result = false; + try { + $.PrivateBin.Model.getPasteId(); + } + catch(err) { + result = true; + } + $.PrivateBin.Model.reset(); + clean(); + return result; + } + ); }); describe('getPasteKey', function () { @@ -411,5 +449,27 @@ describe('Model', function () { return fragmentString === result; } ); + jsc.property( + 'throws exception on empty fragment of the URL', + jsc.nearray(jsc.elements(a2zString)), + jsc.nearray(jsc.elements(a2zString)), + jsc.array(jsc.elements(queryString)), + function (schema, address, query) { + var clean = jsdom('', { + url: schema.join('') + '://' + address.join('') + + '/?' + query.join('') + }), + result = false; + try { + $.PrivateBin.Model.getPasteKey(); + } + catch(err) { + result = true; + } + $.PrivateBin.Model.reset(); + clean(); + return result; + } + ); }); }); diff --git a/tpl/bootstrap.php b/tpl/bootstrap.php index 5381863b..d47e8235 100644 --- a/tpl/bootstrap.php +++ b/tpl/bootstrap.php @@ -69,7 +69,7 @@ if ($MARKDOWN): - + diff --git a/tpl/page.php b/tpl/page.php index 18d60e32..85cdf7cf 100644 --- a/tpl/page.php +++ b/tpl/page.php @@ -47,7 +47,7 @@ if ($MARKDOWN): - + From 145cfccfcb0f915309bc9bc7299af70c85b53727 Mon Sep 17 00:00:00 2001 From: El RIDO Date: Sat, 25 Mar 2017 10:47:12 +0100 Subject: [PATCH 0121/1638] corrections for rngState 82b19a3e7604cf825d --- js/test.js | 41 +++++++++++++++++++++++------------------ 1 file changed, 23 insertions(+), 18 deletions(-) diff --git a/js/test.js b/js/test.js index b2e41073..f328b51d 100644 --- a/js/test.js +++ b/js/test.js @@ -195,9 +195,10 @@ describe('Helper', function () { '(small nearray) string', 'string', function (prefix, params, postfix) { - var prefix = prefix.replace(/%(s|d)/g, '%%'), - postfix = postfix.replace(/%(s|d)/g, '%%'), - result = prefix + params[0] + postfix; + prefix = prefix.replace(/%(s|d)/g, '%%'); + params[0] = params[0].replace(/%(s|d)/g, '%%'); + postfix = postfix.replace(/%(s|d)/g, '%%'); + var result = prefix + params[0] + postfix; params.unshift(prefix + '%s' + postfix); return result === $.PrivateBin.Helper.sprintf.apply(this, params); } @@ -208,9 +209,9 @@ describe('Helper', function () { '(small nearray) nat', 'string', function (prefix, params, postfix) { - var prefix = prefix.replace(/%(s|d)/g, '%%'), - postfix = postfix.replace(/%(s|d)/g, '%%'), - result = prefix + params[0] + postfix; + prefix = prefix.replace(/%(s|d)/g, '%%'); + postfix = postfix.replace(/%(s|d)/g, '%%'); + var result = prefix + params[0] + postfix; params.unshift(prefix + '%d' + postfix); return result === $.PrivateBin.Helper.sprintf.apply(this, params); } @@ -221,9 +222,9 @@ describe('Helper', function () { '(small nearray) falsy', 'string', function (prefix, params, postfix) { - var prefix = prefix.replace(/%(s|d)/g, '%%'), - postfix = postfix.replace(/%(s|d)/g, '%%'), - result = prefix + '0' + postfix; + prefix = prefix.replace(/%(s|d)/g, '%%'); + postfix = postfix.replace(/%(s|d)/g, '%%'); + var result = prefix + '0' + postfix; params.unshift(prefix + '%d' + postfix); return result === $.PrivateBin.Helper.sprintf.apply(this, params) } @@ -236,9 +237,10 @@ describe('Helper', function () { 'string', 'string', function (prefix, uint, middle, string, postfix) { - var prefix = prefix.replace(/%(s|d)/g, '%%'), - postfix = postfix.replace(/%(s|d)/g, '%%'), - params = [prefix + '%d' + middle + '%s' + postfix, uint, string], + 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], result = prefix + uint + middle + string + postfix; return result === $.PrivateBin.Helper.sprintf.apply(this, params); } @@ -251,9 +253,10 @@ describe('Helper', function () { 'string', 'string', function (prefix, uint, middle, string, postfix) { - var prefix = prefix.replace(/%(s|d)/g, '%%'), - postfix = postfix.replace(/%(s|d)/g, '%%'), - params = [prefix + '%s' + middle + '%d' + postfix, string, uint], + 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], result = prefix + string + middle + uint + postfix; return result === $.PrivateBin.Helper.sprintf.apply(this, params); } @@ -336,6 +339,7 @@ describe('I18n', function () { 'returns message ID unchanged if no translation found', 'string', function (messageId) { + messageId = messageId.replace(/%(s|d)/g, '%%'); var result = $.PrivateBin.I18n.translate(messageId); $.PrivateBin.I18n.reset(); var alias = $.PrivateBin.I18n._(messageId); @@ -349,9 +353,10 @@ describe('I18n', function () { '(small nearray) string', 'string', function (prefix, params, postfix) { - var prefix = prefix.replace(/%(s|d)/g, '%%'), - postfix = postfix.replace(/%(s|d)/g, '%%'), - translation = prefix + params[0] + postfix; + prefix = prefix.replace(/%(s|d)/g, '%%'); + params[0] = params[0].replace(/%(s|d)/g, '%%'); + postfix = postfix.replace(/%(s|d)/g, '%%'); + var translation = prefix + params[0] + postfix; params.unshift(prefix + '%s' + postfix); var result = $.PrivateBin.I18n.translate.apply(this, params); $.PrivateBin.I18n.reset(); From e80c726f92c3694e7f60e58fec472e3070eddf9d Mon Sep 17 00:00:00 2001 From: El RIDO Date: Sat, 25 Mar 2017 12:46:08 +0100 Subject: [PATCH 0122/1638] added unit test for missing message IDs accross all translations, added IDs found this way to translation files (#201) --- i18n/de.json | 3 +-- i18n/es.json | 13 ++++++++----- i18n/fr.json | 13 ++++++++----- i18n/it.json | 13 ++++++++----- i18n/no.json | 13 ++++++++----- i18n/oc.json | 13 ++++++++----- i18n/pl.json | 13 ++++++++----- i18n/pt.json | 5 ++++- i18n/ru.json | 10 ++++------ i18n/sl.json | 13 ++++++++----- tst/I18nTest.php | 20 ++++++++++++++++++++ 11 files changed, 85 insertions(+), 44 deletions(-) diff --git a/i18n/de.json b/i18n/de.json index 7974d773..332c61c8 100644 --- a/i18n/de.json +++ b/i18n/de.json @@ -151,6 +151,5 @@ "Preparing new paste…": "Bereite neues Paste vor…", "In case this message never disappears please have a look at this FAQ for information to troubleshoot.": "Wenn diese Nachricht nicht mehr verschwindet, schau bitte in die FAQ (englisch), um zu sehen, wie der Fehler behoben werden kann.", - "+++ no paste text +++": - "+++ kein Paste-Text +++" + "+++ no paste text +++": "+++ kein Paste-Text +++" } diff --git a/i18n/es.json b/i18n/es.json index e957c8f8..98251e98 100644 --- a/i18n/es.json +++ b/i18n/es.json @@ -92,7 +92,7 @@ "Responder", "Anonymous": "Anónimo", - "Anonymous avatar (Vizhash of the IP address)": + "Avatar generated from IP address": "Avatar anónimo (Vizhash de la dirección IP)", "Add comment": "Añadir comentario", @@ -112,8 +112,8 @@ "Error del servidor o el servidor no responde", "Could not post comment: %s": "No fue posible publicar comentario: %s", - "Sending paste (Please move your mouse for more entropy)…": - "Enviando texto (Por favor, mueva el ratón para mayor entropía)…", + "Please move your mouse for more entropy…": + "Por favor, mueva el ratón para mayor entropía…", "Sending paste…": "Enviando texto…", "Your paste is %s (Hit [Ctrl]+[c] to copy)": @@ -129,7 +129,7 @@ "Source Code": "Código fuente", "Markdown": "Markdown", "Download attachment": "Descargar adjunto", - "Cloned file attached.": "Archivo clonado adjunto.", + "Cloned: '%s'": "Clonado: '%s'.", "Attach a file": "Adjuntar archivo", "Remove attachment": "Remover adjunto", "Your browser does not support uploading encrypted files. Please use a newer browser.": @@ -146,6 +146,9 @@ "Enter password": "Ingrese contraseña", "Loading…": "Cargando…", + "Decrypting paste…": "Decrypting paste…", + "Preparing new paste…": "Preparing new paste…", "In case this message never disappears please have a look at this FAQ for information to troubleshoot.": - "En caso de que este mensaje nunca desaparezca por favor revise este FAQ para obtener información para solucionar problemas." + "En caso de que este mensaje nunca desaparezca por favor revise este FAQ para obtener información para solucionar problemas.", + "+++ no paste text +++": "+++ no paste text +++" } diff --git a/i18n/fr.json b/i18n/fr.json index 35cfd659..d5d447e0 100644 --- a/i18n/fr.json +++ b/i18n/fr.json @@ -92,7 +92,7 @@ "Répondre", "Anonymous": "Anonyme", - "Anonymous avatar (Vizhash of the IP address)": + "Avatar generated from IP address": "Avatar anonyme (Vizhash de l'adresse IP)", "Add comment": "Ajouter un commentaire", @@ -112,8 +112,8 @@ "Le serveur ne répond pas ou a rencontré une erreur", "Could not post comment: %s": "Impossible de poster le commentaire : %s", - "Sending paste (Please move your mouse for more entropy)…": - "Envoi du paste (Merci de bouger votre souris pour plus d'entropie)…", + "Please move your mouse for more entropy…": + "Merci de bouger votre souris pour plus d'entropie…", "Sending paste…": "Envoi du paste…", "Your paste is %s (Hit [Ctrl]+[c] to copy)": @@ -138,7 +138,7 @@ "Source Code": "Code source", "Markdown": "Markdown", "Download attachment": "Télécharger la pièce jointe", - "Cloned file attached.": "Cloner le fichier attaché.", + "Cloned: '%s'": "Cloner '%s'", "Attach a file": "Attacher un fichier ", "Remove attachment": "Enlever l'attachement", "Your browser does not support uploading encrypted files. Please use a newer browser.": @@ -155,6 +155,9 @@ "Enter password": "Entrez le mot de passe", "Loading…": "Chargement…", + "Decrypting paste…": "Decrypting paste…", + "Preparing new paste…": "Preparing new paste…", "In case this message never disappears please have a look at this FAQ for information to troubleshoot.": - "Si ce message ne disparaîssait pas, jetez un oeil à cette FAQ pour des idées de résolution (en Anglais)." + "Si ce message ne disparaîssait pas, jetez un oeil à cette FAQ pour des idées de résolution (en Anglais).", + "+++ no paste text +++": "+++ no paste text +++" } diff --git a/i18n/it.json b/i18n/it.json index dc56fa75..b7b19e8c 100644 --- a/i18n/it.json +++ b/i18n/it.json @@ -92,7 +92,7 @@ "Rispondi", "Anonymous": "Anonimo", - "Anonymous avatar (Vizhash of the IP address)": + "Avatar generated from IP address": "Avatar Anonimo (Vizhash dell'indirizzo IP)", "Add comment": "Aggiungi un commento", @@ -112,8 +112,8 @@ "errore o mancata risposta dal server", "Could not post comment: %s": "Impossibile inviare il commento: %s", - "Sending paste (Please move your mouse for more entropy)…": - "Invio messaggio (Muovi il mouse in modo casuale, per generare maggior entropia)…", + "Please move your mouse for more entropy…": + "Muovi il mouse in modo casuale, per generare maggior entropia…", "Sending paste…": "Messaggio in fase di invio…", "Your paste is %s (Hit [Ctrl]+[c] to copy)": @@ -129,7 +129,7 @@ "Source Code": "Codice Sorgente", "Markdown": "Markdown", "Download attachment": "Scarica Allegato", - "Cloned file attached.": "Copia del file allegata.", + "Cloned: '%s'": "Copia: '%s'", "Attach a file": "Allega un file", "Remove attachment": "Rimuovi allegato", "Your browser does not support uploading encrypted files. Please use a newer browser.": @@ -146,6 +146,9 @@ "Enter password": "Inserisci la password", "Loading…": "Loading…", + "Decrypting paste…": "Decrypting paste…", + "Preparing new paste…": "Preparing new paste…", "In case this message never disappears please have a look at this FAQ for information to troubleshoot.": - "Nel caso questo messaggio non scompaia, controlla questa FAQ per trovare informazioni su come risolvere il problema (in Inglese)." + "Nel caso questo messaggio non scompaia, controlla questa FAQ per trovare informazioni su come risolvere il problema (in Inglese).", + "+++ no paste text +++": "+++ no paste text +++" } diff --git a/i18n/no.json b/i18n/no.json index fd56583d..f131e4fe 100644 --- a/i18n/no.json +++ b/i18n/no.json @@ -92,7 +92,7 @@ "Svar", "Anonymous": "Anonym", - "Anonymous avatar (Vizhash of the IP address)": + "Avatar generated from IP address": "Anonym avatar (Vizhash av IP adressen)", "Add comment": "Legg til kommentar", @@ -112,8 +112,8 @@ "server feilet eller svarer ikke", "Could not post comment: %s": "Kunne ikke sende kommentar: %s", - "Sending paste (Please move your mouse for more entropy)…": - "Sender innlegg (Flytt musen for mere entropi)…", + "Please move your mouse for more entropy…": + "Flytt musen for mere entropi…", "Sending paste…": "Sender innlegg…", "Your paste is %s (Hit [Ctrl]+[c] to copy)": @@ -129,7 +129,7 @@ "Source Code": "Kildekode", "Markdown": "Oppmerket", "Download attachment": "Last ned vedlegg", - "Cloned file attached.": "Kopier vedlegg.", + "Cloned: '%s'": "Kopier: '%s'", "Attach a file": "Legg til fil", "Remove attachment": "Slett vedlegg", "Your browser does not support uploading encrypted files. Please use a newer browser.": @@ -146,6 +146,9 @@ "Enter password": "Skriv inn passord", "Loading…": "Laster…", + "Decrypting paste…": "Decrypting paste…", + "Preparing new paste…": "Preparing new paste…", "In case this message never disappears please have a look at this FAQ for information to troubleshoot.": - "Hvis denne meldingen ikke forsvinner kan du ta en titt på siden med ofte stilte spørsmål for informasjon om feilsøking." + "Hvis denne meldingen ikke forsvinner kan du ta en titt på siden med ofte stilte spørsmål for informasjon om feilsøking.", + "+++ no paste text +++": "+++ no paste text +++" } diff --git a/i18n/oc.json b/i18n/oc.json index b6b449de..8f923b1b 100644 --- a/i18n/oc.json +++ b/i18n/oc.json @@ -92,7 +92,7 @@ "Respondre", "Anonymous": "Anonime", - "Anonymous avatar (Vizhash of the IP address)": + "Avatar generated from IP address": "Avatar anonime (Vizhash de l'adreça IP)", "Add comment": "Apondre un comentari", @@ -112,8 +112,8 @@ "Lo servidor respond pas o a rencontrat una error", "Could not post comment: %s": "Impossible de mandar lo comentari : %s", - "Sending paste (Please move your mouse for more entropy)…": - "Mandadís del tèxte (Mercés de bolegar vòstra mirga per mai entropia)…", + "Please move your mouse for more entropy…": + "Mercés de bolegar vòstra mirga per mai entropia…", "Sending paste…": "Mandadís del tèxte…", "Your paste is %s (Hit [Ctrl]+[c] to copy)": @@ -138,7 +138,7 @@ "Source Code": "Còdi font", "Markdown": "Markdown", "Download attachment": "Telecargar la pèça junta", - "Cloned file attached.": "Clonar lo fichièr junt.", + "Cloned: '%s'": "Clonar: '%s'", "Attach a file": "Juntar un fichièr ", "Remove attachment": "Levar la pèca junta", "Your browser does not support uploading encrypted files. Please use a newer browser.": @@ -155,6 +155,9 @@ "Enter password": "Picatz lo senhal", "Loading…": "Cargament…", + "Decrypting paste…": "Decrypting paste…", + "Preparing new paste…": "Preparing new paste…", "In case this message never disappears please have a look at this FAQ for information to troubleshoot.": - "Se per cas aqueste messatge quita pas de s'afichar mercés de gaitar aquesta FAQ per las solucions (en Anglés)." + "Se per cas aqueste messatge quita pas de s'afichar mercés de gaitar aquesta FAQ per las solucions (en Anglés).", + "+++ no paste text +++": "+++ no paste text +++" } diff --git a/i18n/pl.json b/i18n/pl.json index 99554981..b722c480 100644 --- a/i18n/pl.json +++ b/i18n/pl.json @@ -92,7 +92,7 @@ "Odpowiedz", "Anonymous": "Anonim", - "Anonymous avatar (Vizhash of the IP address)": + "Avatar generated from IP address": "Anonimowy avatar (Vizhash z adresu IP)", "Add comment": "Dodaj komentarz", @@ -112,8 +112,8 @@ "bląd serwera lub brak odpowiedzi", "Could not post comment: %s": "Nie udało się wysłać komentarza: %s", - "Sending paste (Please move your mouse for more entropy)…": - "Wysyłanie wklejki (proszę poruszać myszą aby uzyskać większą entropię)…", + "Please move your mouse for more entropy…": + "Proszę poruszać myszą aby uzyskać większą entropię…", "Sending paste…": "Wysyłanie wklejki…", "Your paste is %s (Hit [Ctrl]+[c] to copy)": @@ -129,7 +129,7 @@ "Source Code": "Kod źródłowy", "Markdown": "Markdown", "Download attachment": "Pobierz załącznik", - "Cloned file attached.": "Sklonowano załączony plik.", + "Cloned: '%s'": "Sklonowano: '%s'", "Attach a file": "Załącz plik", "Remove attachment": "Usuń załącznik", "Your browser does not support uploading encrypted files. Please use a newer browser.": @@ -146,6 +146,9 @@ "Enter password": "Wpisz hasło", "Loading…": "Loading…", + "Decrypting paste…": "Decrypting paste…", + "Preparing new paste…": "Preparing new paste…", "In case this message never disappears please have a look at this FAQ for information to troubleshoot.": - "In case this message never disappears please have a look at this FAQ for information to troubleshoot (in English)." + "In case this message never disappears please have a look at this FAQ for information to troubleshoot (in English).", + "+++ no paste text +++": "+++ no paste text +++" } diff --git a/i18n/pt.json b/i18n/pt.json index 06f72c51..01d5dfe8 100644 --- a/i18n/pt.json +++ b/i18n/pt.json @@ -146,6 +146,9 @@ "Enter password": "Digite a senha", "Loading…": "Carregando…", + "Decrypting paste…": "Decrypting paste…", + "Preparing new paste…": "Preparing new paste…", "In case this message never disappears please have a look at this FAQ for information to troubleshoot.": - "Caso essa mensagem nunca desapareça, por favor veja este FAQ para saber como resolver os problemas." + "Caso essa mensagem nunca desapareça, por favor veja este FAQ para saber como resolver os problemas.", + "+++ no paste text +++": "+++ no paste text +++" } diff --git a/i18n/ru.json b/i18n/ru.json index ca4c707b..53431ad2 100644 --- a/i18n/ru.json +++ b/i18n/ru.json @@ -92,7 +92,7 @@ "Ответить", "Anonymous": "Аноним", - "Anonymous avatar (Vizhash of the IP address)": + "Avatar generated from IP address": "Анонимный аватар (Vizhash IP адреса)", "Add comment": "Добавить комментарий", @@ -104,6 +104,8 @@ "Отправка комментария…", "Comment posted.": "Комментарий опубликован.", + "Could not refresh display: %s": + "Could not refresh display: %s", "unknown status": "неизвестная причина", "server error or not responding": @@ -138,7 +140,6 @@ "Source Code": "Исходный код", "Markdown": "Язык разметки", "Download attachment": "Скачать прикрепленный файл", - "Cloned file attached.": "Дубликат файла прикреплен.", "Cloned: '%s'": "Дублировано: '%s'", "The cloned file '%s' was attached to this paste.": "Дубликат файла '%s' был прикреплен к этой записи.", @@ -157,13 +158,10 @@ "Расшифровать", "Enter password": "Введите пароль", - "Uploading paste… Please wait.": - "Отправка записи… Пожалуйста подождите.", "Loading…": "Загрузка…", "Decrypting paste…": "Расшифровка записи…", "Preparing new paste…": "Подготовка новой записи…", "In case this message never disappears please have a look at this FAQ for information to troubleshoot.": "Если данное сообщение не исчезает длительное время, посмотрите этот FAQ с информацией о возможном решении проблемы (на английском).", - "+++ no paste text +++": - "+++ в записи нет текста +++" + "+++ no paste text +++": "+++ в записи нет текста +++" } diff --git a/i18n/sl.json b/i18n/sl.json index 00b09702..4c0a7e9f 100644 --- a/i18n/sl.json +++ b/i18n/sl.json @@ -92,7 +92,7 @@ "Odgovori", "Anonymous": "Aninomno", - "Anonymous avatar (Vizhash of the IP address)": + "Avatar generated from IP address": "Anonimen avatar (Vizhash IP naslova)", "Add comment": "Dodaj komentar", @@ -112,8 +112,8 @@ "napaka na strežniku, ali pa se strežnik ne odziva", "Could not post comment: %s": "Komentarja ni bilo mogoče objaviti : %s", - "Sending paste (Please move your mouse for more entropy)…": - "Pošiljam prilepek (prosim premakni svojo miško za več entropije) …", + "Please move your mouse for more entropy…": + "Prosim premakni svojo miško za več entropije…", "Sending paste…": "Pošiljam prilepek…", "Your paste is %s (Hit [Ctrl]+[c] to copy)": @@ -138,7 +138,7 @@ "Source Code": "Odprta koda", "Markdown": "Markdown", "Download attachment": "Pretoči priponko", - "Cloned file attached.": "Pripeta datoteka klonirana", + "Cloned: '%s'": "'%s' klonirana", "Attach a file": "Pripni datoteko", "Remove attachment": "Odstrani priponko", "Your browser does not support uploading encrypted files. Please use a newer browser.": @@ -155,6 +155,9 @@ "Enter password": "Prosim vnesi geslo", "Loading…": "Loading…", + "Decrypting paste…": "Decrypting paste…", + "Preparing new paste…": "Preparing new paste…", "In case this message never disappears please have a look at this FAQ for information to troubleshoot.": - "In case this message never disappears please have a look at this FAQ for information to troubleshoot (in English)." + "In case this message never disappears please have a look at this FAQ for information to troubleshoot (in English).", + "+++ no paste text +++": "+++ no paste text +++" } diff --git a/tst/I18nTest.php b/tst/I18nTest.php index 91c92aa6..187fd1a2 100644 --- a/tst/I18nTest.php +++ b/tst/I18nTest.php @@ -142,4 +142,24 @@ class I18nTest extends PHPUnit_Framework_TestCase I18n::loadTranslations(); $this->assertEquals('some string + 1', I18n::_('some %s + %d', 'string', 1), 'browser language en'); } + + public function testMessageIdsExistInAllLanguages() + { + $messageIds = array(); + $languages = array(); + $dir = dir(PATH . 'i18n'); + while (false !== ($file = $dir->read())) { + if (strlen($file) === 7) { + $language = substr($file, 0, 2); + $translations = json_decode(file_get_contents(PATH . 'i18n' . DIRECTORY_SEPARATOR . $file), true); + $messageIds = array_unique($messageIds + array_keys($translations)); + $languages[$language] = $translations; + } + } + foreach ($messageIds as $messageId) { + foreach (array_keys($languages) as $language) { + $this->assertArrayHasKey($messageId, $languages[$language], "message ID '$messageId' exists in translation file $language.json"); + } + } + } } From 44327bed58b409500fdb62b37efabadc1309a4ba Mon Sep 17 00:00:00 2001 From: El RIDO Date: Sat, 25 Mar 2017 13:19:11 +0100 Subject: [PATCH 0123/1638] added missing/removed translation IDs found using improved unit test (#201) --- i18n/es.json | 1 + i18n/fr.json | 1 + i18n/it.json | 1 + i18n/no.json | 1 + i18n/oc.json | 1 + i18n/pl.json | 1 + i18n/pt.json | 1 + i18n/ru.json | 2 -- i18n/sl.json | 1 + tst/I18nTest.php | 26 +++++++++++++++++++------- 10 files changed, 27 insertions(+), 9 deletions(-) diff --git a/i18n/es.json b/i18n/es.json index 98251e98..427f90dc 100644 --- a/i18n/es.json +++ b/i18n/es.json @@ -130,6 +130,7 @@ "Markdown": "Markdown", "Download attachment": "Descargar adjunto", "Cloned: '%s'": "Clonado: '%s'.", + "The cloned file '%s' was attached to this paste.": "The cloned file '%s' was attached to this paste.", "Attach a file": "Adjuntar archivo", "Remove attachment": "Remover adjunto", "Your browser does not support uploading encrypted files. Please use a newer browser.": diff --git a/i18n/fr.json b/i18n/fr.json index d5d447e0..10c36a4e 100644 --- a/i18n/fr.json +++ b/i18n/fr.json @@ -139,6 +139,7 @@ "Markdown": "Markdown", "Download attachment": "Télécharger la pièce jointe", "Cloned: '%s'": "Cloner '%s'", + "The cloned file '%s' was attached to this paste.": "The cloned file '%s' was attached to this paste.", "Attach a file": "Attacher un fichier ", "Remove attachment": "Enlever l'attachement", "Your browser does not support uploading encrypted files. Please use a newer browser.": diff --git a/i18n/it.json b/i18n/it.json index b7b19e8c..583e0e49 100644 --- a/i18n/it.json +++ b/i18n/it.json @@ -130,6 +130,7 @@ "Markdown": "Markdown", "Download attachment": "Scarica Allegato", "Cloned: '%s'": "Copia: '%s'", + "The cloned file '%s' was attached to this paste.": "The cloned file '%s' was attached to this paste.", "Attach a file": "Allega un file", "Remove attachment": "Rimuovi allegato", "Your browser does not support uploading encrypted files. Please use a newer browser.": diff --git a/i18n/no.json b/i18n/no.json index f131e4fe..c0d376c5 100644 --- a/i18n/no.json +++ b/i18n/no.json @@ -130,6 +130,7 @@ "Markdown": "Oppmerket", "Download attachment": "Last ned vedlegg", "Cloned: '%s'": "Kopier: '%s'", + "The cloned file '%s' was attached to this paste.": "The cloned file '%s' was attached to this paste.", "Attach a file": "Legg til fil", "Remove attachment": "Slett vedlegg", "Your browser does not support uploading encrypted files. Please use a newer browser.": diff --git a/i18n/oc.json b/i18n/oc.json index 8f923b1b..90478768 100644 --- a/i18n/oc.json +++ b/i18n/oc.json @@ -139,6 +139,7 @@ "Markdown": "Markdown", "Download attachment": "Telecargar la pèça junta", "Cloned: '%s'": "Clonar: '%s'", + "The cloned file '%s' was attached to this paste.": "The cloned file '%s' was attached to this paste.", "Attach a file": "Juntar un fichièr ", "Remove attachment": "Levar la pèca junta", "Your browser does not support uploading encrypted files. Please use a newer browser.": diff --git a/i18n/pl.json b/i18n/pl.json index b722c480..82d9b579 100644 --- a/i18n/pl.json +++ b/i18n/pl.json @@ -130,6 +130,7 @@ "Markdown": "Markdown", "Download attachment": "Pobierz załącznik", "Cloned: '%s'": "Sklonowano: '%s'", + "The cloned file '%s' was attached to this paste.": "The cloned file '%s' was attached to this paste.", "Attach a file": "Załącz plik", "Remove attachment": "Usuń załącznik", "Your browser does not support uploading encrypted files. Please use a newer browser.": diff --git a/i18n/pt.json b/i18n/pt.json index 01d5dfe8..6af67cf6 100644 --- a/i18n/pt.json +++ b/i18n/pt.json @@ -130,6 +130,7 @@ "Markdown": "Markdown", "Download attachment": "Baixar anexo", "Cloned: '%s'": "Clonado: '%s'", + "The cloned file '%s' was attached to this paste.": "The cloned file '%s' was attached to this paste.", "Attach a file": "Anexar um arquivo", "Remove attachment": "Remover anexo", "Your browser does not support uploading encrypted files. Please use a newer browser.": diff --git a/i18n/ru.json b/i18n/ru.json index 53431ad2..989d3e4f 100644 --- a/i18n/ru.json +++ b/i18n/ru.json @@ -110,8 +110,6 @@ "неизвестная причина", "server error or not responding": "ошибка сервера или нет ответа", - "unknown error": - "неизвестная ошибка", "Could not post comment: %s": "Не удалось опубликовать комментарий: %s", "Please move your mouse for more entropy…": diff --git a/i18n/sl.json b/i18n/sl.json index 4c0a7e9f..21db8c1d 100644 --- a/i18n/sl.json +++ b/i18n/sl.json @@ -139,6 +139,7 @@ "Markdown": "Markdown", "Download attachment": "Pretoči priponko", "Cloned: '%s'": "'%s' klonirana", + "The cloned file '%s' was attached to this paste.": "The cloned file '%s' was attached to this paste.", "Attach a file": "Pripni datoteko", "Remove attachment": "Odstrani priponko", "Your browser does not support uploading encrypted files. Please use a newer browser.": diff --git a/tst/I18nTest.php b/tst/I18nTest.php index 187fd1a2..c7ded0ee 100644 --- a/tst/I18nTest.php +++ b/tst/I18nTest.php @@ -146,19 +146,31 @@ class I18nTest extends PHPUnit_Framework_TestCase public function testMessageIdsExistInAllLanguages() { $messageIds = array(); - $languages = array(); - $dir = dir(PATH . 'i18n'); + $languages = array(); + $dir = dir(PATH . 'i18n'); while (false !== ($file = $dir->read())) { if (strlen($file) === 7) { - $language = substr($file, 0, 2); - $translations = json_decode(file_get_contents(PATH . 'i18n' . DIRECTORY_SEPARATOR . $file), true); - $messageIds = array_unique($messageIds + array_keys($translations)); - $languages[$language] = $translations; + $language = substr($file, 0, 2); + $languageMessageIds = array_keys( + json_decode( + file_get_contents(PATH . 'i18n' . DIRECTORY_SEPARATOR . $file), + true + ) + ); + $messageIds = array_unique(array_merge($messageIds, $languageMessageIds)); + $languages[$language] = $languageMessageIds; } } foreach ($messageIds as $messageId) { foreach (array_keys($languages) as $language) { - $this->assertArrayHasKey($messageId, $languages[$language], "message ID '$messageId' exists in translation file $language.json"); + // most languages don't translate the data size units, ignore those + if ($messageId !== 'B' && strlen($messageId) !== 3 && strpos($messageId, 'B', 2) !== 2) { + $this->assertContains( + $messageId, + $languages[$language], + "message ID '$messageId' exists in translation file $language.json" + ); + } } } } From 3fc6ede5bbc32d61826d53ddfe0b478c474148b2 Mon Sep 17 00:00:00 2001 From: Tulio Leao Date: Sat, 25 Mar 2017 09:26:31 -0300 Subject: [PATCH 0124/1638] Translate missing IDs to Portuguese As referenced on issue #201, some IDs were missing from the pt translation, which were now translated. --- i18n/pt.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/i18n/pt.json b/i18n/pt.json index 6af67cf6..05ce23d1 100644 --- a/i18n/pt.json +++ b/i18n/pt.json @@ -130,7 +130,7 @@ "Markdown": "Markdown", "Download attachment": "Baixar anexo", "Cloned: '%s'": "Clonado: '%s'", - "The cloned file '%s' was attached to this paste.": "The cloned file '%s' was attached to this paste.", + "The cloned file '%s' was attached to this paste.": "O arquivo clonado '%s' foi anexado a essa cópia.", "Attach a file": "Anexar um arquivo", "Remove attachment": "Remover anexo", "Your browser does not support uploading encrypted files. Please use a newer browser.": @@ -147,9 +147,9 @@ "Enter password": "Digite a senha", "Loading…": "Carregando…", - "Decrypting paste…": "Decrypting paste…", - "Preparing new paste…": "Preparing new paste…", + "Decrypting paste…": "Decifrando cópia…", + "Preparing new paste…": "Preparando nova cópia…", "In case this message never disappears please have a look at this FAQ for information to troubleshoot.": "Caso essa mensagem nunca desapareça, por favor veja este FAQ para saber como resolver os problemas.", - "+++ no paste text +++": "+++ no paste text +++" + "+++ no paste text +++": "+++ sem texto de cópia +++" } From d8e0a6e986cb42d0d1040eecf6fc01e4bb1ccb2d Mon Sep 17 00:00:00 2001 From: Stefano Martinelli Date: Sat, 25 Mar 2017 14:42:31 +0100 Subject: [PATCH 0125/1638] Italian translation update - new strings translated - couple of minor errors fixed Some strings (i.e. line 133) are literal translations. I could probably give a better translation by reading the messages in their context, when I get the next update. --- i18n/it.json | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/i18n/it.json b/i18n/it.json index 583e0e49..e69ea81e 100644 --- a/i18n/it.json +++ b/i18n/it.json @@ -18,7 +18,7 @@ "Invalid data.": "Dati non validi.", "You are unlucky. Try again.": - "Riprova, sarai più fortunato.", + "Ritenta, sarai più fortunato.", "Error saving comment. Sorry.": "Errore durante il salvataggio del commento.", "Error saving paste. Sorry.": @@ -93,7 +93,7 @@ "Anonymous": "Anonimo", "Avatar generated from IP address": - "Avatar Anonimo (Vizhash dell'indirizzo IP)", + "Avatar generato dall'indirizzo IP)", "Add comment": "Aggiungi un commento", "Optional nickname…": @@ -107,7 +107,7 @@ "Could not refresh display: %s": "Non riesco ad aggiornare il display: %s", "unknown status": - "errore sconosciuto", + "stato sconosciuto", "server error or not responding": "errore o mancata risposta dal server", "Could not post comment: %s": @@ -117,11 +117,11 @@ "Sending paste…": "Messaggio in fase di invio…", "Your paste is %s (Hit [Ctrl]+[c] to copy)": - "Il tuo messaggio è qui: %s ([CTRL | CMD]+[C] per copiare il link)", + "Il tuo messaggio è qui: %s (Premi [Ctrl]+[c] (Windows) o [Cmd]+[c] (Mac) per copiare il link)", "Delete data": "Cancella i dati", "Could not create paste: %s": - "Non rieco a creare il messaggio: %s", + "Non riesco a creare il messaggio: %s", "Cannot decrypt paste: Decryption key missing in URL (Did you use a redirector or an URL shortener which strips part of the URL?)": "Non riesco a decifrare il messaggio: manca la chiave di decifrazione nell'URL (La chiave è parte integrante dell'URL. Per caso hai usato un Redirector o un altro servizio che ha rimosso una parte dell'URL?)", "Format": "Formato", @@ -129,8 +129,8 @@ "Source Code": "Codice Sorgente", "Markdown": "Markdown", "Download attachment": "Scarica Allegato", - "Cloned: '%s'": "Copia: '%s'", - "The cloned file '%s' was attached to this paste.": "The cloned file '%s' was attached to this paste.", + "Cloned: '%s'": "Clonato: '%s'", + "The cloned file '%s' was attached to this paste.": "Il file clonato '%s' era allegato a questo messaggio.", "Attach a file": "Allega un file", "Remove attachment": "Rimuovi allegato", "Your browser does not support uploading encrypted files. Please use a newer browser.": @@ -143,13 +143,13 @@ "%s requires the PATH to end in a \"%s\". Please update the PATH in your index.php.": "%s necessita che PATH termini con \"%s\". Aggiorna la variabile PATH nel tuo index.php.", "Decrypt": - "Decrypt", + "Decifra", "Enter password": "Inserisci la password", - "Loading…": "Loading…", - "Decrypting paste…": "Decrypting paste…", - "Preparing new paste…": "Preparing new paste…", + "Loading…": "Carico…", + "Decrypting paste…": "Decifro il messaggio…", + "Preparing new paste…": "Preparo il nuovo messaggio…", "In case this message never disappears please have a look at this FAQ for information to troubleshoot.": - "Nel caso questo messaggio non scompaia, controlla questa FAQ per trovare informazioni su come risolvere il problema (in Inglese).", - "+++ no paste text +++": "+++ no paste text +++" + "Nel caso questo messaggio non scompaia, controlla questa FAQ per trovare informazioni su come risolvere il problema.", + "+++ no paste text +++": "+++ nessun testo nel messaggio +++" } From 82e45915c73b566a1247c459fcbbd785c783d747 Mon Sep 17 00:00:00 2001 From: Alexey Pyltsyn Date: Sat, 25 Mar 2017 17:06:56 +0300 Subject: [PATCH 0126/1638] Update Russian translation --- i18n/ru.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/i18n/ru.json b/i18n/ru.json index 989d3e4f..da462c38 100644 --- a/i18n/ru.json +++ b/i18n/ru.json @@ -93,7 +93,7 @@ "Anonymous": "Аноним", "Avatar generated from IP address": - "Анонимный аватар (Vizhash IP адреса)", + "Аватар, сгенерированный из IP-адреса", "Add comment": "Добавить комментарий", "Optional nickname…": @@ -105,7 +105,7 @@ "Comment posted.": "Комментарий опубликован.", "Could not refresh display: %s": - "Could not refresh display: %s", + "Не удалось обновить отображение: %s", "unknown status": "неизвестная причина", "server error or not responding": From d23c696e97979d3a513ac991586f933f0de3bbd5 Mon Sep 17 00:00:00 2001 From: stefanomarty Date: Sat, 25 Mar 2017 17:33:54 +0100 Subject: [PATCH 0127/1638] Revert delete of (in Inglese) --- i18n/it.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/i18n/it.json b/i18n/it.json index e69ea81e..d7885d70 100644 --- a/i18n/it.json +++ b/i18n/it.json @@ -150,6 +150,6 @@ "Decrypting paste…": "Decifro il messaggio…", "Preparing new paste…": "Preparo il nuovo messaggio…", "In case this message never disappears please have a look at this FAQ for information to troubleshoot.": - "Nel caso questo messaggio non scompaia, controlla questa FAQ per trovare informazioni su come risolvere il problema.", + "Nel caso questo messaggio non scompaia, controlla questa FAQ per trovare informazioni su come risolvere il problema (in Inglese).", "+++ no paste text +++": "+++ nessun testo nel messaggio +++" } From cd40717301125d8a4cf638b81395b6f18733ccfc Mon Sep 17 00:00:00 2001 From: El RIDO Date: Sat, 25 Mar 2017 18:44:20 +0100 Subject: [PATCH 0128/1638] fixing #209, refactoring regression when file upload is disabled --- js/privatebin.js | 3 ++- tpl/bootstrap.php | 2 +- tpl/page.php | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/js/privatebin.js b/js/privatebin.js index c55a0271..65d81878 100644 --- a/js/privatebin.js +++ b/js/privatebin.js @@ -2082,7 +2082,8 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { */ me.hasAttachment = function() { - return ($attachmentLink.prop('href') !== '') + var link = $attachmentLink.prop('href'); + return (typeof link !== 'undefined' && link !== '') } /** diff --git a/tpl/bootstrap.php b/tpl/bootstrap.php index 5381863b..e1df5d7e 100644 --- a/tpl/bootstrap.php +++ b/tpl/bootstrap.php @@ -69,7 +69,7 @@ if ($MARKDOWN): - + diff --git a/tpl/page.php b/tpl/page.php index 18d60e32..c38a1ebb 100644 --- a/tpl/page.php +++ b/tpl/page.php @@ -47,7 +47,7 @@ if ($MARKDOWN): - + From e1ea14627f8a4dafba4e6db5023b991d0ef6c89c Mon Sep 17 00:00:00 2001 From: El RIDO Date: Sun, 26 Mar 2017 06:47:57 +0200 Subject: [PATCH 0129/1638] handling JSVerify RNG state 88caf85079d32e416b --- js/test.js | 2 +- tst/README.md | 69 ++++++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 66 insertions(+), 5 deletions(-) diff --git a/js/test.js b/js/test.js index f328b51d..6f193f1d 100644 --- a/js/test.js +++ b/js/test.js @@ -277,7 +277,7 @@ describe('Helper', function () { var key = item.replace(/[\s;,=]/g, Array(i+2).join('£')), value = (values[i] || values[0]).replace(/[\s;,=]/g, ''); cookieArray.push(key + '=' + value); - if (Math.random() < 1 / i) + if (Math.random() < 1 / i || selectedKey === key) { selectedKey = key; selectedValue = value; diff --git a/tst/README.md b/tst/README.md index 76e69ee1..e11bc495 100644 --- a/tst/README.md +++ b/tst/README.md @@ -2,7 +2,7 @@ Running PHP unit tests ====================== In order to run these tests, you will need to install the following packages -and its dependencies: +and their dependencies: * phpunit * php-gd * php-sqlite3 @@ -13,12 +13,30 @@ Example for Debian and Ubuntu: $ sudo apt install phpunit php-gd php-sqlite php-xdebug ``` -To run the tests, just change into this directory and run phpunit: +To run the tests, change into the `tst` directory and run phpunit: ```console $ cd PrivateBin/tst $ phpunit ``` +Additionally there is the `ConfigurationTestGenerator`. Based on the +configurations defined in its constructor, it generates the unit test file +`tst/ConfigurationCombinationsTest.php`, containing all possible combinations +of these configurations and tests for (most of the) valid combinations. Some of +combinations can't be tested with this method, i.e. a valid option combined with +an invalid one. Other very specific test cases (i.e. to trigger multiple errors) +are covered in `tst/PrivateBinTest.php`. Here is how to generate the +configuration test and run it: + +```console +$ cd PrivateBin/tst +$ php ConfigurationTestGenerator.php +$ phpunit ConfigurationCombinationsTest.php +``` + +Note that it can take an hour or longer to run the several thousand tests. + + Running JavaScript unit tests ============================= @@ -36,8 +54,8 @@ $ cd PrivateBin/js $ npm install jsverify jsdom jsdom-global ``` -Example for Debian and Ubuntu, including steps to allow current user to install -node modules globally: +Example for Debian and Ubuntu, including steps to allow the current user to +install node modules globally: ```console $ sudo apt install npm $ sudo mkdir /usr/local/lib/node_modules @@ -54,3 +72,46 @@ $ cd PrivateBin/js $ istanbul cover _mocha ``` +Property based unit testing +--------------------------- + +In the JavaScript unit tests we use the JSVerify library to leverage property +based unit testing. Instead of artificially creating specific test cases to +cover all relevant paths of the tested code (with the generated coverage reports +providing means to check the tested paths), property based testing allows us to +describe the patterns of data that are valid input. + +With each run of the tests, for each `jsc.property` 100 random inputs are +generated and tested. For example we tell the test to generate random strings, +which will include empty strings, numeric strings, long strings, unicode +sequences, etc. This is great for finding corner cases that one might not think +of when explicitly writing one test case at a time. + +There is another benefit, too: When an error is found, JSVerify will try to find +the smallest, still failing test case for you and print this out including the +associated random number generator (RNG) state, so you can reproduce it easily: + +```console +[...] + + 30 passing (3s) + 1 failing + + 1) Helper getCookie returns the requested cookie: + Error: Failed after 30 tests and 11 shrinks. rngState: 88caf85079d32e416b; Counterexample: ["{", "9", "9", "YD8%fT"]; [" ", "_|K:"]; + +[...] +``` + +Of course it may just be that you need to adjust a test case if the random +pattern generated is ambiguous. In the above example the cookie string would +contain two identical keys "9", something that may not be valid, but that our +code could encounter and needs to be able to handle. + +After you adjusted the code of the library or the test you can rerun the test +with the same RNG state as follows: + +```console +$ istanbul cover _mocha -- test.js --jsverifyRngState 88caf85079d32e416b +``` + From 37f5d99bc4d2d4853a4f134ff2cd85825d9f0a41 Mon Sep 17 00:00:00 2001 From: El RIDO Date: Sun, 26 Mar 2017 09:24:42 +0200 Subject: [PATCH 0130/1638] finalizing tests for I18n class, AJAX loading of translations needs to be tested in browser, mocked for now --- .gitignore | 1 + js/privatebin.js | 10 +++---- js/test.js | 76 ++++++++++++++++++++++++++++++++++++++++++++--- tpl/bootstrap.php | 2 +- tpl/page.php | 2 +- 5 files changed, 80 insertions(+), 11 deletions(-) diff --git a/.gitignore b/.gitignore index a752f8cc..c17e3b4a 100644 --- a/.gitignore +++ b/.gitignore @@ -30,6 +30,7 @@ vendor/**/build_phar.php # Ignore local node modules, unit testing logs, api docs and eclipse project files js/node_modules/ +js/test.log tst/log/ tst/ConfigurationCombinationsTest.php .settings diff --git a/js/privatebin.js b/js/privatebin.js index 17b4457d..8cf76831 100644 --- a/js/privatebin.js +++ b/js/privatebin.js @@ -306,7 +306,7 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { * @param {object} document * @class */ - var I18n = (function (window, document) { + var I18n = (function () { var me = {}; /** @@ -544,14 +544,14 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { * @name I18n.reset * @function */ - me.reset = function() + me.reset = function(mockLanguage, mockTranslations) { - language = null; - translations = {}; + language = mockLanguage || null; + translations = mockTranslations || {}; } return me; - })(window, document); + })(); /** * handles everything related to en/decryption diff --git a/js/test.js b/js/test.js index 6f193f1d..462d6ff1 100644 --- a/js/test.js +++ b/js/test.js @@ -13,7 +13,9 @@ var jsc = require('jsverify'), }) ), // schemas supported by the whatwg-url library - schemas = ['ftp','gopher','http','https','ws','wss']; + schemas = ['ftp','gopher','http','https','ws','wss'], + supportedLanguages = ['de', 'es', 'fr', 'it', 'no', 'pl', 'pt', 'oc', 'ru', 'sl', 'zh'], + logFile = require('fs').createWriteStream('test.log'); global.$ = global.jQuery = require('./jquery-3.1.1'); global.sjcl = require('./sjcl-1.0.6'); @@ -22,6 +24,11 @@ global.RawDeflate = require('./rawdeflate-0.5'); require('./rawinflate-0.3'); require('./privatebin'); +// redirect console messages to log file +console.warn = console.error = function (msg) { + logFile.write(msg + '\n'); +} + describe('Helper', function () { describe('secondsToHuman', function () { after(function () { @@ -339,12 +346,30 @@ describe('I18n', function () { 'returns message ID unchanged if no translation found', 'string', function (messageId) { - messageId = messageId.replace(/%(s|d)/g, '%%'); - var result = $.PrivateBin.I18n.translate(messageId); + messageId = messageId.replace(/%(s|d)/g, '%%'); + var plurals = [messageId, messageId + 's'], + fake = [messageId], + result = $.PrivateBin.I18n.translate(messageId); $.PrivateBin.I18n.reset(); + var alias = $.PrivateBin.I18n._(messageId); $.PrivateBin.I18n.reset(); - return messageId === result && messageId === alias; + + var p_result = $.PrivateBin.I18n.translate(plurals); + $.PrivateBin.I18n.reset(); + + var p_alias = $.PrivateBin.I18n._(plurals); + $.PrivateBin.I18n.reset(); + + var f_result = $.PrivateBin.I18n.translate(fake); + $.PrivateBin.I18n.reset(); + + var f_alias = $.PrivateBin.I18n._(fake); + $.PrivateBin.I18n.reset(); + + return messageId === result && messageId === alias && + messageId === p_result && messageId === p_alias && + messageId === f_result && messageId === f_alias; } ); jsc.property( @@ -366,6 +391,49 @@ describe('I18n', function () { } ); }); + + describe('getPluralForm', function () { + before(function () { + $.PrivateBin.I18n.reset(); + }); + + jsc.property( + 'returns valid key for plural form', + jsc.elements(supportedLanguages), + 'integer', + function(language, n) { + $.PrivateBin.I18n.reset(language); + var result = $.PrivateBin.I18n.getPluralForm(n); + // arabic seems to have the highest plural count with 6 forms + return result >= 0 && result <= 5; + } + ); + }); + + // loading of JSON via AJAX needs to be tested in the browser, this just mocks it + // TODO: This needs to be tested using a browser. + describe('loadTranslations', function () { + before(function () { + $.PrivateBin.I18n.reset(); + }); + + jsc.property( + 'downloads and handles any supported language', + jsc.elements(supportedLanguages), + function(language) { + var clean = jsdom('', {url: 'https://privatebin.net/', cookie: ['lang=' + language]}); + + $.PrivateBin.I18n.reset('en'); + $.PrivateBin.I18n.loadTranslations(); + $.PrivateBin.I18n.reset(language, require('../i18n/' + language + '.json')); + var result = $.PrivateBin.I18n.translate('en'), + alias = $.PrivateBin.I18n._('en'); + + clean(); + return language === result && language === alias; + } + ); + }); }); describe('Model', function () { diff --git a/tpl/bootstrap.php b/tpl/bootstrap.php index c55a766f..6fc01bd1 100644 --- a/tpl/bootstrap.php +++ b/tpl/bootstrap.php @@ -69,7 +69,7 @@ if ($MARKDOWN): - + diff --git a/tpl/page.php b/tpl/page.php index 6316a182..46b8df14 100644 --- a/tpl/page.php +++ b/tpl/page.php @@ -47,7 +47,7 @@ if ($MARKDOWN): - + From cdb62b44c768616f31d81228e51082eb650480af Mon Sep 17 00:00:00 2001 From: El RIDO Date: Sun, 26 Mar 2017 11:34:19 +0200 Subject: [PATCH 0131/1638] basic tests for CryptTool classes encryption and compression functions --- js/test.js | 31 ++++++++++++++++++++++++++++--- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/js/test.js b/js/test.js index 462d6ff1..5625710f 100644 --- a/js/test.js +++ b/js/test.js @@ -2,6 +2,9 @@ var jsc = require('jsverify'), jsdom = require('jsdom-global'), cleanup = jsdom(), + base64lib = require('./base64-2.1.9'), + rawdeflatelib = require('./rawdeflate-0.5'), + rawinflatelib = require('./rawinflate-0.3'), a2zString = ['a','b','c','d','e','f','g','h','i','j','k','l','m', 'n','o','p','q','r','s','t','u','v','w','x','y','z'], @@ -19,9 +22,9 @@ var jsc = require('jsverify'), global.$ = global.jQuery = require('./jquery-3.1.1'); global.sjcl = require('./sjcl-1.0.6'); -global.Base64 = require('./base64-2.1.9'); -global.RawDeflate = require('./rawdeflate-0.5'); -require('./rawinflate-0.3'); +global.Base64 = base64lib.Base64; +global.RawDeflate = rawdeflatelib.RawDeflate; +global.RawDeflate.inflate = rawinflatelib.RawDeflate.inflate; require('./privatebin'); // redirect console messages to log file @@ -436,6 +439,28 @@ describe('I18n', function () { }); }); +describe('CryptTool', function () { + describe('cipher & decipher', function () { + this.timeout(20000); + it('can en- and decrypt any message', function () { + jsc.check(jsc.forall( + 'string', + 'string', + 'string', + function (key, password, message) { + return message === $.PrivateBin.CryptTool.decipher( + key, + password, + $.PrivateBin.CryptTool.cipher(key, password, message) + ); + } + ), + // reducing amount of checks as running 100 takes about 5 minutes + {tests: 5, quiet: true}); + }); + }); +}); + describe('Model', function () { describe('getPasteId', function () { before(function () { From 3cf005c8ae3f2e73360a953c25bc2a7b762c7a99 Mon Sep 17 00:00:00 2001 From: El RIDO Date: Sun, 26 Mar 2017 16:16:15 +0200 Subject: [PATCH 0132/1638] added test with hardcoded v1 pastes to ensure decryption of the original paste format still works, even when the format is changed in the future --- js/test.js | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/js/test.js b/js/test.js index 5625710f..c355be82 100644 --- a/js/test.js +++ b/js/test.js @@ -458,6 +458,27 @@ describe('CryptTool', function () { // reducing amount of checks as running 100 takes about 5 minutes {tests: 5, quiet: true}); }); + + // The below static unit test is included to ensure deciphering of "classic" + // SJCL based pastes still works + it('supports v1 ciphertext (SJCL)', function () { + // Of course you can easily decipher the following texts, if you like. + // Bonus points for finding their sources and hidden meanings. + var paste1 = $.PrivateBin.CryptTool.decipher( + '6t2qsmLyfXIokNCL+3/yl15rfTUBQvm5SOnFPvNE7Q8=', + // -- "That's amazing. I've got the same combination on my luggage." + Array.apply(0, Array(6)).map(function(_,b) { return b + 1; }).join(''), + '{"iv":"4HNFIl7eYbCh6HuShctTIA==","v":1,"iter":10000,"ks":256,"ts":128,"mode":"gcm","adata":"","cipher":"aes","salt":"u0lQvePq6L0=","ct":"fGPUVrDyaVr1ZDGb+kqQ3CPEW8x4YKGfzHDmA0Vjkh250aWNe7Cnigkps9aaFVMX9AaerrTp3yZbojJtNqVGMfLdUTu+53xmZHqRKxCCqSfDNSNoW4Oxk5OVgAtRyuG4bXHDsWTXDNz2xceqzVFqhkwTwlUchrV7uuFK/XUKTNjPFM744moivIcBbfM2FOeKlIFs8RYPYuvqQhp2rMLlNGwwKh//4kykQsHMQDeSDuJl8stMQzgWR/btUBZuwNZEydkMH6IPpTdf5WTSrZ+wC2OK0GutCm4UaEe6txzaTMfu+WRVu4PN6q+N+2zljWJ1XdpVcN/i0Sv4QVMym0Xa6y0eccEhj/69o47PmExmMMeEwExImPalMNT9JUSiZdOZJ/GdzwrwoIuq1mdQR6vSH+XJ/8jXJQ7bjjJVJYXTcT0Di5jixArI2Kpp1GGlGVFbLgPugwU1wczg+byqeDOAECXRRnQcogeaJtVcRwXwfy4j3ORFcblYMilxyHqKBewcYPRVBGtBs50cVjSIkAfR84rnc1nfvnxK/Gmm+4VBNHI6ODWNpRolVMCzXjbKYnV3Are5AgSpsTqaGl41VJGpcco6cAwi4K0Bys1seKR+bLSdUgqRrkEqSRSdu3/VTu9HhEk8an0rjTE4CBB5/LMn16p0TGLoOb32odKFIEtpanVvLjeyiVMvSxcgYLNnTi/5FiaAC4pJxRD+AZHedU1FICUeEXxIcac/4E5qjkHjX9SpQtLl80QLIVnjNliZm7QLB/nKu7W8Jb0+/CiTdV3Q9LhxlH4ciprnX+W0B00BKYFHnL9jRVzKdXhf1EHydbXMAfpCjHAXIVCkFakJinQBDIIw/SC6Yig0u0ddEID2B7LYAP1iE4RZwzTrxCB+ke2jQr8c20Jj6u6ShFOPC9DCw9XupZ4HAalVG00kSgjus+b8zrVji3/LKEhb4EBzp1ctBJCFTeXwej8ZETLoXTylev5dlwZSYAbuBPPcbFR/xAIPx3uDabd1E1gTqUc68ICIGhd197Mb2eRWiSvHr5SPsASerMxId6XA6+iQlRiI+NDR+TGVNmCnfxSlyPFMOHGTmslXOGIqGfBR8l4ft8YVZ70lCwmwTuViGc75ULSf9mM57/LmRzQFMYQtvI8IFK9JaQEMY5xz0HLtR4iyQUUdwR9e0ytBNdWF2a2WPDEnJuY/QJo4GzTlgv4QUxMXI5htsn2rf0HxCFu7Po8DNYLxTS+67hYjDIYWYaEIc8LXWMLyDm9C5fARPJ4F2BIWgzgzkNj+dVjusft2XnziamWdbS5u3kuRlVuz5LQj+R5imnqQAincdZTkTT1nYx+DatlOLllCYIHffpI="}' + ), + paste2 = $.PrivateBin.CryptTool.decipher( + 's9pmKZKOBN7EVvHpTA8jjLFH3Xlz/0l8lB4+ONPACrM=', + '', // no password + '{"iv":"WA42mdxIVXUwBqZu7JYNiw==","v":1,"iter":10000,"ks":256,"ts":128,"mode":"gcm","adata":"","cipher":"aes","salt":"jN6CjbQMJCM=","ct":"kYYMo5DFG1+w0UHiYXT5pdV0IUuXxzOlslkW/c3DRCbGFROCVkAskHce7HoRczee1N9c5MhHjVMJUIZE02qIS8UyHdJ/GqcPVidTUcj9rnDNWsTXkjVv8jCwHS/cwmAjDTWpwp5ThECN+ov/wNp/NdtTj8Qj7f/T3rfZIOCWfwLH9s4Des35UNcUidfPTNQ1l0Gm0X+r98CCUSYZjQxkZc6hRZBLPQ8EaNVooUwd5eP4GiYlmSDNA0wOSA+5isPYxomVCt+kFf58VBlNhpfNi7BLYAUTPpXT4SfH5drR9+C7NTeZ+tTCYjbU94PzYItOpu8vgnB1/a6BAM5h3m9w+giUb0df4hgTWeZnZxLjo5BN8WV+kdTXMj3/Vv0gw0DQrDcCuX/cBAjpy3lQGwlAN1vXoOIyZJUjMpQRrOLdKvLB+zcmVNtGDbgnfP2IYBzk9NtodpUa27ne0T0ZpwOPlVwevsIVZO224WLa+iQmmHOWDFFpVDlS0t0fLfOk7Hcb2xFsTxiCIiyKMho/IME1Du3X4e6BVa3hobSSZv0rRtNgY1KcyYPrUPW2fxZ+oik3y9SgGvb7XpjVIta8DWlDWRfZ9kzoweWEYqz9IA8Xd373RefpyuWI25zlHoX3nwljzsZU6dC//h/Dt2DNr+IAvKO3+u23cWoB9kgcZJ2FJuqjLvVfCF+OWcig7zs2pTYJW6Rg6lqbBCxiUUlae6xJrjfv0pzD2VYCLY7v1bVTagppwKzNI3WaluCOrdDYUCxUSe56yd1oAoLPRVbYvomRboUO6cjQhEknERyvt45og2kORJOEJayHW+jZgR0Y0jM3Nk17ubpij2gHxNx9kiLDOiCGSV5mn9mV7qd3HHcOMSykiBgbyzjobi96LT2dIGLeDXTIdPOog8wyobO4jWq0GGs0vBB8oSYXhHvixZLcSjX2KQuHmEoWzmJcr3DavdoXZmAurGWLKjzEdJc5dSD/eNr99gjHX7wphJ6umKMM+fn6PcbYJkhDh2GlJL5COXjXfm/5aj/vuyaRRWZMZtmnYpGAtAPg7AUG"}' + ); + if (!paste1.includes('securely packed in iron') || !paste2.includes('Sol is right')) { + throw Error('v1 (SJCL based) pastes could not be deciphered'); + } + }); }); }); From 806b665c6abb38018acd5047406af4e246d8f0e7 Mon Sep 17 00:00:00 2001 From: idarlund Date: Tue, 28 Mar 2017 16:42:48 +0200 Subject: [PATCH 0133/1638] Update no.json updated based on https://github.com/PrivateBin/PrivateBin/issues/201 --- i18n/no.json | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/i18n/no.json b/i18n/no.json index c0d376c5..8292e104 100644 --- a/i18n/no.json +++ b/i18n/no.json @@ -26,13 +26,13 @@ "Invalid paste ID.": "Feil innlegg ID.", "Paste is not of burn-after-reading type.": - "Innlegg er ikke av type slett-etter-lesing.", + "Innlegg er ikke av typen slett etter lesing.", "Wrong deletion token. Paste was not deleted.": "Feil slettingsnøkkel. Innlegg ble ikke fjernet.", "Paste was properly deleted.": "Innlegget er slettet.", "JavaScript is required for %s to work.
Sorry for the inconvenience.": - "Javascript kreves for at %s skal fungere
Beklager ulempene.", + "Javascript kreves for at %s skal fungere
Beklager.", "%s requires a modern browser to work.": "%s krever en moderne nettleser for å fungere.", "Still using Internet Explorer? Do yourself a favor, switch to a modern browser:": @@ -83,7 +83,7 @@ "Could not decrypt data (Wrong key?)": "Kunne ikke dekryptere data (Feil nøkkel?)", "Could not delete the paste, it was not stored in burn after reading mode.": - "Kan ikke slette innlegget, det ble ikke lagret i slett-etter-les modus.", + "Kan ikke slette innlegget, det ble ikke lagret som 'slett etter les' type.", "FOR YOUR EYES ONLY. Don't close this window, this message can't be displayed again.": "KUN FOR DINE ØYNE. Ikke lukk dette vinduet, denne meldingen kan ikke bli vist igjen.", "Could not decrypt comment; Wrong key?": @@ -93,7 +93,7 @@ "Anonymous": "Anonym", "Avatar generated from IP address": - "Anonym avatar (Vizhash av IP adressen)", + "Anonym avatar generert med data fra IP adressen)", "Add comment": "Legg til kommentar", "Optional nickname…": @@ -105,15 +105,15 @@ "Comment posted.": "Kommentar sendt.", "Could not refresh display: %s": - "Kunne ikke oppdatere skjermen: %s", + "Kunne ikke oppdatere bildet: %s", "unknown status": "ukjent status", "server error or not responding": - "server feilet eller svarer ikke", + "tjener feilet eller svarer ikke", "Could not post comment: %s": "Kunne ikke sende kommentar: %s", "Please move your mouse for more entropy…": - "Flytt musen for mere entropi…", + "Flytt musen for mer entropi…", "Sending paste…": "Sender innlegg…", "Your paste is %s (Hit [Ctrl]+[c] to copy)": @@ -129,7 +129,7 @@ "Source Code": "Kildekode", "Markdown": "Oppmerket", "Download attachment": "Last ned vedlegg", - "Cloned: '%s'": "Kopier: '%s'", + "Cloned: '%s'": "Kopiert: '%s'", "The cloned file '%s' was attached to this paste.": "The cloned file '%s' was attached to this paste.", "Attach a file": "Legg til fil", "Remove attachment": "Slett vedlegg", @@ -137,7 +137,7 @@ "Nettleseren din støtter ikke å laste opp krypterte filer. Vennligst bruk en nyere nettleser.", "Invalid attachment.": "Ugyldig vedlegg.", "Options": "Alternativer", - "Shorten URL": "Adresse-forkorter", + "Shorten URL": "Adresse forkorter", "Editor": "Rediger", "Preview": "Forhåndsvis", "%s requires the PATH to end in a \"%s\". Please update the PATH in your index.php.": @@ -147,9 +147,9 @@ "Enter password": "Skriv inn passord", "Loading…": "Laster…", - "Decrypting paste…": "Decrypting paste…", - "Preparing new paste…": "Preparing new paste…", + "Decrypting paste…": "Dekrypterer innlegg…", + "Preparing new paste…": "Klargjør nytt innlegg…", "In case this message never disappears please have a look at this FAQ for information to troubleshoot.": "Hvis denne meldingen ikke forsvinner kan du ta en titt på siden med ofte stilte spørsmål for informasjon om feilsøking.", - "+++ no paste text +++": "+++ no paste text +++" + "+++ no paste text +++": "+++ ingen innleggstekst +++" } From ec9fb750b4449ecae9fea0b3cba605612873c793 Mon Sep 17 00:00:00 2001 From: thororm Date: Sun, 2 Apr 2017 18:58:11 +0200 Subject: [PATCH 0134/1638] Adapted attachment handling to refactoring --- js/privatebin.js | 271 +++++++++++++++++++++++++++++++++++++--------- tpl/bootstrap.php | 2 +- tpl/page.php | 2 +- 3 files changed, 220 insertions(+), 55 deletions(-) diff --git a/js/privatebin.js b/js/privatebin.js index 65d81878..bbb2fe76 100644 --- a/js/privatebin.js +++ b/js/privatebin.js @@ -308,6 +308,185 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { baseUri = null; } + me.attachmentHelpers = { + attachmentData: undefined, + file: undefined, + $fileInput: undefined, + + init: function () { + me.attachmentHelpers.$fileInput = $('#file'); + me.attachmentHelpers.addDragDropHandler(); + }, + + /* + * Read file data as dataURL using the FileReader API + * https://developer.mozilla.org/en-US/docs/Web/API/FileReader#readAsDataURL() + */ + readFileData: function (file) { + if (typeof FileReader === 'undefined') { + // revert loading status… + AttachmentViewer.hideAttachment(); + AttachmentViewer.hideAttachmentPreview(); + Alert.showError(I18n._('Your browser does not support uploading encrypted files. Please use a newer browser.')); + return; + } + + var fileReader = new FileReader(); + if (file === undefined) { + file = me.attachmentHelpers.$fileInput[0].files[0]; + $('#dragAndDropFileName').text(''); + } else { + $('#dragAndDropFileName').text(file.name); + } + + me.attachmentHelpers.file = file; + + fileReader.onload = function (event) { + var dataURL = event.target.result; + me.attachmentHelpers.attachmentData = dataURL; + + if (Editor.isPreview()) { + me.attachmentHelpers.handleAttachmentPreview(AttachmentViewer.$attachmentPreview, dataURL); + } + }; + fileReader.readAsDataURL(file); + }, + + /** + * Handle the preview of files that can either be an image, video, audio or pdf element. + * @argument {DOM Element} targetElement where the preview should be appended. + * @argument {File Data} data of the file to be displayed. + */ + handleAttachmentPreview: function (targetElement, data) { + if (data) { + var mimeType = this.getMimeTypeFromDataURL(data); + + if (mimeType.match(/image\//i)) { + this.showImagePreview(targetElement, data); + } else if (mimeType.match(/video\//i)) { + this.showVideoPreview(targetElement, data, mimeType); + } else if (mimeType.match(/audio\//i)) { + this.showAudioPreview(targetElement, data, mimeType); + } else if (mimeType.match(/\/pdf/i)) { + this.showPDFPreview(targetElement, data); + } + //else { + //console.log("file but no image/video/audio/pdf"); + //} + } + }, + + /** + * Get Mime Type from a DataURL + * + * @param {type} dataURL + * @returns Mime Type from a dataURL as obtained for a file using the FileReader API https://developer.mozilla.org/en-US/docs/Web/API/FileReader#readAsDataURL() + */ + getMimeTypeFromDataURL: function (dataURL) { + return dataURL.slice(dataURL.indexOf('data:') + 5, dataURL.indexOf(';base64,')); + }, + + showImagePreview: function (targetElement, image) { + targetElement.html( + $(document.createElement('img')) + .attr('src', image) + .attr('class', 'img-thumbnail') + ); + targetElement.removeClass('hidden'); + }, + + showVideoPreview: function (targetElement, video, mimeType) { + var videoPlayer = $(document.createElement('video')) + .attr('controls', 'true') + .attr('autoplay', 'true') + .attr('loop', 'true') + .attr('class', 'img-thumbnail'); + + videoPlayer.append($(document.createElement('source')) + .attr('type', mimeType) + .attr('src', video)); + targetElement.html(videoPlayer); + targetElement.removeClass('hidden'); + }, + + showAudioPreview: function (targetElement, audio, mimeType) { + var audioPlayer = $(document.createElement('audio')) + .attr('controls', 'true') + .attr('autoplay', 'true'); + + audioPlayer.append($(document.createElement('source')) + .attr('type', mimeType) + .attr('src', audio)); + targetElement.html(audioPlayer); + targetElement.removeClass('hidden'); + }, + + showPDFPreview: function (targetElement, pdf) { + //PDFs are only displayed if the filesize is smaller than about 1MB (after base64 encoding). + //Bigger filesizes currently cause crashes in various browsers. + //See also: https://code.google.com/p/chromium/issues/detail?id=69227 + + //Firefox crashes with files that are about 1.5MB + //The performance with 1MB files is bareable + if (pdf.length < 1398488) { + + //Fallback for browsers, that don't support the vh unit + var clientHeight = $(window).height(); + + targetElement.html( + $(document.createElement('embed')) + .attr('src', pdf) + .attr('type', 'application/pdf') + .attr('class', 'pdfPreview') + .css('height', clientHeight) + ); + targetElement.removeClass('hidden'); + } else { + Alert.showError(I18n._('File too large, to display a preview. Please download the attachment.')); + } + }, + + /** + * Attaches the file attachment drag & drop handler to the page. + * @returns {undefined} + */ + addDragDropHandler: function () { + var fileInput = me.attachmentHelpers.$fileInput; + + if (typeof fileInput === 'undefined' || fileInput.length === 0) { + return; + } + + var ignoreDragDrop = function(event) { + event.stopPropagation(); + event.preventDefault(); + }; + + var drop = function(event) { + event.stopPropagation(); + event.preventDefault(); + + if (fileInput) { + var file = event.dataTransfer.files[0]; + //Clear the file input: + fileInput.wrap('
').closest('form').get(0).reset(); + fileInput.unwrap(); + //Only works in Chrome: + //fileInput[0].files = e.dataTransfer.files; + + me.attachmentHelpers.readFileData(file); + } + }; + + document.addEventListener("drop", drop, false); + document.addEventListener("dragenter", ignoreDragDrop, false); + document.addEventListener("dragover", ignoreDragDrop, false); + fileInput.on("change", function () { + me.attachmentHelpers.readFileData(); + }); + } + }; + return me; })(); @@ -1596,6 +1775,8 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { // show preview PasteViewer.setText($message.val()); + var attachmentData = Helper.attachmentHelpers.attachmentData || AttachmentViewer.$attachmentLink.attr('href'); + Helper.attachmentHelpers.handleAttachmentPreview(AttachmentViewer.$attachmentPreview, attachmentData); PasteViewer.run(); // finish @@ -1930,6 +2111,7 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { $plainText.addClass('hidden'); $prettyMessage.addClass('hidden'); $placeholder.addClass('hidden'); + AttachmentViewer.hideAttachmentPreview(); isDisplayed = false; } @@ -1981,9 +2163,9 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { var AttachmentViewer = (function (window, document) { var me = {}; - var $attachmentLink, - $attachmentPreview, - $attachment; + me.$attachmentLink; + me.$attachmentPreview; + me.$attachment; var attachmentHasPreview = false; @@ -1997,22 +2179,14 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { */ me.setAttachment = function(attachmentData, fileName) { - var imagePrefix = 'data:image/'; - - $attachmentLink.attr('href', attachmentData); + me.$attachmentLink.attr('href', attachmentData); if (typeof fileName !== 'undefined') { - $attachmentLink.attr('download', fileName); + me.$attachmentLink.attr('download', fileName); } - // if the attachment is an image, display it - if (attachmentData.substring(0, imagePrefix.length) === imagePrefix) { - $attachmentPreview.html( - $(document.createElement('img')) - .attr('src', attachmentData) - .attr('class', 'img-thumbnail') - ); - attachmentHasPreview = true; - } + Helper.attachmentHelpers.handleAttachmentPreview(AttachmentViewer.$attachmentPreview, attachmentData); + attachmentHasPreview = true; + } /** @@ -2023,10 +2197,10 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { */ me.showAttachment = function() { - $attachment.removeClass('hidden'); + me.$attachment.removeClass('hidden'); if (attachmentHasPreview) { - $attachmentPreview.removeClass('hidden'); + me.$attachmentPreview.removeClass('hidden'); } } @@ -2043,9 +2217,12 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { { me.hideAttachment(); me.hideAttachmentPreview(); - $attachmentLink.prop('href', ''); - $attachmentLink.prop('download', ''); - $attachmentPreview.html(''); + me.$attachmentLink.prop('href', ''); + me.$attachmentLink.prop('download', ''); + me.$attachmentPreview.html(''); + + Helper.attachmentHelpers.file = undefined; + Helper.attachmentHelpers.attachmentData = undefined; } /** @@ -2060,7 +2237,7 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { */ me.hideAttachment = function() { - $attachment.addClass('hidden'); + me.$attachment.addClass('hidden'); } /** @@ -2071,7 +2248,7 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { */ me.hideAttachmentPreview = function() { - $attachmentPreview.addClass('hidden'); + me.$attachmentPreview.addClass('hidden'); } /** @@ -2082,7 +2259,7 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { */ me.hasAttachment = function() { - var link = $attachmentLink.prop('href'); + var link = me.$attachmentLink.prop('href'); return (typeof link !== 'undefined' && link !== '') } @@ -2096,8 +2273,8 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { me.getAttachment = function() { return [ - $attachmentLink.prop('href'), - $attachmentLink.prop('download') + me.$attachmentLink.prop('href'), + me.$attachmentLink.prop('download') ]; } @@ -2114,10 +2291,10 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { me.moveAttachmentTo = function($element, label) { // move elemement to new place - $attachmentLink.appendTo($element); + me.$attachmentLink.appendTo($element); // update text - I18n._($attachmentLink, label, $attachmentLink.attr('download')); + I18n._(me.$attachmentLink, label, me.$attachmentLink.attr('download')); } /** @@ -2130,9 +2307,9 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { */ me.init = function() { - $attachment = $('#attachment'); - $attachmentLink = $('#attachment a'); - $attachmentPreview = $('#attachmentPreview'); + me.$attachment = $('#attachment'); + me.$attachmentLink = $('#attachment a'); + me.$attachmentPreview = $('#attachmentPreview'); } return me; @@ -3356,31 +3533,19 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { * @name PasteEncrypter.encryptAttachments * @private * @function - * @param {File|null|undefined} file - optional, falls back to cloned attachment * @param {function} callback - excuted when action is successful */ - function encryptAttachments(file, callback) { + function encryptAttachments(callback) { + var file = Helper.attachmentHelpers.attachmentData; + if (typeof file !== 'undefined' && file !== null) { - // check file reader requirements for upload - if (typeof FileReader === 'undefined') { - Alert.showError('Your browser does not support uploading encrypted files. Please use a newer browser.'); - // cancels process as it does not execute callback - return; - } + var fileName = Helper.attachmentHelpers.file.name; - var reader = new FileReader(); + Uploader.setData('attachment', file); + Uploader.setData('attachmentname', fileName); - // closure to capture the file information - reader.onload = function(event) { - Uploader.setData('attachment', event.target.result); - Uploader.setData('attachmentname', file.name); - - // run callback - return callback(); - } - - // actually read first file - reader.readAsDataURL(file); + // run callback + return callback(); } else if (AttachmentViewer.hasAttachment()) { // fall back to cloned part var attachment = AttachmentViewer.getAttachment(); @@ -3489,7 +3654,7 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { // get data var plainText = Editor.getText(), format = PasteViewer.getFormat(), - files = TopNav.getFileList(); + files = TopNav.getFileList() || Helper.attachmentHelpers.file || AttachmentViewer.hasAttachment(); // do not send if there is no data if (plainText.length === 0 && files === null) { @@ -3540,7 +3705,6 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { // encrypt attachments encryptAttachments( - files === null ? null : files[0], function () { // send data Uploader.run(); @@ -4001,6 +4165,7 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { TopNav.init(); UiHelper.init(); Uploader.init(); + Helper.attachmentHelpers.init(); // display an existing paste if (Model.hasCipherData()) { diff --git a/tpl/bootstrap.php b/tpl/bootstrap.php index c3d77808..4ab7ba59 100644 --- a/tpl/bootstrap.php +++ b/tpl/bootstrap.php @@ -69,7 +69,7 @@ if ($MARKDOWN): - + diff --git a/tpl/page.php b/tpl/page.php index ebc7e84a..22bf1401 100644 --- a/tpl/page.php +++ b/tpl/page.php @@ -47,7 +47,7 @@ if ($MARKDOWN): - + From 1a1369ff53162f07afc184e16d492739bf506385 Mon Sep 17 00:00:00 2001 From: thororm Date: Sun, 2 Apr 2017 19:11:49 +0200 Subject: [PATCH 0135/1638] scrutinizer issues --- js/privatebin.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/js/privatebin.js b/js/privatebin.js index bbb2fe76..04172860 100644 --- a/js/privatebin.js +++ b/js/privatebin.js @@ -2163,9 +2163,9 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { var AttachmentViewer = (function (window, document) { var me = {}; - me.$attachmentLink; - me.$attachmentPreview; - me.$attachment; + me.$attachmentLink = undefined; + me.$attachmentPreview = undefined; + me.$attachment = undefined; var attachmentHasPreview = false; From 662b48fccfec82ada0ebe41476ddae60e537b2c8 Mon Sep 17 00:00:00 2001 From: thororm Date: Sun, 2 Apr 2017 19:28:25 +0200 Subject: [PATCH 0136/1638] Hashes --- tpl/bootstrap.php | 2 +- tpl/page.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tpl/bootstrap.php b/tpl/bootstrap.php index 4ab7ba59..11ed7af6 100644 --- a/tpl/bootstrap.php +++ b/tpl/bootstrap.php @@ -69,7 +69,7 @@ if ($MARKDOWN): - + diff --git a/tpl/page.php b/tpl/page.php index 22bf1401..bee575b8 100644 --- a/tpl/page.php +++ b/tpl/page.php @@ -47,7 +47,7 @@ if ($MARKDOWN): - + From 2d4c75be8519ccad916f1acc0f43f37ecb716df0 Mon Sep 17 00:00:00 2001 From: El RIDO Date: Tue, 4 Apr 2017 07:43:41 +0200 Subject: [PATCH 0137/1638] added tests for entropy checks and key generation, added base64 experiment, showing we could replace Base64.js v2.1.9 with other options, but still need to find a way to handle v1.7 format and UTF16 to UTF8 conversion (btou / utob functions) --- js/test.js | 74 +++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 60 insertions(+), 14 deletions(-) diff --git a/js/test.js b/js/test.js index c355be82..3f035088 100644 --- a/js/test.js +++ b/js/test.js @@ -2,9 +2,6 @@ var jsc = require('jsverify'), jsdom = require('jsdom-global'), cleanup = jsdom(), - base64lib = require('./base64-2.1.9'), - rawdeflatelib = require('./rawdeflate-0.5'), - rawinflatelib = require('./rawinflate-0.3'), a2zString = ['a','b','c','d','e','f','g','h','i','j','k','l','m', 'n','o','p','q','r','s','t','u','v','w','x','y','z'], @@ -22,9 +19,9 @@ var jsc = require('jsverify'), global.$ = global.jQuery = require('./jquery-3.1.1'); global.sjcl = require('./sjcl-1.0.6'); -global.Base64 = base64lib.Base64; -global.RawDeflate = rawdeflatelib.RawDeflate; -global.RawDeflate.inflate = rawinflatelib.RawDeflate.inflate; +global.Base64 = require('./base64-2.1.9').Base64; +global.RawDeflate = require('./rawdeflate-0.5').RawDeflate; +global.RawDeflate.inflate = require('./rawinflate-0.3').RawDeflate.inflate; require('./privatebin'); // redirect console messages to log file @@ -441,7 +438,7 @@ describe('I18n', function () { describe('CryptTool', function () { describe('cipher & decipher', function () { - this.timeout(20000); + this.timeout(30000); it('can en- and decrypt any message', function () { jsc.check(jsc.forall( 'string', @@ -461,10 +458,12 @@ describe('CryptTool', function () { // The below static unit test is included to ensure deciphering of "classic" // SJCL based pastes still works - it('supports v1 ciphertext (SJCL)', function () { - // Of course you can easily decipher the following texts, if you like. - // Bonus points for finding their sources and hidden meanings. - var paste1 = $.PrivateBin.CryptTool.decipher( + it( + 'supports v1 ciphertext (SJCL)', + function () { + // Of course you can easily decipher the following texts, if you like. + // Bonus points for finding their sources and hidden meanings. + var paste1 = $.PrivateBin.CryptTool.decipher( '6t2qsmLyfXIokNCL+3/yl15rfTUBQvm5SOnFPvNE7Q8=', // -- "That's amazing. I've got the same combination on my luggage." Array.apply(0, Array(6)).map(function(_,b) { return b + 1; }).join(''), @@ -475,10 +474,57 @@ describe('CryptTool', function () { '', // no password '{"iv":"WA42mdxIVXUwBqZu7JYNiw==","v":1,"iter":10000,"ks":256,"ts":128,"mode":"gcm","adata":"","cipher":"aes","salt":"jN6CjbQMJCM=","ct":"kYYMo5DFG1+w0UHiYXT5pdV0IUuXxzOlslkW/c3DRCbGFROCVkAskHce7HoRczee1N9c5MhHjVMJUIZE02qIS8UyHdJ/GqcPVidTUcj9rnDNWsTXkjVv8jCwHS/cwmAjDTWpwp5ThECN+ov/wNp/NdtTj8Qj7f/T3rfZIOCWfwLH9s4Des35UNcUidfPTNQ1l0Gm0X+r98CCUSYZjQxkZc6hRZBLPQ8EaNVooUwd5eP4GiYlmSDNA0wOSA+5isPYxomVCt+kFf58VBlNhpfNi7BLYAUTPpXT4SfH5drR9+C7NTeZ+tTCYjbU94PzYItOpu8vgnB1/a6BAM5h3m9w+giUb0df4hgTWeZnZxLjo5BN8WV+kdTXMj3/Vv0gw0DQrDcCuX/cBAjpy3lQGwlAN1vXoOIyZJUjMpQRrOLdKvLB+zcmVNtGDbgnfP2IYBzk9NtodpUa27ne0T0ZpwOPlVwevsIVZO224WLa+iQmmHOWDFFpVDlS0t0fLfOk7Hcb2xFsTxiCIiyKMho/IME1Du3X4e6BVa3hobSSZv0rRtNgY1KcyYPrUPW2fxZ+oik3y9SgGvb7XpjVIta8DWlDWRfZ9kzoweWEYqz9IA8Xd373RefpyuWI25zlHoX3nwljzsZU6dC//h/Dt2DNr+IAvKO3+u23cWoB9kgcZJ2FJuqjLvVfCF+OWcig7zs2pTYJW6Rg6lqbBCxiUUlae6xJrjfv0pzD2VYCLY7v1bVTagppwKzNI3WaluCOrdDYUCxUSe56yd1oAoLPRVbYvomRboUO6cjQhEknERyvt45og2kORJOEJayHW+jZgR0Y0jM3Nk17ubpij2gHxNx9kiLDOiCGSV5mn9mV7qd3HHcOMSykiBgbyzjobi96LT2dIGLeDXTIdPOog8wyobO4jWq0GGs0vBB8oSYXhHvixZLcSjX2KQuHmEoWzmJcr3DavdoXZmAurGWLKjzEdJc5dSD/eNr99gjHX7wphJ6umKMM+fn6PcbYJkhDh2GlJL5COXjXfm/5aj/vuyaRRWZMZtmnYpGAtAPg7AUG"}' ); - if (!paste1.includes('securely packed in iron') || !paste2.includes('Sol is right')) { - throw Error('v1 (SJCL based) pastes could not be deciphered'); + if (!paste1.includes('securely packed in iron') || !paste2.includes('Sol is right')) { + throw Error('v1 (SJCL based) pastes could not be deciphered'); + } } - }); + ); + }); + + describe('isEntropyReady & addEntropySeedListener', function () { + it( + 'lets us know that enough entropy is collected or make us wait for it', + function(done) { + if ($.PrivateBin.CryptTool.isEntropyReady()) { + done(); + } else { + $.PrivateBin.CryptTool.addEntropySeedListener(function() { + done(); + }); + } + } + ); + }); + + describe('getSymmetricKey', function () { + var keys = []; + + // the parameter is used to ensure the test is run more then one time + jsc.property( + 'returns random, non-empty keys', + 'nat', + function(n) { + var key = $.PrivateBin.CryptTool.getSymmetricKey(), + result = (key !== '' && keys.indexOf(key) === -1); + keys.push(key); + return result; + } + ); + }); + + describe('Base64.js vs SJCL.js vs abab.js', function () { + var btoa = require('abab').btoa; + + jsc.property( + 'these all return the same base64 string', + 'string', + function(string) { + var base64 = Base64.toBase64(string), + sjcl = global.sjcl.codec.base64.fromBits(global.sjcl.codec.utf8String.toBits(string)), + abab = btoa(Base64.utob(string)); + return base64 === sjcl && sjcl === abab; + } + ); }); }); From 8f6c1ee079c973b3f9c058fdd79cc80d6e64c574 Mon Sep 17 00:00:00 2001 From: El RIDO Date: Wed, 5 Apr 2017 06:46:21 +0200 Subject: [PATCH 0138/1638] added a check for the ZeroBin paste format (uses Base64.js v1.7) --- js/test.js | 39 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 37 insertions(+), 2 deletions(-) diff --git a/js/test.js b/js/test.js index 3f035088..88dcf58e 100644 --- a/js/test.js +++ b/js/test.js @@ -456,10 +456,10 @@ describe('CryptTool', function () { {tests: 5, quiet: true}); }); - // The below static unit test is included to ensure deciphering of "classic" + // The below static unit tests are included to ensure deciphering of "classic" // SJCL based pastes still works it( - 'supports v1 ciphertext (SJCL)', + 'supports PrivateBin v1 ciphertext (SJCL & Base64 2.1.9)', function () { // Of course you can easily decipher the following texts, if you like. // Bonus points for finding their sources and hidden meanings. @@ -474,6 +474,40 @@ describe('CryptTool', function () { '', // no password '{"iv":"WA42mdxIVXUwBqZu7JYNiw==","v":1,"iter":10000,"ks":256,"ts":128,"mode":"gcm","adata":"","cipher":"aes","salt":"jN6CjbQMJCM=","ct":"kYYMo5DFG1+w0UHiYXT5pdV0IUuXxzOlslkW/c3DRCbGFROCVkAskHce7HoRczee1N9c5MhHjVMJUIZE02qIS8UyHdJ/GqcPVidTUcj9rnDNWsTXkjVv8jCwHS/cwmAjDTWpwp5ThECN+ov/wNp/NdtTj8Qj7f/T3rfZIOCWfwLH9s4Des35UNcUidfPTNQ1l0Gm0X+r98CCUSYZjQxkZc6hRZBLPQ8EaNVooUwd5eP4GiYlmSDNA0wOSA+5isPYxomVCt+kFf58VBlNhpfNi7BLYAUTPpXT4SfH5drR9+C7NTeZ+tTCYjbU94PzYItOpu8vgnB1/a6BAM5h3m9w+giUb0df4hgTWeZnZxLjo5BN8WV+kdTXMj3/Vv0gw0DQrDcCuX/cBAjpy3lQGwlAN1vXoOIyZJUjMpQRrOLdKvLB+zcmVNtGDbgnfP2IYBzk9NtodpUa27ne0T0ZpwOPlVwevsIVZO224WLa+iQmmHOWDFFpVDlS0t0fLfOk7Hcb2xFsTxiCIiyKMho/IME1Du3X4e6BVa3hobSSZv0rRtNgY1KcyYPrUPW2fxZ+oik3y9SgGvb7XpjVIta8DWlDWRfZ9kzoweWEYqz9IA8Xd373RefpyuWI25zlHoX3nwljzsZU6dC//h/Dt2DNr+IAvKO3+u23cWoB9kgcZJ2FJuqjLvVfCF+OWcig7zs2pTYJW6Rg6lqbBCxiUUlae6xJrjfv0pzD2VYCLY7v1bVTagppwKzNI3WaluCOrdDYUCxUSe56yd1oAoLPRVbYvomRboUO6cjQhEknERyvt45og2kORJOEJayHW+jZgR0Y0jM3Nk17ubpij2gHxNx9kiLDOiCGSV5mn9mV7qd3HHcOMSykiBgbyzjobi96LT2dIGLeDXTIdPOog8wyobO4jWq0GGs0vBB8oSYXhHvixZLcSjX2KQuHmEoWzmJcr3DavdoXZmAurGWLKjzEdJc5dSD/eNr99gjHX7wphJ6umKMM+fn6PcbYJkhDh2GlJL5COXjXfm/5aj/vuyaRRWZMZtmnYpGAtAPg7AUG"}' ); + + if (!paste1.includes('securely packed in iron') || !paste2.includes('Sol is right')) { + throw Error('v1 (SJCL based) pastes could not be deciphered'); + } + } + ); + + it( + 'supports ZeroBin ciphertext (SJCL & Base64 1.7)', + function () { + var newBase64 = global.Base64; + global.Base64 = require('./base64-1.7').Base64; + jsdom(); + delete require.cache[require.resolve('./privatebin')]; + require('./privatebin'); + + // Of course you can easily decipher the following texts, if you like. + // Bonus points for finding their sources and hidden meanings. + var paste1 = $.PrivateBin.CryptTool.decipher( + '6t2qsmLyfXIokNCL+3/yl15rfTUBQvm5SOnFPvNE7Q8=', + // -- "That's amazing. I've got the same combination on my luggage." + Array.apply(0, Array(6)).map(function(_,b) { return b + 1; }).join(''), + '{"iv":"aTnR2qBL1CAmLX8FdWe3VA==","v":1,"iter":10000,"ks":256,"ts":128,"mode":"gcm","adata":"","cipher":"aes","salt":"u0lQvePq6L0=","ct":"A3nBTvICZtYy6xqbIJE0c8Veored5lMJUGgGUm4581wjrPFlU0Q0tUZSf+RUUoZj2jqDa4kiyyZ5YNMe30hNMV0oVSalNhRgD9svVMnPuF162IbyhVCwr7ULjT981CHxVlGNqGqmIU6L/XixgdArxAA8x1GCrfAkBWWGeq8Qw5vJPG/RCHpwR4Wy3azrluqeyERBzmaOQjO/kM35TiI6IrLYFyYyL7upYlxAaxS0XBMZvN8QU8Lnerwvh5JVC6OkkKrhogajTJIKozCF79yI78c50LUh7tTuI3Yoh7+fXxhoODvQdYFmoiUlrutN7Y5ZMRdITvVu8fTYtX9c7Fiufmcq5icEimiHp2g1bvfpOaGOsFT+XNFgC9215jcp5mpBdN852xs7bUtw+nDrf+LsDEX6iRpRZ+PYgLDN5xQT1ByEtYbeP+tO38pnx72oZdIB3cj8UkOxnxdNiZM5YB5egn4jUj1fHot1I69WoTiUJipZ5PIATv7ScymRB+AYzjxjurQ9lVfX9QtAbEH2dhdmoUo3IDRSXpWNCe9RC1aUIyWfZO7oI7FEohNscHNTLEcT+wFnFUPByLlXmjNZ7FKeNpvUm3jTY4t4sbZH8o2dUl624PAw1INcJ6FKqWGWwoFT2j1MYC+YV/LkLTdjuWfayvwLMh27G/FfKCRbW36vqinegqpPDylsx9+3oFkEw3y5Z8+44oN91rE/4Md7JhPJeRVlFC9TNCj4dA+EVhbbQqscvSnIH2uHkMw7mNNo7xba/YT9KoPDaniqnYqb+q2pX1WNWE7dLS2wfroMAS3kh8P22DAV37AeiNoD2PcI6ZcHbRdPa+XRrRcJhSPPW7UQ0z4OvBfjdu/w390QxAxSxvZewoh49fKKB6hTsRnZb4tpHkjlww=="}' + ), + paste2 = $.PrivateBin.CryptTool.decipher( + 's9pmKZKOBN7EVvHpTA8jjLFH3Xlz/0l8lB4+ONPACrM=', + '', // no password + '{"iv":"Z7lAZQbkrqGMvruxoSm6Pw==","v":1,"iter":10000,"ks":256,"ts":128,"mode":"gcm","adata":"","cipher":"aes","salt":"jN6CjbQMJCM=","ct":"PuOPWB3i2FPcreSrLYeQf84LdE8RHjsc+MGtiOr4b7doNyWKYtkNorbRadxaPnEee2/Utrp1MIIfY5juJSy8RGwEPX5ciWcYe6EzsXWznsnvhmpKNj9B7eIIrfSbxfy8E2e/g7xav1nive+ljToka3WT1DZ8ILQd/NbnJeHWaoSEOfvz8+d8QJPb1tNZvs7zEY95DumQwbyOsIMKAvcZHJ9OJNpujXzdMyt6DpcFcqlldWBZ/8q5rAUTw0HNx/rCgbhAxRYfNoTLIcMM4L0cXbPSgCjwf5FuO3EdE13mgEDhcClW79m0QvcnIh8xgzYoxLbp0+AwvC/MbZM8savN/0ieWr2EKkZ04ggiOIEyvfCUuNprQBYO+y8kKduNEN6by0Yf4LRCPfmwN+GezDLuzTnZIMhPbGqUAdgV6ExqK2ULEEIrQEMoOuQIxfoMhqLlzG79vXGt2O+BY+4IiYfvmuRLks4UXfyHqxPXTJg48IYbGs0j4TtJPUgp3523EyYLwEGyVTAuWhYAmVIwd/hoV7d7tmfcF73w9dufDFI3LNca2KxzBnWNPYvIZKBwWbq8ncxkb191dP6mjEi7NnhqVk5A6vIBbu4AC5PZf76l6yep4xsoy/QtdDxCMocCXeAML9MQ9uPQbuspOKrBvMfN5igA1kBqasnxI472KBNXsdZnaDddSVUuvhTcETM="}' + ); + + global.Base64 = newBase64; + jsdom(); + delete require.cache[require.resolve('./privatebin')]; + require('./privatebin'); if (!paste1.includes('securely packed in iron') || !paste2.includes('Sol is right')) { throw Error('v1 (SJCL based) pastes could not be deciphered'); } @@ -532,6 +566,7 @@ describe('Model', function () { describe('getPasteId', function () { before(function () { $.PrivateBin.Model.reset(); + cleanup(); }); jsc.property( From 41701bbfe4b1e89eb56967905552099a296624b0 Mon Sep 17 00:00:00 2001 From: El RIDO Date: Wed, 5 Apr 2017 06:55:20 +0200 Subject: [PATCH 0139/1638] trying to fix unit test execution in Travis --- js/test.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/js/test.js b/js/test.js index 88dcf58e..c6e5c7a6 100644 --- a/js/test.js +++ b/js/test.js @@ -547,15 +547,13 @@ describe('CryptTool', function () { }); describe('Base64.js vs SJCL.js vs abab.js', function () { - var btoa = require('abab').btoa; - jsc.property( 'these all return the same base64 string', 'string', function(string) { var base64 = Base64.toBase64(string), sjcl = global.sjcl.codec.base64.fromBits(global.sjcl.codec.utf8String.toBits(string)), - abab = btoa(Base64.utob(string)); + abab = window.btoa(Base64.utob(string)); return base64 === sjcl && sjcl === abab; } ); From ab2e789aee069601b0a5c4f2fcda2aa158b3e280 Mon Sep 17 00:00:00 2001 From: rugk Date: Tue, 11 Apr 2017 12:45:51 +0200 Subject: [PATCH 0140/1638] Add JS refactor to credits --- CREDITS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CREDITS.md b/CREDITS.md index 1c7ec3cc..cc63cde8 100644 --- a/CREDITS.md +++ b/CREDITS.md @@ -3,7 +3,7 @@ ## Active contributors Simon Rupf - current developer and maintainer -rugk - security review, doc improvment & various other stuff +rugk - security review, doc improvment, JS refactoring & various other stuff ## Past contributions From 183ebe518b02c52b22d2ad5f472dbfcb862c83f2 Mon Sep 17 00:00:00 2001 From: rugk Date: Tue, 11 Apr 2017 16:34:13 +0200 Subject: [PATCH 0141/1638] Force JSON request for getting paste data --- .editorconfig | 3 - INSTALL.md | 2 +- js/privatebin.js | 208 ++++++++++++++++++++++++++++++-------------- lib/Model/Paste.php | 2 +- lib/PrivateBin.php | 16 +++- tpl/.editorconfig | 2 - tpl/bootstrap.php | 3 +- tpl/page.php | 3 +- 8 files changed, 159 insertions(+), 80 deletions(-) diff --git a/.editorconfig b/.editorconfig index 2c9ddc3c..86252fa0 100644 --- a/.editorconfig +++ b/.editorconfig @@ -11,7 +11,6 @@ insert_final_newline = true [*.css] indent_style = tab -indent_size = 4 [*.js] indent_style = space @@ -23,7 +22,6 @@ indent_size = 4 [*.jsonld] indent_style = tab -indent_size = 4 [*.php] indent_style = space @@ -31,7 +29,6 @@ indent_size = 4 [*.{htm,html}] indent_style = tab -indent_size = 4 [*.{md,markdown}] indent_style = space diff --git a/INSTALL.md b/INSTALL.md index b627bc9e..a4bb08cf 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -64,7 +64,7 @@ process (see also > #### PATH Example > Your PrivateBin installation lives in a subfolder called "paste" inside of > your document root. The URL looks like this: -> http://example.com/paste/ +> https://example.com/paste/ > > The full path of PrivateBin on your webserver is: > /home/example.com/htdocs/paste diff --git a/js/privatebin.js b/js/privatebin.js index 8cf76831..2d7ead1f 100644 --- a/js/privatebin.js +++ b/js/privatebin.js @@ -42,11 +42,22 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { var Helper = (function () { var me = {}; + /** + * list of UserAgents (parts) known to belong to a bot + * + * @private + * @enum {Object} + * @readonly + */ + var BadBotUA = [ + 'Bot', + 'bot' + ]; + /** * character to HTML entity lookup table * * @see {@link https://github.com/janl/mustache.js/blob/master/mustache.js#L60} - * @name Helper.entityMap * @private * @enum {Object} * @readonly @@ -160,7 +171,7 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { * URLs to handle: *
          *     magnet:?xt.1=urn:sha1:YNCKHTQCWBTRNJIV4WNAE52SJUQCZO5C&xt.2=urn:sha1:TXGCZQTH26NL6OUQAJJPFALHG2LTGBC7
-         *     http://example.com:8800/zero/?6f09182b8ea51997#WtLEUO5Epj9UHAV9JFs+6pUQZp13TuspAUjnF+iM+dM=
+         *     https://example.com:8800/zero/?6f09182b8ea51997#WtLEUO5Epj9UHAV9JFs+6pUQZp13TuspAUjnF+iM+dM=
          *     http://user:example.com@localhost:8800/zero/?6f09182b8ea51997#WtLEUO5Epj9UHAV9JFs+6pUQZp13TuspAUjnF+iM+dM=
          * 
* @@ -251,7 +262,7 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { /** * get the current location (without search or hash part of the URL), - * eg. http://example.com/path/?aaaa#bbbb --> http://example.com/path/ + * eg. https://example.com/path/?aaaa#bbbb --> https://example.com/path/ * * @name Helper.baseUri * @function @@ -295,6 +306,32 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { baseUri = null; } + /** + * checks whether this is a bot we dislike + * + * @name Helper.isBadBot + * @function + * @return {bool} + */ + me.isBadBot = function() { + /* + if ($.inArray(navigator.userAgent, BadBotUA) >= 0) { + return true; + } + */ + + // check whether a bot user agent part can be found in the current + // user agent + var arrayLength = BadBotUA.length; + for (var i = 0; i < arrayLength; i++) { + if (navigator.userAgent.indexOf(BadBotUA) >= 0) { + return true; + } + } + + return false; + } + return me; })(); @@ -689,7 +726,7 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { var Model = (function () { var me = {}; - var $cipherData, + var pasteData = null, $templates; var id = null, symmetricKey = null; @@ -721,32 +758,53 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { } /** - * check if cipher data was supplied + * returns the paste data (inlduing the cipher data) * - * @name Model.getCipherData - * @function - * @return boolean - */ - me.hasCipherData = function() - { - return (me.getCipherData().length > 0); - } - - /** - * returns the cipher data - * - * @name Model.getCipherData + * @name Model.getPasteData * @function + * @param {function} callback (optional) Called when data is available + * @param {function} useCache (optional) Whether to use the cache or + * force a data reload. Default: true * @return string */ - me.getCipherData = function() + me.getPasteData = function(callback, useCache) { - return $cipherData.text(); + // use cache if possible/allowed + if (useCache !== false && pasteData !== null) { + //execute callback + if (typeof callback === 'function') { + return callback(pasteData); + } + + // alternatively just using inline + return pasteData; + } + + // reload data + Uploader.prepare(); + Uploader.setUrl(Helper.baseUri() + '?' + me.getPasteId()); + + Uploader.setFailure(function (status, data) { + // revert loading status… + Alert.hideLoading(); + TopNav.showViewButtons(); + + // show error message + Alert.showError(Uploader.parseUploadError(status, data, 'getting paste data')); + }) + Uploader.setSuccess(function (status, data) { + pasteData = data; + + if (typeof callback === 'function') { + return callback(data); + } + }) + Uploader.run(); } /** * get the pastes unique identifier from the URL, - * eg. http://example.com/path/?c05354954c49a487#dfdsdgdgdfgdf returns c05354954c49a487 + * eg. https://example.com/path/?c05354954c49a487#dfdsdgdgdfgdf returns c05354954c49a487 * * @name Model.getPasteId * @function @@ -819,7 +877,7 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { */ me.reset = function() { - $cipherData = $templates = id = symmetricKey = null; + pasteData = $templates = id = symmetricKey = null; } /** @@ -832,7 +890,6 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { */ me.init = function() { - $cipherData = $('#cipherdata'); $templates = $('#templates'); } @@ -1333,8 +1390,8 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { if (pasteMetaData.burnafterreading) { // display paste "for your eyes only" if it is deleted - // actually remove paste, before we claim it is deleted - Controller.removePaste(Model.getPasteId(), 'burnafterreading'); + // the paste has been deleted when the JSOn with the ciohertext + // has been downloaded Alert.showRemaining("FOR YOUR EYES ONLY. Don't close this window, this message can't be displayed again."); $remainingTime.addClass('foryoureyesonly'); @@ -1462,7 +1519,7 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { } /** - * getthe cached password + * get the cached password * * If you do not get a password with this function * (returns an empty string), use requestPassword. @@ -3572,7 +3629,7 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { var me = {}; /** - * decrypt data or prompts for password in cvase of failure + * decrypt data or prompts for password in case of failure * * @name PasteDecrypter.decryptOrPromptPassword * @private @@ -3590,18 +3647,23 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { // if it fails, request password if (plaindata.length === 0 && password.length === 0) { - // try to get cached password first - password = Prompt.getPassword(); + // show prompt + Prompt.requestPassword(); - // if password is there, re-try - if (password.length === 0) { - password = Prompt.requestPassword(); + // if password is there instantly (legacy method), re-try encryption + if (Prompt.getPassword().length !== 0) { + // recursive + // note: an infinite loop is prevented as the previous if + // clause checks whether a password is already set and ignores + // errors when a password has been passed + return decryptOrPromptPassword(key, password, cipherdata); } - // recursive - // note: an infinite loop is prevented as the previous if - // clause checks whether a password is already set and ignores - // errors when a password has been passed - return decryptOrPromptPassword.apply(key, password, cipherdata); + + // if password could not be received yet, the new modal is used, + // which uses asyncronous event-driven methods to get the password. + // Thus, we cannot do anything yet, we need to wait for the user + // input. + return false; } // if all tries failed, we can only return an error @@ -3615,7 +3677,7 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { /** * decrypt the actual paste text * - * @name PasteDecrypter.decryptOrPromptPassword + * @name PasteDecrypter.decryptPaste * @private * @function * @param {object} paste - paste data in object form @@ -3627,7 +3689,7 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { */ function decryptPaste(paste, key, password, ignoreError) { - var plaintext + var plaintext; if (ignoreError === true) { plaintext = CryptTool.decipher(key, password, paste.data); } else { @@ -3738,7 +3800,9 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { Alert.showLoading('Decrypting paste…', 0, 'cloud-download'); // @TODO icon maybe rotation-lock, but needs full Glyphicons if (typeof paste === 'undefined') { - paste = $.parseJSON(Model.getCipherData()); + // get cipher data and wait until it is available + Model.getPasteData(me.run); + return; } var key = Model.getPasteKey(), @@ -3761,10 +3825,11 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { // ignore empty paste, as this is allowed when pasting attachments decryptPaste(paste, key, password, true); } else { - decryptPaste(paste, key, password); + if (decryptPaste(paste, key, password) === false) { + return false; + } } - // shows the remaining time (until) deletion PasteStatus.showRemainingTime(paste.meta); @@ -3844,6 +3909,18 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { Alert.hideLoading(); } + /** + * shows how we much we love bots that execute JS ;) + * + * @name Controller.showBadBotMessage + * @function + */ + me.showBadBotMessage = function() + { + TopNav.hideAllButtons(); + Alert.showError('I love you too, bot…'); + } + /** * shows the loaded paste * @@ -3853,7 +3930,6 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { me.showPaste = function() { try { - Model.getPasteId(); Model.getPasteKey(); } catch (err) { console.error(err); @@ -3882,26 +3958,17 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { // save window position to restore it later var orgPosition = $(window).scrollTop(); - Uploader.prepare(); - Uploader.setUrl(Helper.baseUri() + '?' + Model.getPasteId()); - - Uploader.setFailure(function (status, data) { - // revert loading status… - Alert.hideLoading(); - TopNav.showViewButtons(); - - // show error message - Alert.showError(Uploader.parseUploadError(status, data, 'refresh display')); - }) - Uploader.setSuccess(function (status, data) { - PasteDecrypter.run(data); - + Model.getPasteData(function (data) { // restore position window.scrollTo(0, orgPosition); + PasteDecrypter.run(data); + + // NOTE: could create problems as callback may be called + // asyncronously if PasteDecrypter e.g. needs to wait for a + // password being entered callback(); - }) - Uploader.run(); + }, false); // this false is important as it circumvents the cache } /** @@ -3959,6 +4026,7 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { * @function * @param {string} pasteId * @param {string} deleteToken + * @deprecated not used anymore, de we still need it? */ me.removePaste = function(pasteId, deleteToken) { // unfortunately many web servers don't support DELETE (and PUT) out of the box @@ -3968,7 +4036,7 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { Uploader.setUnencryptedData('deletetoken', deleteToken); Uploader.setFailure(function () { - Controller.showError(I18n._('Could not delete the paste, it was not stored in burn after reading mode.')); + Alert.showError(I18n._('Could not delete the paste, it was not stored in burn after reading mode.')); }) Uploader.run(); } @@ -4000,13 +4068,23 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) { UiHelper.init(); Uploader.init(); - // display an existing paste - if (Model.hasCipherData()) { - return me.showPaste(); + // check whether existing paste needs to be shown + try { + Model.getPasteId(); + } catch (e) { + // otherwise create a new paste + return me.newPaste(); } - // otherwise create a new paste - me.newPaste(); + // prevent bots from viewing a paste and potentially deleting data + // when burn-after-reading is set + // see https://github.com/elrido/ZeroBin/issues/11 + if (Helper.isBadBot()) { + return me.showBadBotMessage(); + } + + //display an existing paste + return me.showPaste(); } return me; diff --git a/lib/Model/Paste.php b/lib/Model/Paste.php index 8f171fe1..938d1840 100644 --- a/lib/Model/Paste.php +++ b/lib/Model/Paste.php @@ -158,7 +158,7 @@ class Paste extends AbstractModel * * The token is the hmac of the pastes ID signed with the server salt. * The paste can be deleted by calling: - * http://example.com/privatebin/?pasteid=&deletetoken= + * https://example.com/privatebin/?pasteid=&deletetoken= * * @access public * @return string diff --git a/lib/PrivateBin.php b/lib/PrivateBin.php index c817445c..30283cdc 100644 --- a/lib/PrivateBin.php +++ b/lib/PrivateBin.php @@ -147,7 +147,10 @@ class PrivateBin ); break; case 'read': - $this->_read($this->_request->getParam('pasteid')); + // reading paste is disallowed in HTML display + if ($this->_request->isJsonApiCall()) { + $this->_read($this->_request->getParam('pasteid')); + } break; case 'jsonld': $this->_jsonld($this->_request->getParam('jsonld')); @@ -328,10 +331,10 @@ class PrivateBin // deleted if it has already expired $burnafterreading = $paste->isBurnafterreading(); if ( - ($burnafterreading && $deletetoken == 'burnafterreading') || - Filter::slowEquals($deletetoken, $paste->getDeleteToken()) + ($burnafterreading && $deletetoken == 'burnafterreading') || // either we burn-after it has been read //@TODO: not needed anymore now? + Filter::slowEquals($deletetoken, $paste->getDeleteToken()) // or we manually delete it with this secret token ) { - // Paste exists and deletion token is valid: Delete the paste. + // Paste exists and deletion token (if required) is valid: Delete the paste. $paste->delete(); $this->_status = 'Paste was properly deleted.'; } else { @@ -373,6 +376,11 @@ class PrivateBin unset($data->meta->salt); } $this->_data = json_encode($data); + + // If the paste was meant to be read only once, delete it. + if ($paste->isBurnafterreading()) { + $paste->delete(); + } } else { $this->_error = self::GENERIC_ERROR; } diff --git a/tpl/.editorconfig b/tpl/.editorconfig index 30c7ad2f..9159bf49 100644 --- a/tpl/.editorconfig +++ b/tpl/.editorconfig @@ -5,5 +5,3 @@ root = false # special format for PHP templates [*.php] indent_style = tab -indent_size = 4 - diff --git a/tpl/bootstrap.php b/tpl/bootstrap.php index 6fc01bd1..6d1214eb 100644 --- a/tpl/bootstrap.php +++ b/tpl/bootstrap.php @@ -69,7 +69,7 @@ if ($MARKDOWN): - + @@ -478,7 +478,6 @@ endif; -