Merge branch 'webcrypto'
This commit is contained in:
commit
87c7719513
|
@ -1,3 +1,6 @@
|
||||||
|
parserOptions:
|
||||||
|
ecmaVersion: 2017
|
||||||
|
|
||||||
ecmaFeatures:
|
ecmaFeatures:
|
||||||
modules: true
|
modules: true
|
||||||
jsx: true
|
jsx: true
|
||||||
|
@ -10,7 +13,6 @@ env:
|
||||||
node: true
|
node: true
|
||||||
|
|
||||||
globals:
|
globals:
|
||||||
sjcl: false
|
|
||||||
DOMPurify: false
|
DOMPurify: false
|
||||||
after: true
|
after: true
|
||||||
before: true
|
before: true
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
doc/ export-ignore
|
doc/ export-ignore
|
||||||
tst/ export-ignore
|
tst/ export-ignore
|
||||||
js/.istanbul.yml export-ignore
|
js/.istanbul.yml export-ignore
|
||||||
|
js/.nycrc.yml export-ignore
|
||||||
|
js/common.js export-ignore
|
||||||
js/test/ export-ignore
|
js/test/ export-ignore
|
||||||
.codeclimate.yml export-ignore
|
.codeclimate.yml export-ignore
|
||||||
.csslintrc export-ignore
|
.csslintrc export-ignore
|
||||||
.dockerignore export-ignore
|
|
||||||
.editorconfig export-ignore
|
.editorconfig export-ignore
|
||||||
.eslintignore export-ignore
|
.eslintignore export-ignore
|
||||||
.eslintrc export-ignore
|
.eslintrc export-ignore
|
||||||
|
@ -16,7 +17,4 @@ js/test/ export-ignore
|
||||||
.php_cs export-ignore
|
.php_cs export-ignore
|
||||||
.styleci.yml export-ignore
|
.styleci.yml export-ignore
|
||||||
.travis.yml export-ignore
|
.travis.yml export-ignore
|
||||||
Dockerfile export-ignore
|
|
||||||
docker-compose.yml export-ignore
|
|
||||||
docker/ export-ignore
|
|
||||||
composer.json export-ignore
|
composer.json export-ignore
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
"bitwise": true,
|
"bitwise": true,
|
||||||
"curly": true,
|
"curly": true,
|
||||||
"eqeqeq": true,
|
"eqeqeq": true,
|
||||||
"esversion": 5,
|
"esversion": 6,
|
||||||
"forin": true,
|
"forin": true,
|
||||||
"freeze": true,
|
"freeze": true,
|
||||||
"futurehostile": true,
|
"futurehostile": true,
|
||||||
|
@ -39,7 +39,6 @@
|
||||||
"window": true
|
"window": true
|
||||||
},
|
},
|
||||||
"globals": {
|
"globals": {
|
||||||
"sjcl": true,
|
|
||||||
"DOMPurify": true,
|
"DOMPurify": true,
|
||||||
"kjua": true
|
"kjua": true
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,16 +7,17 @@ php:
|
||||||
- '7.0'
|
- '7.0'
|
||||||
- '7.1'
|
- '7.1'
|
||||||
- '7.2'
|
- '7.2'
|
||||||
|
- '7.3'
|
||||||
|
|
||||||
# as this is a php project, node.js v4 (for JS unit testing) isn't installed
|
# as this is a php project, node.js (for JS unit testing) isn't installed
|
||||||
install:
|
install:
|
||||||
- if [ ! -d "$HOME/.nvm" ]; then mkdir -p $HOME/.nvm && curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.8/install.sh | NVM_METHOD=script bash; fi
|
- if [ ! -d "$HOME/.nvm" ]; then mkdir -p $HOME/.nvm && curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.34.0/install.sh | NVM_METHOD=script bash; fi
|
||||||
- source ~/.nvm/nvm.sh && nvm install 10
|
- source ~/.nvm/nvm.sh && nvm install --lts
|
||||||
|
|
||||||
before_script:
|
before_script:
|
||||||
- composer install -n
|
- composer install -n
|
||||||
- npm install -g mocha
|
- npm install -g mocha
|
||||||
- cd js && npm install jsverify jsdom@9 jsdom-global@2 mime-types
|
- cd js && npm install
|
||||||
|
|
||||||
script:
|
script:
|
||||||
- mocha
|
- mocha
|
||||||
|
|
66
LICENSE.md
66
LICENSE.md
|
@ -2,15 +2,15 @@
|
||||||
|
|
||||||
PrivateBin consists of PHP and JS code which was originally written by Sébastien
|
PrivateBin consists of PHP and JS code which was originally written by Sébastien
|
||||||
Sauvage in 2012 and falls unter the Zlib/libpng license. Also included are
|
Sauvage in 2012 and falls unter the Zlib/libpng license. Also included are
|
||||||
libraries that fall under the GPLv2 (SJCL, rawinflate, rawdeflate), BSD
|
libraries that fall under the GPLv2 (rawinflate), BSD 3-clause (Showdown), MIT
|
||||||
2-clause (SJCL), BSD 3-clause (base64.js version 2.1.9, Showdown), MIT
|
|
||||||
(base64.js version 1.7, Bootstrap, Identicon, random_compat, composer, kjua,
|
(base64.js version 1.7, Bootstrap, Identicon, random_compat, composer, kjua,
|
||||||
base-x), Apache (prettify.js) and CC-BY (favicon, icon, logo) licenses. All of
|
base-x), Apache (prettify.js) and CC-BY (favicon, icon, logo) licenses. All of
|
||||||
these license terms can be found here below:
|
these license terms can be found here below:
|
||||||
|
|
||||||
## Zlib/libpng license for PrivateBin
|
## Zlib/libpng license for PrivateBin and zlib
|
||||||
|
|
||||||
Copyright © 2012 Sébastien Sauvage
|
Copyright © 2012 Sébastien Sauvage
|
||||||
|
Copyright © 1995-2017 Jean-loup Gailly and Mark Adler
|
||||||
|
|
||||||
This software is provided 'as-is', without any express or implied warranty. In
|
This software is provided 'as-is', without any express or implied warranty. In
|
||||||
no event will the authors be held liable for any damages arising from the use
|
no event will the authors be held liable for any damages arising from the use
|
||||||
|
@ -30,7 +30,7 @@ the following restrictions:
|
||||||
|
|
||||||
3. This notice may not be removed or altered from any source distribution.
|
3. This notice may not be removed or altered from any source distribution.
|
||||||
|
|
||||||
## GNU General Public License, version 2.0, for SJCL, rawdeflate and rawinflate
|
## GNU General Public License, version 2.0, for rawinflate
|
||||||
|
|
||||||
_Version 2, June 1991_
|
_Version 2, June 1991_
|
||||||
_Copyright © 1989, 1991 Free Software Foundation, Inc.,_
|
_Copyright © 1989, 1991 Free Software Foundation, Inc.,_
|
||||||
|
@ -307,31 +307,6 @@ POSSIBILITY OF SUCH DAMAGES.
|
||||||
|
|
||||||
END OF TERMS AND CONDITIONS
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
## BSD 2-Clause License for SJCL
|
|
||||||
|
|
||||||
_Copyright © 2009-2015, Emily Stark, Mike Hamburg and Dan Boneh at Stanford University._
|
|
||||||
_All rights reserved._
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
|
||||||
modification, are permitted provided that the following conditions are met:
|
|
||||||
|
|
||||||
1. Redistributions of source code must retain the above copyright notice, this
|
|
||||||
list of conditions and the following disclaimer.
|
|
||||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer in the documentation
|
|
||||||
and/or other materials provided with the distribution.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND
|
|
||||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
||||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
||||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
|
||||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
||||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
||||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
||||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
||||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
|
|
||||||
## BSD 3-Clause License for Showdown
|
## BSD 3-Clause License for Showdown
|
||||||
|
|
||||||
Showdown Copyright © 2007, John Fraser
|
Showdown Copyright © 2007, John Fraser
|
||||||
|
@ -367,38 +342,7 @@ any theory of liability, whether in contract, strict liability, or tort
|
||||||
(including negligence or otherwise) arising in any way out of the use of this
|
(including negligence or otherwise) arising in any way out of the use of this
|
||||||
software, even if advised of the possibility of such damage.
|
software, even if advised of the possibility of such damage.
|
||||||
|
|
||||||
## BSD 3-Clause License for base64.js version 2.1.9
|
## MIT License for base64.js version 1.7, Bootstrap, Identicon, random_compat, Composer, kjua and base-x
|
||||||
|
|
||||||
Copyright © 2014, Dan Kogai
|
|
||||||
All rights reserved.
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
|
||||||
modification, are permitted provided that the following conditions are met:
|
|
||||||
|
|
||||||
* Redistributions of source code must retain the above copyright notice, this
|
|
||||||
list of conditions and the following disclaimer.
|
|
||||||
|
|
||||||
* Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer in the documentation
|
|
||||||
and/or other materials provided with the distribution.
|
|
||||||
|
|
||||||
* Neither the name of base64.js nor the names of its contributors may be used
|
|
||||||
to endorse or promote products derived from this software without specific
|
|
||||||
prior written permission.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
||||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
||||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
||||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
||||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
||||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
||||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
|
|
||||||
## MIT License for base64.js version 1.7, Bootstrap, Identicon, random_compat,
|
|
||||||
## Composer, kjua and base-x
|
|
||||||
|
|
||||||
Copyright © 2012 Dan Kogai
|
Copyright © 2012 Dan Kogai
|
||||||
Copyright © 2011-2016 Twitter, Inc.
|
Copyright © 2011-2016 Twitter, Inc.
|
||||||
|
|
|
@ -70,7 +70,7 @@ languageselection = false
|
||||||
; Check the documentation at https://content-security-policy.com/
|
; Check the documentation at https://content-security-policy.com/
|
||||||
; Note: If you use a bootstrap theme, you can remove the allow-popups from the sandbox restrictions.
|
; Note: If you use a bootstrap theme, you can remove the allow-popups from the sandbox restrictions.
|
||||||
; By default this disallows to load images from third-party servers, e.g. when they are embedded in pastes. If you wish to allow that, you can adjust the policy here. See https://github.com/PrivateBin/PrivateBin/wiki/FAQ#why-does-not-it-load-embedded-images for details.
|
; By default this disallows to load images from third-party servers, e.g. when they are embedded in pastes. If you wish to allow that, you can adjust the policy here. See https://github.com/PrivateBin/PrivateBin/wiki/FAQ#why-does-not-it-load-embedded-images for details.
|
||||||
; cspheader = "default-src 'none'; manifest-src 'self'; connect-src *; script-src 'self'; style-src 'self'; font-src 'self'; img-src 'self' data:; media-src data:; object-src data:; Referrer-Policy: 'no-referrer'; sandbox allow-same-origin allow-scripts allow-forms allow-popups allow-modals"
|
; cspheader = "default-src 'none'; manifest-src 'self'; connect-src *; script-src 'self' 'unsafe-eval'; style-src 'self'; font-src 'self'; img-src 'self' data:; media-src data:; object-src data:; Referrer-Policy: 'no-referrer'; sandbox allow-same-origin allow-scripts allow-forms allow-popups allow-modals"
|
||||||
|
|
||||||
; stay compatible with PrivateBin Alpha 0.19, less secure
|
; stay compatible with PrivateBin Alpha 0.19, less secure
|
||||||
; if enabled will use base64.js version 1.7 instead of 2.1.9 and sha1 instead of
|
; if enabled will use base64.js version 1.7 instead of 2.1.9 and sha1 instead of
|
||||||
|
|
|
@ -3,13 +3,13 @@
|
||||||
"description": "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 bit AES in Galois Counter mode (GCM).",
|
"description": "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 bit AES in Galois Counter mode (GCM).",
|
||||||
"type": "project",
|
"type": "project",
|
||||||
"keywords": ["private", "secure", "end-to-end-encrypted", "e2e", "paste", "pastebin", "zero", "zero-knowledge", "encryption", "encrypted", "AES"],
|
"keywords": ["private", "secure", "end-to-end-encrypted", "e2e", "paste", "pastebin", "zero", "zero-knowledge", "encryption", "encrypted", "AES"],
|
||||||
"homepage": "https://github.com/PrivateBin",
|
"homepage": "https://privatebin.info/",
|
||||||
"license":"zlib-acknowledgement",
|
"license":"zlib-acknowledgement",
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/PrivateBin/PrivateBin/issues",
|
"issues": "https://github.com/PrivateBin/PrivateBin/issues",
|
||||||
"wiki": "https://github.com/PrivateBin/PrivateBin/wiki",
|
"wiki": "https://github.com/PrivateBin/PrivateBin/wiki",
|
||||||
"source": "https://github.com/PrivateBin/PrivateBin",
|
"source": "https://github.com/PrivateBin/PrivateBin",
|
||||||
"docs": "https://zerobin.dssr.ch/documentation/"
|
"docs": "https://privatebin.info/codedoc/"
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
"php": "^5.4.0 || ^7.0",
|
"php": "^5.4.0 || ^7.0",
|
||||||
|
|
|
@ -20,7 +20,7 @@ $ sudo pear install phpdoc/phpDocumentor
|
||||||
To generate the documentation, change into the main directory and run phpdoc:
|
To generate the documentation, change into the main directory and run phpdoc:
|
||||||
```console
|
```console
|
||||||
$ cd PrivateBin
|
$ cd PrivateBin
|
||||||
$ phpdoc -t doc/phpdoc -d lib/
|
$ phpdoc --visibility public,protected,private -t doc/phpdoc -d lib/
|
||||||
```
|
```
|
||||||
|
|
||||||
**Note:** When used with PHP 7, the prerelease of phpDocumentator 2.9 needs to be
|
**Note:** When used with PHP 7, the prerelease of phpDocumentator 2.9 needs to be
|
||||||
|
@ -55,6 +55,6 @@ $ ln -s /usr/bin/nodejs /usr/local/bin/node
|
||||||
To generate the documentation, change into the main directory and run phpdoc:
|
To generate the documentation, change into the main directory and run phpdoc:
|
||||||
```console
|
```console
|
||||||
$ cd PrivateBin
|
$ cd PrivateBin
|
||||||
$ jsdoc -d doc/jsdoc js/privatebin.js
|
$ jsdoc -p -d doc/jsdoc js/privatebin.js
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
@ -112,8 +112,6 @@
|
||||||
"Fehler auf dem Server oder keine Antwort vom Server",
|
"Fehler auf dem Server oder keine Antwort vom Server",
|
||||||
"Could not post comment: %s":
|
"Could not post comment: %s":
|
||||||
"Konnte Kommentar nicht senden: %s",
|
"Konnte Kommentar nicht senden: %s",
|
||||||
"Please move your mouse for more entropy…":
|
|
||||||
"Bitte bewege Deine Maus um die Entropie zu erhöhen…",
|
|
||||||
"Sending paste…":
|
"Sending paste…":
|
||||||
"Sende Paste…",
|
"Sende Paste…",
|
||||||
"Your paste is <a id=\"pasteurl\" href=\"%s\">%s</a> <span id=\"copyhint\">(Hit [Ctrl]+[c] to copy)</span>":
|
"Your paste is <a id=\"pasteurl\" href=\"%s\">%s</a> <span id=\"copyhint\">(Hit [Ctrl]+[c] to copy)</span>":
|
||||||
|
|
|
@ -112,8 +112,6 @@
|
||||||
"Error del servidor o el servidor no responde",
|
"Error del servidor o el servidor no responde",
|
||||||
"Could not post comment: %s":
|
"Could not post comment: %s":
|
||||||
"No fue posible publicar comentario: %s",
|
"No fue posible publicar comentario: %s",
|
||||||
"Please move your mouse for more entropy…":
|
|
||||||
"Por favor, mueva el ratón para mayor entropía…",
|
|
||||||
"Sending paste…":
|
"Sending paste…":
|
||||||
"Enviando \"paste\"…",
|
"Enviando \"paste\"…",
|
||||||
"Your paste is <a id=\"pasteurl\" href=\"%s\">%s</a> <span id=\"copyhint\">(Hit [Ctrl]+[c] to copy)</span>":
|
"Your paste is <a id=\"pasteurl\" href=\"%s\">%s</a> <span id=\"copyhint\">(Hit [Ctrl]+[c] to copy)</span>":
|
||||||
|
|
|
@ -112,8 +112,6 @@
|
||||||
"Le serveur ne répond pas ou a rencontré une erreur",
|
"Le serveur ne répond pas ou a rencontré une erreur",
|
||||||
"Could not post comment: %s":
|
"Could not post comment: %s":
|
||||||
"Impossible de poster le commentaire : %s",
|
"Impossible de poster le commentaire : %s",
|
||||||
"Please move your mouse for more entropy…":
|
|
||||||
"Merci de bouger votre souris pour plus d'entropie…",
|
|
||||||
"Sending paste…":
|
"Sending paste…":
|
||||||
"Envoi du paste…",
|
"Envoi du paste…",
|
||||||
"Your paste is <a id=\"pasteurl\" href=\"%s\">%s</a> <span id=\"copyhint\">(Hit [Ctrl]+[c] to copy)</span>":
|
"Your paste is <a id=\"pasteurl\" href=\"%s\">%s</a> <span id=\"copyhint\">(Hit [Ctrl]+[c] to copy)</span>":
|
||||||
|
|
|
@ -112,8 +112,6 @@
|
||||||
"A szerveren hiba lépett fel vagy nem válaszol.",
|
"A szerveren hiba lépett fel vagy nem válaszol.",
|
||||||
"Could not post comment: %s":
|
"Could not post comment: %s":
|
||||||
"Nem tudtuk beküldeni a hozzászólást: %s",
|
"Nem tudtuk beküldeni a hozzászólást: %s",
|
||||||
"Please move your mouse for more entropy…":
|
|
||||||
"Nincs elég véletlenszerűség a rendszerben. Mozgasd az egered, hogy növeld az entrópiát.",
|
|
||||||
"Sending paste…":
|
"Sending paste…":
|
||||||
"Bejegyzés elküldése...",
|
"Bejegyzés elküldése...",
|
||||||
"Your paste is <a id=\"pasteurl\" href=\"%s\">%s</a> <span id=\"copyhint\">(Hit [Ctrl]+[c] to copy)</span>":
|
"Your paste is <a id=\"pasteurl\" href=\"%s\">%s</a> <span id=\"copyhint\">(Hit [Ctrl]+[c] to copy)</span>":
|
||||||
|
|
|
@ -112,8 +112,6 @@
|
||||||
"errore o mancata risposta dal server",
|
"errore o mancata risposta dal server",
|
||||||
"Could not post comment: %s":
|
"Could not post comment: %s":
|
||||||
"Impossibile inviare il commento: %s",
|
"Impossibile inviare il commento: %s",
|
||||||
"Please move your mouse for more entropy…":
|
|
||||||
"Muovi il mouse in modo casuale, per generare maggior entropia…",
|
|
||||||
"Sending paste…":
|
"Sending paste…":
|
||||||
"Messaggio in fase di invio…",
|
"Messaggio in fase di invio…",
|
||||||
"Your paste is <a id=\"pasteurl\" href=\"%s\">%s</a> <span id=\"copyhint\">(Hit [Ctrl]+[c] to copy)</span>":
|
"Your paste is <a id=\"pasteurl\" href=\"%s\">%s</a> <span id=\"copyhint\">(Hit [Ctrl]+[c] to copy)</span>":
|
||||||
|
|
|
@ -112,8 +112,6 @@
|
||||||
"Serverfout of server reageert niet",
|
"Serverfout of server reageert niet",
|
||||||
"Could not post comment: %s":
|
"Could not post comment: %s":
|
||||||
"Kon het commentaar niet plaatsen: %s",
|
"Kon het commentaar niet plaatsen: %s",
|
||||||
"Please move your mouse for more entropy…":
|
|
||||||
"Aub uw muis bewegen voor meer entropie…",
|
|
||||||
"Sending paste…":
|
"Sending paste…":
|
||||||
"Geplakte tekst verzenden…",
|
"Geplakte tekst verzenden…",
|
||||||
"Your paste is <a id=\"pasteurl\" href=\"%s\">%s</a> <span id=\"copyhint\">(Hit [Ctrl]+[c] to copy)</span>":
|
"Your paste is <a id=\"pasteurl\" href=\"%s\">%s</a> <span id=\"copyhint\">(Hit [Ctrl]+[c] to copy)</span>":
|
||||||
|
|
|
@ -112,8 +112,6 @@
|
||||||
"tjener feilet eller svarer ikke",
|
"tjener feilet eller svarer ikke",
|
||||||
"Could not post comment: %s":
|
"Could not post comment: %s":
|
||||||
"Kunne ikke sende kommentar: %s",
|
"Kunne ikke sende kommentar: %s",
|
||||||
"Please move your mouse for more entropy…":
|
|
||||||
"Flytt musen for mer entropi…",
|
|
||||||
"Sending paste…":
|
"Sending paste…":
|
||||||
"Sender innlegg…",
|
"Sender innlegg…",
|
||||||
"Your paste is <a id=\"pasteurl\" href=\"%s\">%s</a> <span id=\"copyhint\">(Hit [Ctrl]+[c] to copy)</span>":
|
"Your paste is <a id=\"pasteurl\" href=\"%s\">%s</a> <span id=\"copyhint\">(Hit [Ctrl]+[c] to copy)</span>":
|
||||||
|
|
|
@ -112,8 +112,6 @@
|
||||||
"Lo servidor respond pas o a rencontrat una error",
|
"Lo servidor respond pas o a rencontrat una error",
|
||||||
"Could not post comment: %s":
|
"Could not post comment: %s":
|
||||||
"Impossible de mandar lo comentari : %s",
|
"Impossible de mandar lo comentari : %s",
|
||||||
"Please move your mouse for more entropy…":
|
|
||||||
"Mercés de bolegar vòstra mirga per mai entropia…",
|
|
||||||
"Sending paste…":
|
"Sending paste…":
|
||||||
"Mandadís del tèxte…",
|
"Mandadís del tèxte…",
|
||||||
"Your paste is <a id=\"pasteurl\" href=\"%s\">%s</a> <span id=\"copyhint\">(Hit [Ctrl]+[c] to copy)</span>":
|
"Your paste is <a id=\"pasteurl\" href=\"%s\">%s</a> <span id=\"copyhint\">(Hit [Ctrl]+[c] to copy)</span>":
|
||||||
|
|
|
@ -112,8 +112,6 @@
|
||||||
"błąd serwera lub brak odpowiedzi",
|
"błąd serwera lub brak odpowiedzi",
|
||||||
"Could not post comment: %s":
|
"Could not post comment: %s":
|
||||||
"Nie udało się wysłać komentarza: %s",
|
"Nie udało się wysłać komentarza: %s",
|
||||||
"Please move your mouse for more entropy…":
|
|
||||||
"Proszę poruszać myszą, aby uzyskać większą entropię…",
|
|
||||||
"Sending paste…":
|
"Sending paste…":
|
||||||
"Wysyłanie wklejki…",
|
"Wysyłanie wklejki…",
|
||||||
"Your paste is <a id=\"pasteurl\" href=\"%s\">%s</a> <span id=\"copyhint\">(Hit [Ctrl]+[c] to copy)</span>":
|
"Your paste is <a id=\"pasteurl\" href=\"%s\">%s</a> <span id=\"copyhint\">(Hit [Ctrl]+[c] to copy)</span>":
|
||||||
|
@ -148,8 +146,7 @@
|
||||||
"Odszyfruj",
|
"Odszyfruj",
|
||||||
"Enter password":
|
"Enter password":
|
||||||
"Wpisz hasło",
|
"Wpisz hasło",
|
||||||
"Loading…":
|
"Loading…": "Wczytywanie…",
|
||||||
"Wczytywanie…",
|
|
||||||
"Decrypting paste…": "Odszyfrowywanie wklejki…",
|
"Decrypting paste…": "Odszyfrowywanie wklejki…",
|
||||||
"Preparing new paste…": "Przygotowywanie nowej wklejki…",
|
"Preparing new paste…": "Przygotowywanie nowej wklejki…",
|
||||||
"In case this message never disappears please have a look at <a href=\"https://github.com/PrivateBin/PrivateBin/wiki/FAQ#why-does-not-the-loading-message-go-away\">this FAQ for information to troubleshoot</a>.":
|
"In case this message never disappears please have a look at <a href=\"https://github.com/PrivateBin/PrivateBin/wiki/FAQ#why-does-not-the-loading-message-go-away\">this FAQ for information to troubleshoot</a>.":
|
||||||
|
|
|
@ -112,8 +112,6 @@
|
||||||
"Servidor em erro ou não responsivo",
|
"Servidor em erro ou não responsivo",
|
||||||
"Could not post comment: %s":
|
"Could not post comment: %s":
|
||||||
"Não foi possível publicar o comentário: %s",
|
"Não foi possível publicar o comentário: %s",
|
||||||
"Please move your mouse for more entropy…":
|
|
||||||
"Por favor, mova o mouse para maior entropia…",
|
|
||||||
"Sending paste…":
|
"Sending paste…":
|
||||||
"Enviando cópia…",
|
"Enviando cópia…",
|
||||||
"Your paste is <a id=\"pasteurl\" href=\"%s\">%s</a> <span id=\"copyhint\">(Hit [Ctrl]+[c] to copy)</span>":
|
"Your paste is <a id=\"pasteurl\" href=\"%s\">%s</a> <span id=\"copyhint\">(Hit [Ctrl]+[c] to copy)</span>":
|
||||||
|
|
|
@ -112,8 +112,6 @@
|
||||||
"ошибка сервера или нет ответа",
|
"ошибка сервера или нет ответа",
|
||||||
"Could not post comment: %s":
|
"Could not post comment: %s":
|
||||||
"Не удалось опубликовать комментарий: %s",
|
"Не удалось опубликовать комментарий: %s",
|
||||||
"Please move your mouse for more entropy…":
|
|
||||||
"Пожалуйста, двигайте мышкой для большей энтропии…",
|
|
||||||
"Sending paste…":
|
"Sending paste…":
|
||||||
"Отправка записи…",
|
"Отправка записи…",
|
||||||
"Your paste is <a id=\"pasteurl\" href=\"%s\">%s</a> <span id=\"copyhint\">(Hit [Ctrl]+[c] to copy)</span>":
|
"Your paste is <a id=\"pasteurl\" href=\"%s\">%s</a> <span id=\"copyhint\">(Hit [Ctrl]+[c] to copy)</span>":
|
||||||
|
|
|
@ -112,8 +112,6 @@
|
||||||
"napaka na strežniku, ali pa se strežnik ne odziva",
|
"napaka na strežniku, ali pa se strežnik ne odziva",
|
||||||
"Could not post comment: %s":
|
"Could not post comment: %s":
|
||||||
"Komentarja ni bilo mogoče objaviti : %s",
|
"Komentarja ni bilo mogoče objaviti : %s",
|
||||||
"Please move your mouse for more entropy…":
|
|
||||||
"Prosim premakni svojo miško za več entropije…",
|
|
||||||
"Sending paste…":
|
"Sending paste…":
|
||||||
"Pošiljam prilepek…",
|
"Pošiljam prilepek…",
|
||||||
"Your paste is <a id=\"pasteurl\" href=\"%s\">%s</a> <span id=\"copyhint\">(Hit [Ctrl]+[c] to copy)</span>":
|
"Your paste is <a id=\"pasteurl\" href=\"%s\">%s</a> <span id=\"copyhint\">(Hit [Ctrl]+[c] to copy)</span>":
|
||||||
|
|
|
@ -112,8 +112,6 @@
|
||||||
"服务器错误或无回应",
|
"服务器错误或无回应",
|
||||||
"Could not post comment: %s":
|
"Could not post comment: %s":
|
||||||
"无法发送评论: %s",
|
"无法发送评论: %s",
|
||||||
"Please move your mouse for more entropy…":
|
|
||||||
"请移动鼠标增加随机性…",
|
|
||||||
"Sending paste…":
|
"Sending paste…":
|
||||||
"粘贴内容提交中…",
|
"粘贴内容提交中…",
|
||||||
"Your paste is <a id=\"pasteurl\" href=\"%s\">%s</a> <span id=\"copyhint\">(Hit [Ctrl]+[c] to copy)</span>":
|
"Your paste is <a id=\"pasteurl\" href=\"%s\">%s</a> <span id=\"copyhint\">(Hit [Ctrl]+[c] to copy)</span>":
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
---
|
||||||
|
include:
|
||||||
|
- privatebin.js
|
||||||
|
reporter:
|
||||||
|
- text
|
||||||
|
- html
|
||||||
|
report-dir: ../tst/log/js-coverage-report
|
||||||
|
temp-dir: /tmp/nyc-output
|
|
@ -1 +0,0 @@
|
||||||
(function(global,factory){typeof exports==="object"&&typeof module!=="undefined"?module.exports=factory(global):typeof define==="function"&&define.amd?define(factory):factory(global)})(typeof self!=="undefined"?self:typeof window!=="undefined"?window:typeof global!=="undefined"?global:this,function(global){"use strict";var _Base64=global.Base64;var version="2.4.5";var buffer;if(typeof module!=="undefined"&&module.exports){try{buffer=require("buffer").Buffer}catch(err){}}var b64chars="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";var b64tab=function(bin){var t={};for(var i=0,l=bin.length;i<l;i++)t[bin.charAt(i)]=i;return t}(b64chars);var fromCharCode=String.fromCharCode;var cb_utob=function(c){if(c.length<2){var cc=c.charCodeAt(0);return cc<128?c:cc<2048?fromCharCode(192|cc>>>6)+fromCharCode(128|cc&63):fromCharCode(224|cc>>>12&15)+fromCharCode(128|cc>>>6&63)+fromCharCode(128|cc&63)}else{var cc=65536+(c.charCodeAt(0)-55296)*1024+(c.charCodeAt(1)-56320);return fromCharCode(240|cc>>>18&7)+fromCharCode(128|cc>>>12&63)+fromCharCode(128|cc>>>6&63)+fromCharCode(128|cc&63)}};var re_utob=/[\uD800-\uDBFF][\uDC00-\uDFFFF]|[^\x00-\x7F]/g;var utob=function(u){return u.replace(re_utob,cb_utob)};var cb_encode=function(ccc){var padlen=[0,2,1][ccc.length%3],ord=ccc.charCodeAt(0)<<16|(ccc.length>1?ccc.charCodeAt(1):0)<<8|(ccc.length>2?ccc.charCodeAt(2):0),chars=[b64chars.charAt(ord>>>18),b64chars.charAt(ord>>>12&63),padlen>=2?"=":b64chars.charAt(ord>>>6&63),padlen>=1?"=":b64chars.charAt(ord&63)];return chars.join("")};var btoa=global.btoa?function(b){return global.btoa(b)}:function(b){return b.replace(/[\s\S]{1,3}/g,cb_encode)};var _encode=buffer?buffer.from&&Uint8Array&&buffer.from!==Uint8Array.from?function(u){return(u.constructor===buffer.constructor?u:buffer.from(u)).toString("base64")}:function(u){return(u.constructor===buffer.constructor?u:new buffer(u)).toString("base64")}:function(u){return btoa(utob(u))};var encode=function(u,urisafe){return!urisafe?_encode(String(u)):_encode(String(u)).replace(/[+\/]/g,function(m0){return m0=="+"?"-":"_"}).replace(/=/g,"")};var encodeURI=function(u){return encode(u,true)};var re_btou=new RegExp(["[À-ß][-¿]","[à-ï][-¿]{2}","[ð-÷][-¿]{3}"].join("|"),"g");var cb_btou=function(cccc){switch(cccc.length){case 4:var cp=(7&cccc.charCodeAt(0))<<18|(63&cccc.charCodeAt(1))<<12|(63&cccc.charCodeAt(2))<<6|63&cccc.charCodeAt(3),offset=cp-65536;return fromCharCode((offset>>>10)+55296)+fromCharCode((offset&1023)+56320);case 3:return fromCharCode((15&cccc.charCodeAt(0))<<12|(63&cccc.charCodeAt(1))<<6|63&cccc.charCodeAt(2));default:return fromCharCode((31&cccc.charCodeAt(0))<<6|63&cccc.charCodeAt(1))}};var btou=function(b){return b.replace(re_btou,cb_btou)};var cb_decode=function(cccc){var len=cccc.length,padlen=len%4,n=(len>0?b64tab[cccc.charAt(0)]<<18:0)|(len>1?b64tab[cccc.charAt(1)]<<12:0)|(len>2?b64tab[cccc.charAt(2)]<<6:0)|(len>3?b64tab[cccc.charAt(3)]:0),chars=[fromCharCode(n>>>16),fromCharCode(n>>>8&255),fromCharCode(n&255)];chars.length-=[0,0,2,1][padlen];return chars.join("")};var atob=global.atob?function(a){return global.atob(a)}:function(a){return a.replace(/[\s\S]{1,4}/g,cb_decode)};var _decode=buffer?buffer.from&&Uint8Array&&buffer.from!==Uint8Array.from?function(a){return(a.constructor===buffer.constructor?a:buffer.from(a,"base64")).toString()}:function(a){return(a.constructor===buffer.constructor?a:new buffer(a,"base64")).toString()}:function(a){return btou(atob(a))};var decode=function(a){return _decode(String(a).replace(/[-_]/g,function(m0){return m0=="-"?"+":"/"}).replace(/[^A-Za-z0-9\+\/]/g,""))};var noConflict=function(){var Base64=global.Base64;global.Base64=_Base64;return Base64};global.Base64={VERSION:version,atob:atob,btoa:btoa,fromBase64:decode,toBase64:encode,utob:utob,encode:encode,encodeURI:encodeURI,btou:btou,decode:decode,noConflict:noConflict};if(typeof Object.defineProperty==="function"){var noEnum=function(v){return{value:v,enumerable:false,writable:true,configurable:true}};global.Base64.extendString=function(){Object.defineProperty(String.prototype,"fromBase64",noEnum(function(){return decode(this)}));Object.defineProperty(String.prototype,"toBase64",noEnum(function(urisafe){return encode(this,urisafe)}));Object.defineProperty(String.prototype,"toBase64URI",noEnum(function(){return encode(this,true)}))}}if(global["Meteor"]){Base64=global.Base64}if(typeof module!=="undefined"&&module.exports){module.exports.Base64=global.Base64}else if(typeof define==="function"&&define.amd){define([],function(){return global.Base64})}return{Base64:global.Base64}});
|
|
|
@ -1,16 +1,35 @@
|
||||||
{
|
{
|
||||||
"@context": {
|
"@context": {
|
||||||
"so": "https://schema.org/",
|
"so": "https://schema.org/",
|
||||||
"status": "so:Integer",
|
"pb": "?jsonld=types#",
|
||||||
"id": "so:name",
|
"cm": "?jsonld=commentmeta#",
|
||||||
"parentid": "so:name",
|
"status": {
|
||||||
"url: {
|
"@type": "so:Integer"
|
||||||
"@id": "so:url",
|
},
|
||||||
"@type": "@id"
|
"id": {
|
||||||
|
"@type": "so:name"
|
||||||
|
},
|
||||||
|
"pasteid": {
|
||||||
|
"@type": "so:name"
|
||||||
|
},
|
||||||
|
"parentid": {
|
||||||
|
"@type": "so:name"
|
||||||
|
},
|
||||||
|
"url": {
|
||||||
|
"@type": "so:url"
|
||||||
|
},
|
||||||
|
"v": {
|
||||||
|
"@type": "so:Integer",
|
||||||
|
"@value": 2
|
||||||
|
},
|
||||||
|
"ct": {
|
||||||
|
"@type": "pb:CipherText"
|
||||||
|
},
|
||||||
|
"adata": {
|
||||||
|
"@type": "pb:CipherParameters"
|
||||||
},
|
},
|
||||||
"data": "so:Text",
|
|
||||||
"meta": {
|
"meta": {
|
||||||
"@id": "?jsonld=commentmeta"
|
"@type": "cm:MetaData"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,8 +1,14 @@
|
||||||
{
|
{
|
||||||
"@context": {
|
"@context": {
|
||||||
"so": "https://schema.org/",
|
"so": "https://schema.org/",
|
||||||
"postdate": "so:Integer",
|
"pb": "?jsonld=types#"
|
||||||
"nickname": "so:Text",
|
},
|
||||||
"vizhash": "so:Text"
|
"MetaData": {
|
||||||
|
"created": {
|
||||||
|
"@type": "CreationTime"
|
||||||
|
},
|
||||||
|
"icon": {
|
||||||
|
"@type": "so:url"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
15
js/common.js
15
js/common.js
|
@ -6,13 +6,12 @@ global.jsc = require('jsverify');
|
||||||
global.jsdom = require('jsdom-global');
|
global.jsdom = require('jsdom-global');
|
||||||
global.cleanup = global.jsdom();
|
global.cleanup = global.jsdom();
|
||||||
global.fs = require('fs');
|
global.fs = require('fs');
|
||||||
|
global.WebCrypto = require('node-webcrypto-ossl');
|
||||||
|
|
||||||
// application libraries to test
|
// application libraries to test
|
||||||
global.$ = global.jQuery = require('./jquery-3.3.1');
|
global.$ = global.jQuery = require('./jquery-3.3.1');
|
||||||
global.sjcl = require('./sjcl-1.0.7');
|
global.RawDeflate = require('./rawinflate-0.3').RawDeflate;
|
||||||
global.Base64 = require('./base64-2.4.5').Base64;
|
global.zlib = require('./zlib-1.2.11').zlib;
|
||||||
global.RawDeflate = require('./rawdeflate-0.5').RawDeflate;
|
|
||||||
global.RawDeflate.inflate = require('./rawinflate-0.3').RawDeflate.inflate;
|
|
||||||
require('./prettify');
|
require('./prettify');
|
||||||
global.prettyPrint = window.PR.prettyPrint;
|
global.prettyPrint = window.PR.prettyPrint;
|
||||||
global.prettyPrintOne = window.PR.prettyPrintOne;
|
global.prettyPrintOne = window.PR.prettyPrintOne;
|
||||||
|
@ -54,15 +53,9 @@ var a2zString = ['a','b','c','d','e','f','g','h','i','j','k','l','m',
|
||||||
'`': '`',
|
'`': '`',
|
||||||
'=': '='
|
'=': '='
|
||||||
},
|
},
|
||||||
logFile = fs.createWriteStream('test.log'),
|
|
||||||
mimeFile = fs.createReadStream('/etc/mime.types'),
|
mimeFile = fs.createReadStream('/etc/mime.types'),
|
||||||
mimeLine = '';
|
mimeLine = '';
|
||||||
|
|
||||||
// redirect console messages to log file
|
|
||||||
console.info = console.warn = console.error = function () {
|
|
||||||
logFile.write(Array.prototype.slice.call(arguments).join('') + '\n');
|
|
||||||
};
|
|
||||||
|
|
||||||
// populate mime types from environment
|
// populate mime types from environment
|
||||||
mimeFile.on('data', function(data) {
|
mimeFile.on('data', function(data) {
|
||||||
mimeLine += data;
|
mimeLine += data;
|
||||||
|
@ -99,6 +92,8 @@ function parseMime(line) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// common testing helper functions
|
// common testing helper functions
|
||||||
|
exports.atob = atob;
|
||||||
|
exports.btoa = btoa;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* convert all applicable characters to HTML entities
|
* convert all applicable characters to HTML entities
|
||||||
|
|
|
@ -0,0 +1,43 @@
|
||||||
|
{
|
||||||
|
"name": "privatebin",
|
||||||
|
"version": "1.2.1",
|
||||||
|
"description": "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 bit AES in Galois Counter mode (GCM).",
|
||||||
|
"main": "privatebin.js",
|
||||||
|
"directories": {
|
||||||
|
"test": "test"
|
||||||
|
},
|
||||||
|
"dependencies": {},
|
||||||
|
"devDependencies": {
|
||||||
|
"jsdom": "^9.12.0",
|
||||||
|
"jsdom-global": "^2.1.1",
|
||||||
|
"jsverify": "^0.8.3",
|
||||||
|
"mime-types": "^2.1.20",
|
||||||
|
"node-webcrypto-ossl": "^1.0.37"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"test": "mocha"
|
||||||
|
},
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "git+https://github.com/PrivateBin/PrivateBin.git"
|
||||||
|
},
|
||||||
|
"keywords": [
|
||||||
|
"private",
|
||||||
|
"secure",
|
||||||
|
"end-to-end-encrypted",
|
||||||
|
"e2e",
|
||||||
|
"paste",
|
||||||
|
"pastebin",
|
||||||
|
"zero",
|
||||||
|
"zero-knowledge",
|
||||||
|
"encryption",
|
||||||
|
"encrypted",
|
||||||
|
"AES"
|
||||||
|
],
|
||||||
|
"author": "",
|
||||||
|
"license": "zlib-acknowledgement",
|
||||||
|
"bugs": {
|
||||||
|
"url": "https://github.com/PrivateBin/PrivateBin/issues"
|
||||||
|
},
|
||||||
|
"homepage": "https://privatebin.info/"
|
||||||
|
}
|
|
@ -1,24 +1,42 @@
|
||||||
{
|
{
|
||||||
"@context": {
|
"@context": {
|
||||||
"so": "https://schema.org/",
|
"so": "https://schema.org/",
|
||||||
"status": {"@id": "so:Integer"},
|
"pb": "?jsonld=types#",
|
||||||
"id": {"@id": "so:name"},
|
"pm": "?jsonld=pastemeta#",
|
||||||
"deletetoken": {"@id": "so:Text"},
|
"status": {
|
||||||
"url": {
|
"@type": "so:Integer"
|
||||||
"@type": "@id",
|
},
|
||||||
"@id": "so:url"
|
"id": {
|
||||||
|
"@type": "so:name"
|
||||||
|
},
|
||||||
|
"deletetoken": {
|
||||||
|
"@type": "so:Text"
|
||||||
|
},
|
||||||
|
"url": {
|
||||||
|
"@type": "so:url"
|
||||||
|
},
|
||||||
|
"v": {
|
||||||
|
"@type": "so:Integer",
|
||||||
|
"@value": 2
|
||||||
|
},
|
||||||
|
"ct": {
|
||||||
|
"@type": "pb:CipherText"
|
||||||
|
},
|
||||||
|
"adata": {
|
||||||
|
"@type": "pm:AuthenticatedData"
|
||||||
},
|
},
|
||||||
"data": {"@id": "so:Text"},
|
|
||||||
"attachment": {"@id": "so:Text"},
|
|
||||||
"attachmentname": {"@id": "so:Text"},
|
|
||||||
"meta": {
|
"meta": {
|
||||||
"@id": "?jsonld=pastemeta"
|
"@type": "pm:MetaData"
|
||||||
},
|
},
|
||||||
"comments": {
|
"comments": {
|
||||||
"@id": "?jsonld=comment",
|
"@type": "?jsonld=comment",
|
||||||
"@container": "@list"
|
"@container": "@list"
|
||||||
},
|
},
|
||||||
"comment_count": {"@id": "so:Integer"},
|
"comment_count": {
|
||||||
"comment_offset": {"@id": "so:Integer"}
|
"@type": "so:Integer"
|
||||||
|
},
|
||||||
|
"comment_offset": {
|
||||||
|
"@type": "so:Integer"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,11 +1,31 @@
|
||||||
{
|
{
|
||||||
"@context": {
|
"@context": {
|
||||||
"so": "https://schema.org/",
|
"so": "https://schema.org/",
|
||||||
"formatter": {"@id": "so:Text"},
|
"pb": "?jsonld=types#"
|
||||||
"postdate": {"@id": "so:Integer"},
|
},
|
||||||
"opendiscussion": {"@id": "so:True"},
|
"AuthenticatedData": {
|
||||||
"burnafterreading": {"@id": "so:True"},
|
"@container": "@list",
|
||||||
"expire_date": {"@id": "so:Integer"},
|
"@value": [
|
||||||
"remaining_time": {"@id": "so:Integer"}
|
{
|
||||||
|
"@type": "pb:CipherParameters"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"@type": "pb:Formatter"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"@type": "pb:OpenDiscussion"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"@type": "pb:BurnAfterReading"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"MetaData": {
|
||||||
|
"expire": {
|
||||||
|
"@type": "pb:Expiration"
|
||||||
|
},
|
||||||
|
"time_to_live": {
|
||||||
|
"@type": "pb:RemainingSeconds"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
1744
js/privatebin.js
1744
js/privatebin.js
File diff suppressed because it is too large
Load Diff
1675
js/rawdeflate-0.5.js
1675
js/rawdeflate-0.5.js
File diff suppressed because it is too large
Load Diff
|
@ -1,60 +0,0 @@
|
||||||
"use strict";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}}};
|
|
||||||
sjcl.cipher.aes=function(a){this.s[0][0][0]||this.O();var b,c,d,e,f=this.s[0][4],g=this.s[1];b=a.length;var h=1;if(4!==b&&6!==b&&8!==b)throw 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=f[c>>>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<n;m++)h=a[e>>>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<<c)-1},concat:function(a,b){if(0===a.length||0===b.length)return a.concat(b);var c=a[a.length-1],d=sjcl.bitArray.getPartial(c);return 32===d?a.concat(b):sjcl.bitArray.$(b,d,c|0,a.slice(0,a.length-1))},bitLength:function(a){var b=a.length;return 0===
|
|
||||||
b?0:32*(b-1)+sjcl.bitArray.getPartial(a[b-1])},clamp:function(a,b){if(32*a.length<b)return a;a=a.slice(0,Math.ceil(b/32));var c=a.length;b=b&31;0<c&&b&&(a[c-1]=sjcl.bitArray.partial(b,a[c-1]&2147483648>>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<a.length;d++)c|=a[d]^b[d];return 0===
|
|
||||||
c},$:function(a,b,c,d){var e;e=0;for(void 0===d&&(d=[]);32<=b;b-=32)d.push(c),c=0;if(0===b)return d.concat(a);for(e=0;e<a.length;e++)d.push(c|a[e]>>>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<b+a?c:d.pop(),1));return d},i:function(a,b){return[a[0]^b[0],a[1]^b[1],a[2]^b[2],a[3]^b[3]]},byteswapM:function(a){var b,c;for(b=0;b<a.length;++b)c=a[b],a[b]=c>>>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<c/8;d++)0===(d&3)&&(e=a[d/4]),b+=String.fromCharCode(e>>>8>>>8>>>8),e<<=8;return decodeURIComponent(escape(b))},toBits:function(a){a=unescape(encodeURIComponent(a));var b=[],c,d=0;for(c=0;c<a.length;c++)d=d<<8|a.charCodeAt(c),3===(c&3)&&(b.push(d),d=0);c&3&&b.push(sjcl.bitArray.partial(8*(c&3),d));return b}};
|
|
||||||
sjcl.codec.hex={fromBits:function(a){var b="",c;for(c=0;c<a.length;c++)b+=((a[c]|0)+0xf00000000000).toString(16).substr(4);return b.substr(0,sjcl.bitArray.bitLength(a)/4)},toBits:function(a){var b,c=[],d;a=a.replace(/\s|0x/g,"");d=a.length;a=a+"00000000";for(b=0;b<a.length;b+=8)c.push(parseInt(a.substr(b,8),16)^0);return sjcl.bitArray.clamp(c,4*d)}};
|
|
||||||
sjcl.codec.base32={B:"ABCDEFGHIJKLMNOPQRSTUVWXYZ234567",X:"0123456789ABCDEFGHIJKLMNOPQRSTUV",BITS:32,BASE:5,REMAINING:27,fromBits:function(a,b,c){var d=sjcl.codec.base32.BASE,e=sjcl.codec.base32.REMAINING,f="",g=0,h=sjcl.codec.base32.B,k=0,l=sjcl.bitArray.bitLength(a);c&&(h=sjcl.codec.base32.X);for(c=0;f.length*d<l;)f+=h.charAt((k^a[c]>>>g)>>>e),g<d?(k=a[c]<<d-g,g+=e,c++):(k<<=d,g-=d);for(;f.length&7&&!b;)f+="=";return f},toBits:function(a,b){a=a.replace(/\s|=/g,"").toUpperCase();var c=sjcl.codec.base32.BITS,
|
|
||||||
d=sjcl.codec.base32.BASE,e=sjcl.codec.base32.REMAINING,f=[],g,h=0,k=sjcl.codec.base32.B,l=0,n,m="base32";b&&(k=sjcl.codec.base32.X,m="base32hex");for(g=0;g<a.length;g++){n=k.indexOf(a.charAt(g));if(0>n){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<<c-h):(h+=d,l^=n<<c-h)}h&56&&f.push(sjcl.bitArray.partial(h&56,l,1));return f}};
|
|
||||||
sjcl.codec.base32hex={fromBits:function(a,b){return sjcl.codec.base32.fromBits(a,b,1)},toBits:function(a){return sjcl.codec.base32.toBits(a,1)}};
|
|
||||||
sjcl.codec.base64={B:"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",fromBits:function(a,b,c){var d="",e=0,f=sjcl.codec.base64.B,g=0,h=sjcl.bitArray.bitLength(a);c&&(f=f.substr(0,62)+"-_");for(c=0;6*d.length<h;)d+=f.charAt((g^a[c]>>>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;d<a.length;d++){h=f.indexOf(a.charAt(d));
|
|
||||||
if(0>h)throw new sjcl.exception.invalid("this isn't base64!");26<e?(e-=26,c.push(g^h>>>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(0x1fffffffffffff<a)throw new sjcl.exception.invalid("Cannot hash more than 2^53 - 1 bits");if("undefined"!==typeof Uint32Array){var d=new Uint32Array(c),e=0;for(b=512+b-(512+b&0x1ff);b<=a;b+=512)u(this,d.subarray(16*e,
|
|
||||||
16*(e+1))),e+=1;c.splice(0,16*e)}else for(b=512+b-(512+b&0x1ff);b<=a;b+=512)u(this,c.splice(0,16));return this},finalize:function(){var a,b=this.A,c=this.F,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.l/0x100000000));for(b.push(this.l|0);b.length;)u(this,b.splice(0,16));this.reset();return c},Y:[],b:[],O:function(){function a(a){return 0x100000000*(a-Math.floor(a))|0}for(var b=0,c=2,d,e;64>b;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);-1<a&&sjcl.mode.ccm.G.splice(a,1)},fa:function(a){var b=sjcl.mode.ccm.G.slice(),c;for(c=0;c<b.length;c+=1)b[c](a)},encrypt:function(a,b,c,d,e){var f,g=b.slice(0),h=sjcl.bitArray,k=h.bitLength(c)/8,l=h.bitLength(g)/8;e=e||64;d=d||[];if(7>k)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;b<g.length;b+=4)d=a.encrypt(k(d,g.slice(b,b+4).concat([0,0,0])));return d},V:function(a,b,c,d,e,f){var g=sjcl.bitArray,h=g.i;e/=8;if(e%2||4>e||16<e)throw new sjcl.exception.invalid("ccm: invalid tag length");
|
|
||||||
if(0xffffffff<d.length||0xffffffff<b.length)throw new sjcl.exception.bug("ccm: can't deal with 4GiB or more data");c=sjcl.mode.ccm.na(a,d,c,e,g.bitLength(b)/8,f);for(d=0;d<b.length;d+=4)c=a.encrypt(h(c,b.slice(d,d+4).concat([0,0,0])));return g.clamp(c,8*e)},C:function(a,b,c,d,e,f){var g,h=sjcl.bitArray;g=h.i;var k=b.length,l=h.bitLength(b),n=k/50,m=n;c=h.concat([h.partial(8,f-1)],c).concat([0,0,0]).slice(0,4);d=h.bitSlice(g(d,a.encrypt(c)),0,e);if(!k)return{tag:d,data:[]};for(g=0;g<k;g+=4)g>n&&(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+4<b.length;g+=4)m=b.slice(g,g+4),n=l(n,m),p=p.concat(l(c,a.encrypt(l(c,m)))),c=h(c);m=b.slice(g);b=k.bitLength(m);g=a.encrypt(l(c,[0,0,0,b]));m=k.clamp(l(m.concat([0,0,0]),g),b);n=l(n,l(m.concat([0,0,0]),g));n=a.encrypt(l(n,l(c,h(c))));
|
|
||||||
d.length&&(n=l(n,f?d:sjcl.mode.ocb2.pmac(a,d)));return p.concat(k.concat(m,k.clamp(n,e)))},decrypt:function(a,b,c,d,e,f){if(128!==sjcl.bitArray.bitLength(c))throw new sjcl.exception.invalid("ocb iv must be 128 bits");e=e||64;var g=sjcl.mode.ocb2.S,h=sjcl.bitArray,k=h.i,l=[0,0,0,0],n=g(a.encrypt(c)),m,p,r=sjcl.bitArray.bitLength(b)-e,q=[];d=d||[];for(c=0;c+4<r/32;c+=4)m=k(n,a.decrypt(k(n,b.slice(c,c+4)))),l=k(l,m),q=q.concat(m),n=g(n);p=r-32*c;m=a.encrypt(k(n,[0,0,0,p]));m=k(m,h.clamp(b.slice(c),p).concat([0,
|
|
||||||
0,0]));l=k(l,m);l=a.encrypt(k(l,k(n,g(n))));d.length&&(l=k(l,f?d:sjcl.mode.ocb2.pmac(a,d)));if(!h.equal(h.clamp(l,e),h.bitSlice(b,r)))throw new sjcl.exception.corrupt("ocb: tag doesn't match");return q.concat(h.clamp(m,p))},pmac:function(a,b){var c,d=sjcl.mode.ocb2.S,e=sjcl.bitArray,f=e.i,g=[0,0,0,0],h=a.encrypt([0,0,0,0]),h=f(h,d(d(h)));for(c=0;c+4<b.length;c+=4)h=d(h),g=f(g,a.encrypt(f(h,b.slice(c,c+4))));c=b.slice(c);128>e.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<d;d--)f[d]=f[d]>>>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;d<e;d+=4)b[0]^=0xffffffff&c[d],b[1]^=0xffffffff&c[d+1],b[2]^=0xffffffff&c[d+2],b[3]^=0xffffffff&c[d+3],b=sjcl.mode.gcm.ka(b,a);return b},C:function(a,b,c,d,e,f){var g,h,k,l,n,m,p,r,q=sjcl.bitArray;m=c.length;p=q.bitLength(c);r=q.bitLength(d);h=q.bitLength(e);
|
|
||||||
g=b.encrypt([0,0,0,0]);96===h?(e=e.slice(0),e=q.concat(e,[1])):(e=sjcl.mode.gcm.j(g,[0,0,0,0],e),e=sjcl.mode.gcm.j(g,e,[0,0,Math.floor(h/0x100000000),h&0xffffffff]));h=sjcl.mode.gcm.j(g,[0,0,0,0],d);n=e.slice(0);d=h.slice(0);a||(d=sjcl.mode.gcm.j(g,h,c));for(l=0;l<m;l+=4)n[3]++,k=b.encrypt(n),c[l]^=k[0],c[l+1]^=k[1],c[l+2]^=k[2],c[l+3]^=k[3];c=q.clamp(c,p);a&&(d=sjcl.mode.gcm.j(g,h,c));a=[Math.floor(r/0x100000000),r&0xffffffff,Math.floor(p/0x100000000),p&0xffffffff];d=sjcl.mode.gcm.j(g,d,a);k=b.encrypt(e);
|
|
||||||
d[0]^=k[0];d[1]^=k[1];d[2]^=k[2];d[3]^=k[3];return{tag:q.bitSlice(d,0,f),data:c}}};sjcl.misc.hmac=function(a,b){this.W=b=b||sjcl.hash.sha256;var c=[[],[]],d,e=b.prototype.blockSize/32;this.w=[new b,new b];a.length>e&&(a=b.hash(a));for(d=0;d<e;d++)c[0][d]=a[d]^909522486,c[1][d]=a[d]^1549556828;this.w[0].update(c[0]);this.w[1].update(c[1]);this.R=new b(this.w[0])};
|
|
||||||
sjcl.misc.hmac.prototype.encrypt=sjcl.misc.hmac.prototype.mac=function(a){if(this.aa)throw new sjcl.exception.invalid("encrypt on already updated hmac called!");this.update(a);return this.digest(a)};sjcl.misc.hmac.prototype.reset=function(){this.R=new this.W(this.w[0]);this.aa=!1};sjcl.misc.hmac.prototype.update=function(a){this.aa=!0;this.R.update(a)};sjcl.misc.hmac.prototype.digest=function(){var a=this.R.finalize(),a=(new this.W(this.w[1])).update(a).finalize();this.reset();return a};
|
|
||||||
sjcl.misc.pbkdf2=function(a,b,c,d,e){c=c||1E4;if(0>d||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;g<c;g++)for(f=a.encrypt(f),h=0;h<f.length;h++)e[h]^=f[h];l=l.concat(e)}d&&(l=n.clamp(l,d));return l};
|
|
||||||
sjcl.prng=function(a){this.c=[new sjcl.hash.sha256];this.m=[0];this.P=0;this.H={};this.N=0;this.U={};this.Z=this.f=this.o=this.ha=0;this.b=[0,0,0,0,0,0,0,0];this.h=[0,0,0,0];this.L=void 0;this.M=a;this.D=!1;this.K={progress:{},seeded:{}};this.u=this.ga=0;this.I=1;this.J=2;this.ca=0x10000;this.T=[0,48,64,96,128,192,0x100,384,512,768,1024];this.da=3E4;this.ba=80};
|
|
||||||
sjcl.prng.prototype={randomWords:function(a,b){var c=[],d;d=this.isReady(b);var e;if(d===this.u)throw new sjcl.exception.notReady("generator isn't seeded");if(d&this.J){d=!(d&this.I);e=[];var f=0,g;this.Z=e[0]=(new Date).valueOf()+this.da;for(g=0;16>g;g++)e.push(0x100000000*Math.random()|0);for(g=0;g<this.c.length&&(e=e.concat(this.c[g].finalize()),f+=this.m[g],this.m[g]=0,d||!(this.P&1<<g));g++);this.P>=1<<this.c.length&&(this.c.push(new sjcl.hash.sha256),this.m.push(0));this.f-=f;f>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<a;d+=4)0===(d+1)%this.ca&&y(this),e=z(this),c.push(e[0],e[1],e[2],e[3]);y(this);return c.slice(0,a)},setDefaultParanoia:function(a,b){if(0===a&&"Setting paranoia=0 will ruin your security; use it only for testing"!==b)throw new sjcl.exception.invalid("Setting paranoia=0 will ruin your security; use it only for testing");this.M=a},addEntropy:function(a,
|
|
||||||
b,c){c=c||"user";var d,e,f=(new Date).valueOf(),g=this.H[c],h=this.isReady(),k=0;d=this.U[c];void 0===d&&(d=this.U[c]=this.ha++);void 0===g&&(g=this.H[c]=0);this.H[c]=(this.H[c]+1)%this.c.length;switch(typeof a){case "number":void 0===b&&(b=1);this.c[g].update([d,this.N++,1,b,f,1,a|0]);break;case "object":c=Object.prototype.toString.call(a);if("[object Uint32Array]"===c){e=[];for(c=0;c<a.length;c++)e.push(a[c]);a=e}else for("[object Array]"!==c&&(k=1),c=0;c<a.length&&!k;c++)"number"!==typeof a[c]&&
|
|
||||||
(k=1);if(!k){if(void 0===b)for(c=b=0;c<a.length;c++)for(e=a[c];0<e;)b++,e=e>>>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;c<f.length;c++)d=f[c],delete e[d]},la:function(){C(this,1)},oa:function(a){var b,c;try{b=a.x||a.clientX||a.offsetX||0,c=a.y||a.clientY||a.offsetY||0}catch(d){c=b=0}0!=b&&0!=c&&this.addEntropy([b,c],2,"mouse");C(this,0)},qa:function(a){a=
|
|
||||||
a.touches[0]||a.changedTouches[0];this.addEntropy([a.pageX||a.clientX,a.pageY||a.clientY],1,"touch");C(this,0)},ma:function(){C(this,2)},ea:function(a){a=a.accelerationIncludingGravity.x||a.accelerationIncludingGravity.y||a.accelerationIncludingGravity.z;if(window.orientation){var b=window.orientation;"number"===typeof b&&this.addEntropy(b,1,"accelerometer")}a&&this.addEntropy(a,2,"accelerometer");C(this,0)}};
|
|
||||||
function A(a,b){var c,d=sjcl.random.K[a],e=[];for(c in d)d.hasOwnProperty(c)&&e.push(d[c]);for(c=0;c<e.length;c++)e[c](b)}function C(a,b){"undefined"!==typeof window&&window.performance&&"function"===typeof window.performance.now?a.addEntropy(window.performance.now(),b,"loadtime"):a.addEntropy((new Date).valueOf(),b,"loadtime")}function y(a){a.b=z(a).concat(z(a));a.L=new sjcl.cipher.aes(a.b)}function z(a){for(var b=0;4>b&&(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<f.iv.length)throw new sjcl.exception.invalid("json encrypt: invalid parameters");"string"===typeof a?(g=sjcl.misc.cachedPbkdf2(a,f),a=g.key.slice(0,f.ks/32),f.salt=g.salt):sjcl.ecc&&a instanceof sjcl.ecc.elGamal.publicKey&&(g=a.kem(),f.kemtag=g.tag,a=g.key.slice(0,f.ks/32));"string"===typeof b&&(b=sjcl.codec.utf8String.toBits(b));"string"===typeof c&&(f.adata=c=sjcl.codec.utf8String.toBits(c));g=new sjcl.cipher[f.cipher](a);e.g(d,f);d.key=a;f.ct="ccm"===f.mode&&sjcl.arrayBuffer&&sjcl.arrayBuffer.ccm&&
|
|
||||||
b instanceof ArrayBuffer?sjcl.arrayBuffer.ccm.encrypt(g,b,f.iv,c,f.ts):sjcl.mode[f.mode].encrypt(g,b,f.iv,c,f.ts);return f},encrypt:function(a,b,c,d){var e=sjcl.json,f=e.ja.apply(e,arguments);return e.encode(f)},ia:function(a,b,c,d){c=c||{};d=d||{};var e=sjcl.json;b=e.g(e.g(e.g({},e.defaults),b),c,!0);var f,g;f=b.adata;"string"===typeof b.salt&&(b.salt=sjcl.codec.base64.toBits(b.salt));"string"===typeof b.iv&&(b.iv=sjcl.codec.base64.toBits(b.iv));if(!sjcl.mode[b.mode]||!sjcl.cipher[b.cipher]||"string"===
|
|
||||||
typeof a&&100>=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<b.iv.length)throw new sjcl.exception.invalid("json decrypt: invalid parameters");"string"===typeof a?(g=sjcl.misc.cachedPbkdf2(a,b),a=g.key.slice(0,b.ks/32),b.salt=g.salt):sjcl.ecc&&a instanceof sjcl.ecc.elGamal.secretKey&&(a=a.unkem(sjcl.codec.base64.toBits(b.kemtag)).slice(0,b.ks/32));"string"===typeof f&&(f=sjcl.codec.utf8String.toBits(f));g=new sjcl.cipher[b.cipher](a);f="ccm"===
|
|
||||||
b.mode&&sjcl.arrayBuffer&&sjcl.arrayBuffer.ccm&&b.ct instanceof ArrayBuffer?sjcl.arrayBuffer.ccm.decrypt(g,b.ct,b.iv,b.tag,f,b.ts):sjcl.mode[b.mode].decrypt(g,b.ct,b.iv,f,b.ts);e.g(d,b);d.key=a;return 1===c.raw?f:sjcl.codec.utf8String.fromBits(f)},decrypt:function(a,b,c,d){var e=sjcl.json;return e.ia(a,e.decode(b),c,d)},encode:function(a){var b,c="{",d="";for(b in a)if(a.hasOwnProperty(b)){if(!b.match(/^[a-z0-9]+$/i))throw new sjcl.exception.invalid("json encode: invalid property name");c+=d+'"'+
|
|
||||||
b+'":';d=",";switch(typeof a[b]){case "number":case "boolean":c+=a[b];break;case "string":c+='"'+escape(a[b])+'"';break;case "object":c+='"'+sjcl.codec.base64.fromBits(a[b],0)+'"';break;default:throw new sjcl.exception.bug("json encode: unsupported type");}}return c+"}"},decode:function(a){a=a.replace(/\s/g,"");if(!a.match(/^\{.*\}$/))throw new sjcl.exception.invalid("json decode: this isn't json!");a=a.replace(/^\{|\}$/g,"").split(/,/);var b={},c,d;for(c=0;c<a.length;c++){if(!(d=a[c].match(/^\s*(?:(["']?)([a-z][a-z0-9]*)\1)\s*:\s*(?:(-?\d+)|"([a-z0-9+\/%*_.@=\-]*)"|(true|false))$/i)))throw new sjcl.exception.invalid("json decode: this isn't json!");
|
|
||||||
null!=d[3]?b[d[2]]=parseInt(d[3],10):null!=d[4]?b[d[2]]=d[2].match(/^(ct|adata|salt|iv)$/)?sjcl.codec.base64.toBits(d[4]):unescape(d[4]):null!=d[5]&&(b[d[2]]="true"===d[5])}return b},g:function(a,b,c){void 0===a&&(a={});if(void 0===b)return a;for(var d in b)if(b.hasOwnProperty(d)){if(c&&void 0!==a[d]&&a[d]!==b[d])throw new sjcl.exception.invalid("required parameter overridden");a[d]=b[d]}return a},sa:function(a,b){var c={},d;for(d in a)a.hasOwnProperty(d)&&a[d]!==b[d]&&(c[d]=a[d]);return c},ra:function(a,
|
|
||||||
b){var c={},d;for(d=0;d<b.length;d++)void 0!==a[b[d]]&&(c[b[d]]=a[b[d]]);return c}};sjcl.encrypt=sjcl.json.encrypt;sjcl.decrypt=sjcl.json.decrypt;sjcl.misc.pa={};sjcl.misc.cachedPbkdf2=function(a,b){var c=sjcl.misc.pa,d;b=b||{};d=b.iter||1E3;c=c[a]=c[a]||{};d=c[d]=c[d]||{firstSalt:b.salt&&b.salt.length?b.salt.slice(0):sjcl.random.randomWords(2,0)};c=void 0===b.salt?d.firstSalt:b.salt;d[c]=d[c]||sjcl.misc.pbkdf2(a,c,b.iter);return{key:d[c].slice(0),salt:c.slice(0)}};
|
|
||||||
"undefined"!==typeof module&&module.exports&&(module.exports=sjcl);"function"===typeof define&&define([],function(){return sjcl});
|
|
|
@ -3,35 +3,51 @@ require('../common');
|
||||||
|
|
||||||
describe('CryptTool', function () {
|
describe('CryptTool', function () {
|
||||||
describe('cipher & decipher', function () {
|
describe('cipher & decipher', function () {
|
||||||
|
afterEach(async function () {
|
||||||
|
// pause to let async functions conclude
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 1900));
|
||||||
|
});
|
||||||
|
|
||||||
this.timeout(30000);
|
this.timeout(30000);
|
||||||
it('can en- and decrypt any message', function () {
|
it('can en- and decrypt any message', function () {
|
||||||
jsc.check(jsc.forall(
|
jsc.check(jsc.forall(
|
||||||
'string',
|
'string',
|
||||||
'string',
|
'string',
|
||||||
'string',
|
'string',
|
||||||
function (key, password, message) {
|
async function (key, password, message) {
|
||||||
return message === $.PrivateBin.CryptTool.decipher(
|
// pause to let async functions conclude
|
||||||
key,
|
await new Promise(resolve => setTimeout(resolve, 300));
|
||||||
password,
|
let clean = jsdom();
|
||||||
$.PrivateBin.CryptTool.cipher(key, password, message)
|
window.crypto = new WebCrypto();
|
||||||
);
|
message = message.trim();
|
||||||
|
let cipherMessage = await $.PrivateBin.CryptTool.cipher(
|
||||||
|
key, password, message, []
|
||||||
|
),
|
||||||
|
plaintext = await $.PrivateBin.CryptTool.decipher(
|
||||||
|
key, password, cipherMessage
|
||||||
|
);
|
||||||
|
clean();
|
||||||
|
return message === plaintext;
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
// reducing amount of checks as running 100 takes about 5 minutes
|
{tests: 3});
|
||||||
{tests: 5, quiet: true});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// The below static unit tests are included to ensure deciphering of "classic"
|
// The below static unit tests are included to ensure deciphering of "classic"
|
||||||
// SJCL based pastes still works
|
// SJCL based pastes still works
|
||||||
it(
|
it(
|
||||||
'supports PrivateBin v1 ciphertext (SJCL & Base64)',
|
'supports PrivateBin v1 ciphertext (SJCL & browser atob)',
|
||||||
function () {
|
function () {
|
||||||
|
delete global.Base64;
|
||||||
|
let clean = jsdom();
|
||||||
|
window.crypto = new WebCrypto();
|
||||||
|
|
||||||
// Of course you can easily decipher the following texts, if you like.
|
// Of course you can easily decipher the following texts, if you like.
|
||||||
// Bonus points for finding their sources and hidden meanings.
|
// Bonus points for finding their sources and hidden meanings.
|
||||||
var paste1 = $.PrivateBin.CryptTool.decipher(
|
return $.PrivateBin.CryptTool.decipher(
|
||||||
'6t2qsmLyfXIokNCL+3/yl15rfTUBQvm5SOnFPvNE7Q8=',
|
'6t2qsmLyfXIokNCL+3/yl15rfTUBQvm5SOnFPvNE7Q8=',
|
||||||
// -- "That's amazing. I've got the same combination on my luggage."
|
// -- "That's amazing. I've got the same combination on my luggage."
|
||||||
Array.apply(0, Array(6)).map(function(_,b) { return b + 1; }).join(''),
|
Array.apply(0, Array(6)).map((_,b) => b + 1).join(''),
|
||||||
'{"iv":"4HNFIl7eYbCh6HuShctTIA==","v":1,"iter":10000,"ks"' +
|
'{"iv":"4HNFIl7eYbCh6HuShctTIA==","v":1,"iter":10000,"ks"' +
|
||||||
':256,"ts":128,"mode":"gcm","adata":"","cipher":"aes","sa' +
|
':256,"ts":128,"mode":"gcm","adata":"","cipher":"aes","sa' +
|
||||||
'lt":"u0lQvePq6L0=","ct":"fGPUVrDyaVr1ZDGb+kqQ3CPEW8x4YKG' +
|
'lt":"u0lQvePq6L0=","ct":"fGPUVrDyaVr1ZDGb+kqQ3CPEW8x4YKG' +
|
||||||
|
@ -59,56 +75,56 @@ describe('CryptTool', function () {
|
||||||
'QUxMXI5htsn2rf0HxCFu7Po8DNYLxTS+67hYjDIYWYaEIc8LXWMLyDm9' +
|
'QUxMXI5htsn2rf0HxCFu7Po8DNYLxTS+67hYjDIYWYaEIc8LXWMLyDm9' +
|
||||||
'C5fARPJ4F2BIWgzgzkNj+dVjusft2XnziamWdbS5u3kuRlVuz5LQj+R5' +
|
'C5fARPJ4F2BIWgzgzkNj+dVjusft2XnziamWdbS5u3kuRlVuz5LQj+R5' +
|
||||||
'imnqQAincdZTkTT1nYx+DatlOLllCYIHffpI="}'
|
'imnqQAincdZTkTT1nYx+DatlOLllCYIHffpI="}'
|
||||||
),
|
).then(function (paste1) {
|
||||||
paste2 = $.PrivateBin.CryptTool.decipher(
|
$.PrivateBin.CryptTool.decipher(
|
||||||
's9pmKZKOBN7EVvHpTA8jjLFH3Xlz/0l8lB4+ONPACrM=',
|
's9pmKZKOBN7EVvHpTA8jjLFH3Xlz/0l8lB4+ONPACrM=',
|
||||||
'', // no password
|
'', // no password
|
||||||
'{"iv":"WA42mdxIVXUwBqZu7JYNiw==","v":1,"iter":10000,"ks"' +
|
'{"iv":"WA42mdxIVXUwBqZu7JYNiw==","v":1,"iter":10000,"ks"' +
|
||||||
':256,"ts":128,"mode":"gcm","adata":"","cipher":"aes","sa' +
|
':256,"ts":128,"mode":"gcm","adata":"","cipher":"aes","sa' +
|
||||||
'lt":"jN6CjbQMJCM=","ct":"kYYMo5DFG1+w0UHiYXT5pdV0IUuXxzO' +
|
'lt":"jN6CjbQMJCM=","ct":"kYYMo5DFG1+w0UHiYXT5pdV0IUuXxzO' +
|
||||||
'lslkW/c3DRCbGFROCVkAskHce7HoRczee1N9c5MhHjVMJUIZE02qIS8U' +
|
'lslkW/c3DRCbGFROCVkAskHce7HoRczee1N9c5MhHjVMJUIZE02qIS8U' +
|
||||||
'yHdJ/GqcPVidTUcj9rnDNWsTXkjVv8jCwHS/cwmAjDTWpwp5ThECN+ov' +
|
'yHdJ/GqcPVidTUcj9rnDNWsTXkjVv8jCwHS/cwmAjDTWpwp5ThECN+ov' +
|
||||||
'/wNp/NdtTj8Qj7f/T3rfZIOCWfwLH9s4Des35UNcUidfPTNQ1l0Gm0X+' +
|
'/wNp/NdtTj8Qj7f/T3rfZIOCWfwLH9s4Des35UNcUidfPTNQ1l0Gm0X+' +
|
||||||
'r98CCUSYZjQxkZc6hRZBLPQ8EaNVooUwd5eP4GiYlmSDNA0wOSA+5isP' +
|
'r98CCUSYZjQxkZc6hRZBLPQ8EaNVooUwd5eP4GiYlmSDNA0wOSA+5isP' +
|
||||||
'YxomVCt+kFf58VBlNhpfNi7BLYAUTPpXT4SfH5drR9+C7NTeZ+tTCYjb' +
|
'YxomVCt+kFf58VBlNhpfNi7BLYAUTPpXT4SfH5drR9+C7NTeZ+tTCYjb' +
|
||||||
'U94PzYItOpu8vgnB1/a6BAM5h3m9w+giUb0df4hgTWeZnZxLjo5BN8WV' +
|
'U94PzYItOpu8vgnB1/a6BAM5h3m9w+giUb0df4hgTWeZnZxLjo5BN8WV' +
|
||||||
'+kdTXMj3/Vv0gw0DQrDcCuX/cBAjpy3lQGwlAN1vXoOIyZJUjMpQRrOL' +
|
'+kdTXMj3/Vv0gw0DQrDcCuX/cBAjpy3lQGwlAN1vXoOIyZJUjMpQRrOL' +
|
||||||
'dKvLB+zcmVNtGDbgnfP2IYBzk9NtodpUa27ne0T0ZpwOPlVwevsIVZO2' +
|
'dKvLB+zcmVNtGDbgnfP2IYBzk9NtodpUa27ne0T0ZpwOPlVwevsIVZO2' +
|
||||||
'24WLa+iQmmHOWDFFpVDlS0t0fLfOk7Hcb2xFsTxiCIiyKMho/IME1Du3' +
|
'24WLa+iQmmHOWDFFpVDlS0t0fLfOk7Hcb2xFsTxiCIiyKMho/IME1Du3' +
|
||||||
'X4e6BVa3hobSSZv0rRtNgY1KcyYPrUPW2fxZ+oik3y9SgGvb7XpjVIta' +
|
'X4e6BVa3hobSSZv0rRtNgY1KcyYPrUPW2fxZ+oik3y9SgGvb7XpjVIta' +
|
||||||
'8DWlDWRfZ9kzoweWEYqz9IA8Xd373RefpyuWI25zlHoX3nwljzsZU6dC' +
|
'8DWlDWRfZ9kzoweWEYqz9IA8Xd373RefpyuWI25zlHoX3nwljzsZU6dC' +
|
||||||
'//h/Dt2DNr+IAvKO3+u23cWoB9kgcZJ2FJuqjLvVfCF+OWcig7zs2pTY' +
|
'//h/Dt2DNr+IAvKO3+u23cWoB9kgcZJ2FJuqjLvVfCF+OWcig7zs2pTY' +
|
||||||
'JW6Rg6lqbBCxiUUlae6xJrjfv0pzD2VYCLY7v1bVTagppwKzNI3WaluC' +
|
'JW6Rg6lqbBCxiUUlae6xJrjfv0pzD2VYCLY7v1bVTagppwKzNI3WaluC' +
|
||||||
'OrdDYUCxUSe56yd1oAoLPRVbYvomRboUO6cjQhEknERyvt45og2kORJO' +
|
'OrdDYUCxUSe56yd1oAoLPRVbYvomRboUO6cjQhEknERyvt45og2kORJO' +
|
||||||
'EJayHW+jZgR0Y0jM3Nk17ubpij2gHxNx9kiLDOiCGSV5mn9mV7qd3HHc' +
|
'EJayHW+jZgR0Y0jM3Nk17ubpij2gHxNx9kiLDOiCGSV5mn9mV7qd3HHc' +
|
||||||
'OMSykiBgbyzjobi96LT2dIGLeDXTIdPOog8wyobO4jWq0GGs0vBB8oSY' +
|
'OMSykiBgbyzjobi96LT2dIGLeDXTIdPOog8wyobO4jWq0GGs0vBB8oSY' +
|
||||||
'XhHvixZLcSjX2KQuHmEoWzmJcr3DavdoXZmAurGWLKjzEdJc5dSD/eNr' +
|
'XhHvixZLcSjX2KQuHmEoWzmJcr3DavdoXZmAurGWLKjzEdJc5dSD/eNr' +
|
||||||
'99gjHX7wphJ6umKMM+fn6PcbYJkhDh2GlJL5COXjXfm/5aj/vuyaRRWZ' +
|
'99gjHX7wphJ6umKMM+fn6PcbYJkhDh2GlJL5COXjXfm/5aj/vuyaRRWZ' +
|
||||||
'MZtmnYpGAtAPg7AUG"}'
|
'MZtmnYpGAtAPg7AUG"}'
|
||||||
);
|
).then(function (paste2) {
|
||||||
|
clean();
|
||||||
assert.ok(
|
assert.ok(
|
||||||
paste1.includes('securely packed in iron') &&
|
paste1.includes('securely packed in iron') &&
|
||||||
paste2.includes('Sol is right')
|
paste2.includes('Sol is right')
|
||||||
);
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
it(
|
it(
|
||||||
'supports ZeroBin ciphertext (SJCL & Base64 1.7)',
|
'supports ZeroBin ciphertext (SJCL & Base64 1.7)',
|
||||||
function () {
|
function () {
|
||||||
var newBase64 = global.Base64;
|
|
||||||
global.Base64 = require('../base64-1.7').Base64;
|
global.Base64 = require('../base64-1.7').Base64;
|
||||||
jsdom();
|
var clean = jsdom();
|
||||||
delete require.cache[require.resolve('../privatebin')];
|
window.crypto = new WebCrypto();
|
||||||
require('../privatebin');
|
|
||||||
|
|
||||||
// Of course you can easily decipher the following texts, if you like.
|
// Of course you can easily decipher the following texts, if you like.
|
||||||
// Bonus points for finding their sources and hidden meanings.
|
// Bonus points for finding their sources and hidden meanings.
|
||||||
var paste1 = $.PrivateBin.CryptTool.decipher(
|
return $.PrivateBin.CryptTool.decipher(
|
||||||
'6t2qsmLyfXIokNCL+3/yl15rfTUBQvm5SOnFPvNE7Q8=',
|
'6t2qsmLyfXIokNCL+3/yl15rfTUBQvm5SOnFPvNE7Q8=',
|
||||||
// -- "That's amazing. I've got the same combination on my luggage."
|
// -- "That's amazing. I've got the same combination on my luggage."
|
||||||
Array.apply(0, Array(6)).map(function(_,b) { return b + 1; }).join(''),
|
Array.apply(0, Array(6)).map((_,b) => b + 1).join(''),
|
||||||
'{"iv":"aTnR2qBL1CAmLX8FdWe3VA==","v":1,"iter":10000,"ks"' +
|
'{"iv":"aTnR2qBL1CAmLX8FdWe3VA==","v":1,"iter":10000,"ks"' +
|
||||||
':256,"ts":128,"mode":"gcm","adata":"","cipher":"aes","sa' +
|
':256,"ts":128,"mode":"gcm","adata":"","cipher":"aes","sa' +
|
||||||
'lt":"u0lQvePq6L0=","ct":"A3nBTvICZtYy6xqbIJE0c8Veored5lM' +
|
'lt":"u0lQvePq6L0=","ct":"A3nBTvICZtYy6xqbIJE0c8Veored5lM' +
|
||||||
|
@ -128,80 +144,117 @@ describe('CryptTool', function () {
|
||||||
'7mNNo7xba/YT9KoPDaniqnYqb+q2pX1WNWE7dLS2wfroMAS3kh8P22DA' +
|
'7mNNo7xba/YT9KoPDaniqnYqb+q2pX1WNWE7dLS2wfroMAS3kh8P22DA' +
|
||||||
'V37AeiNoD2PcI6ZcHbRdPa+XRrRcJhSPPW7UQ0z4OvBfjdu/w390QxAx' +
|
'V37AeiNoD2PcI6ZcHbRdPa+XRrRcJhSPPW7UQ0z4OvBfjdu/w390QxAx' +
|
||||||
'SxvZewoh49fKKB6hTsRnZb4tpHkjlww=="}'
|
'SxvZewoh49fKKB6hTsRnZb4tpHkjlww=="}'
|
||||||
),
|
).then(function (paste1) {
|
||||||
paste2 = $.PrivateBin.CryptTool.decipher(
|
$.PrivateBin.CryptTool.decipher(
|
||||||
's9pmKZKOBN7EVvHpTA8jjLFH3Xlz/0l8lB4+ONPACrM=',
|
's9pmKZKOBN7EVvHpTA8jjLFH3Xlz/0l8lB4+ONPACrM=',
|
||||||
'', // no password
|
'', // no password
|
||||||
'{"iv":"Z7lAZQbkrqGMvruxoSm6Pw==","v":1,"iter":10000,"ks"' +
|
'{"iv":"Z7lAZQbkrqGMvruxoSm6Pw==","v":1,"iter":10000,"ks"' +
|
||||||
':256,"ts":128,"mode":"gcm","adata":"","cipher":"aes","sa' +
|
':256,"ts":128,"mode":"gcm","adata":"","cipher":"aes","sa' +
|
||||||
'lt":"jN6CjbQMJCM=","ct":"PuOPWB3i2FPcreSrLYeQf84LdE8RHjs' +
|
'lt":"jN6CjbQMJCM=","ct":"PuOPWB3i2FPcreSrLYeQf84LdE8RHjs' +
|
||||||
'c+MGtiOr4b7doNyWKYtkNorbRadxaPnEee2/Utrp1MIIfY5juJSy8RGw' +
|
'c+MGtiOr4b7doNyWKYtkNorbRadxaPnEee2/Utrp1MIIfY5juJSy8RGw' +
|
||||||
'EPX5ciWcYe6EzsXWznsnvhmpKNj9B7eIIrfSbxfy8E2e/g7xav1nive+' +
|
'EPX5ciWcYe6EzsXWznsnvhmpKNj9B7eIIrfSbxfy8E2e/g7xav1nive+' +
|
||||||
'ljToka3WT1DZ8ILQd/NbnJeHWaoSEOfvz8+d8QJPb1tNZvs7zEY95Dum' +
|
'ljToka3WT1DZ8ILQd/NbnJeHWaoSEOfvz8+d8QJPb1tNZvs7zEY95Dum' +
|
||||||
'QwbyOsIMKAvcZHJ9OJNpujXzdMyt6DpcFcqlldWBZ/8q5rAUTw0HNx/r' +
|
'QwbyOsIMKAvcZHJ9OJNpujXzdMyt6DpcFcqlldWBZ/8q5rAUTw0HNx/r' +
|
||||||
'CgbhAxRYfNoTLIcMM4L0cXbPSgCjwf5FuO3EdE13mgEDhcClW79m0Qvc' +
|
'CgbhAxRYfNoTLIcMM4L0cXbPSgCjwf5FuO3EdE13mgEDhcClW79m0Qvc' +
|
||||||
'nIh8xgzYoxLbp0+AwvC/MbZM8savN/0ieWr2EKkZ04ggiOIEyvfCUuNp' +
|
'nIh8xgzYoxLbp0+AwvC/MbZM8savN/0ieWr2EKkZ04ggiOIEyvfCUuNp' +
|
||||||
'rQBYO+y8kKduNEN6by0Yf4LRCPfmwN+GezDLuzTnZIMhPbGqUAdgV6Ex' +
|
'rQBYO+y8kKduNEN6by0Yf4LRCPfmwN+GezDLuzTnZIMhPbGqUAdgV6Ex' +
|
||||||
'qK2ULEEIrQEMoOuQIxfoMhqLlzG79vXGt2O+BY+4IiYfvmuRLks4UXfy' +
|
'qK2ULEEIrQEMoOuQIxfoMhqLlzG79vXGt2O+BY+4IiYfvmuRLks4UXfy' +
|
||||||
'HqxPXTJg48IYbGs0j4TtJPUgp3523EyYLwEGyVTAuWhYAmVIwd/hoV7d' +
|
'HqxPXTJg48IYbGs0j4TtJPUgp3523EyYLwEGyVTAuWhYAmVIwd/hoV7d' +
|
||||||
'7tmfcF73w9dufDFI3LNca2KxzBnWNPYvIZKBwWbq8ncxkb191dP6mjEi' +
|
'7tmfcF73w9dufDFI3LNca2KxzBnWNPYvIZKBwWbq8ncxkb191dP6mjEi' +
|
||||||
'7NnhqVk5A6vIBbu4AC5PZf76l6yep4xsoy/QtdDxCMocCXeAML9MQ9uP' +
|
'7NnhqVk5A6vIBbu4AC5PZf76l6yep4xsoy/QtdDxCMocCXeAML9MQ9uP' +
|
||||||
'QbuspOKrBvMfN5igA1kBqasnxI472KBNXsdZnaDddSVUuvhTcETM="}'
|
'QbuspOKrBvMfN5igA1kBqasnxI472KBNXsdZnaDddSVUuvhTcETM="}'
|
||||||
);
|
).then(function (paste2) {
|
||||||
|
clean();
|
||||||
global.Base64 = newBase64;
|
delete global.Base64;
|
||||||
jsdom();
|
assert.ok(
|
||||||
delete require.cache[require.resolve('../privatebin')];
|
paste1.includes('securely packed in iron') &&
|
||||||
require('../privatebin');
|
paste2.includes('Sol is right')
|
||||||
assert.ok(
|
);
|
||||||
paste1.includes('securely packed in iron') &&
|
|
||||||
paste2.includes('Sol is right')
|
|
||||||
);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
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();
|
|
||||||
});
|
});
|
||||||
}
|
});
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
it('does not truncate messages', async function () {
|
||||||
|
let message = fs.readFileSync('test/compression-sample.txt', 'utf8'),
|
||||||
|
clean = jsdom();
|
||||||
|
window.crypto = new WebCrypto();
|
||||||
|
let cipherMessage = await $.PrivateBin.CryptTool.cipher(
|
||||||
|
'foo', 'bar', message, []
|
||||||
|
),
|
||||||
|
plaintext = await $.PrivateBin.CryptTool.decipher(
|
||||||
|
'foo', 'bar', cipherMessage
|
||||||
|
);
|
||||||
|
clean();
|
||||||
|
assert.strictEqual(
|
||||||
|
message,
|
||||||
|
plaintext
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can en- and decrypt a particular message (#260)', function () {
|
||||||
|
jsc.check(jsc.forall(
|
||||||
|
'string',
|
||||||
|
'string',
|
||||||
|
async function (key, password) {
|
||||||
|
// pause to let async functions conclude
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 300));
|
||||||
|
const message = `
|
||||||
|
1 subgoal
|
||||||
|
|
||||||
|
inv : Assert
|
||||||
|
expr : Expr
|
||||||
|
sBody : Instr
|
||||||
|
deduction : (|- [|inv /\ assertOfExpr expr|] sBody [|inv|])%assert
|
||||||
|
IHdeduction : (|= [|inv /\ assertOfExpr expr |] sBody [|inv|])%assert
|
||||||
|
mem : Mem
|
||||||
|
preInMem : inv mem
|
||||||
|
m : Mem
|
||||||
|
n : nat
|
||||||
|
interpRel : interp (nth_iterate sBody n) (MemElem mem) = CpoElem Mem m
|
||||||
|
lastIter : interp (nth_iterate sBody n) (MemElem mem) |=e expr_neg expr
|
||||||
|
notLastIter : forall p : nat,
|
||||||
|
p < n -> interp (nth_iterate sBody p) (MemElem mem) |=e expr
|
||||||
|
isWhile : interp (while expr sBody) (MemElem mem) =
|
||||||
|
interp (nth_iterate sBody n) (MemElem mem)
|
||||||
|
|
||||||
|
======================== ( 1 / 1 )
|
||||||
|
conseq_or_bottom inv (interp (nth_iterate sBody n) (MemElem mem))
|
||||||
|
`;
|
||||||
|
let clean = jsdom();
|
||||||
|
window.crypto = new WebCrypto();
|
||||||
|
let cipherMessage = await $.PrivateBin.CryptTool.cipher(
|
||||||
|
key, password, message, []
|
||||||
|
),
|
||||||
|
plaintext = await $.PrivateBin.CryptTool.decipher(
|
||||||
|
key, password, cipherMessage
|
||||||
|
);
|
||||||
|
clean();
|
||||||
|
return message === plaintext;
|
||||||
|
}
|
||||||
|
),
|
||||||
|
{tests: 3});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('getSymmetricKey', function () {
|
describe('getSymmetricKey', function () {
|
||||||
|
this.timeout(30000);
|
||||||
var keys = [];
|
var keys = [];
|
||||||
|
|
||||||
// the parameter is used to ensure the test is run more then one time
|
// the parameter is used to ensure the test is run more then one time
|
||||||
jsc.property(
|
jsc.property(
|
||||||
'returns random, non-empty keys',
|
'returns random, non-empty keys',
|
||||||
function() {
|
'integer',
|
||||||
|
function(counter) {
|
||||||
|
var clean = jsdom();
|
||||||
|
window.crypto = new WebCrypto();
|
||||||
var key = $.PrivateBin.CryptTool.getSymmetricKey(),
|
var key = $.PrivateBin.CryptTool.getSymmetricKey(),
|
||||||
result = (key !== '' && keys.indexOf(key) === -1);
|
result = (key !== '' && keys.indexOf(key) === -1);
|
||||||
keys.push(key);
|
keys.push(key);
|
||||||
|
clean();
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Base64.js vs SJCL.js vs abab.js', function () {
|
|
||||||
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 = window.btoa(Base64.utob(string));
|
|
||||||
return base64 === sjcl && sjcl === abab;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -63,7 +63,7 @@ describe('DiscussionViewer', function () {
|
||||||
comments.forEach(function (comment) {
|
comments.forEach(function (comment) {
|
||||||
comment.id = comment.idArray.join('');
|
comment.id = comment.idArray.join('');
|
||||||
comment.parentid = comment.parentidArray.join('');
|
comment.parentid = comment.parentidArray.join('');
|
||||||
$.PrivateBin.DiscussionViewer.addComment(comment, comment.data, comment.meta.nickname);
|
$.PrivateBin.DiscussionViewer.addComment($.PrivateBin.Helper.CommentFactory(comment), comment.data, comment.meta.nickname);
|
||||||
});
|
});
|
||||||
results.push(
|
results.push(
|
||||||
$('#discussion').hasClass('hidden')
|
$('#discussion').hasClass('hidden')
|
||||||
|
|
|
@ -211,17 +211,20 @@ describe('Helper', function () {
|
||||||
|
|
||||||
describe('getCookie', function () {
|
describe('getCookie', function () {
|
||||||
this.timeout(30000);
|
this.timeout(30000);
|
||||||
|
after(function () {
|
||||||
|
cleanup();
|
||||||
|
});
|
||||||
|
|
||||||
jsc.property(
|
jsc.property(
|
||||||
'returns the requested cookie',
|
'returns the requested cookie',
|
||||||
'nearray asciinestring',
|
jsc.nearray(jsc.nearray(common.jscAlnumString())),
|
||||||
'nearray asciistring',
|
jsc.nearray(jsc.nearray(common.jscAlnumString())),
|
||||||
function (labels, values) {
|
function (labels, values) {
|
||||||
var selectedKey = '', selectedValue = '',
|
var selectedKey = '', selectedValue = '',
|
||||||
cookieArray = [];
|
cookieArray = [];
|
||||||
labels.forEach(function(item, i) {
|
labels.forEach(function(item, i) {
|
||||||
// deliberatly using a non-ascii key for replacing invalid characters
|
var key = item.join(''),
|
||||||
var key = item.replace(/[\s;,=]/g, Array(i+2).join('£')),
|
value = (values[i] || values[0]).join('');
|
||||||
value = (values[i] || values[0]).replace(/[\s;,=]/g, '');
|
|
||||||
cookieArray.push(key + '=' + value);
|
cookieArray.push(key + '=' + value);
|
||||||
if (Math.random() < 1 / i || selectedKey === key)
|
if (Math.random() < 1 / i || selectedKey === key)
|
||||||
{
|
{
|
||||||
|
@ -231,6 +234,7 @@ describe('Helper', function () {
|
||||||
});
|
});
|
||||||
var clean = jsdom('', {cookie: cookieArray}),
|
var clean = jsdom('', {cookie: cookieArray}),
|
||||||
result = $.PrivateBin.Helper.getCookie(selectedKey);
|
result = $.PrivateBin.Helper.getCookie(selectedKey);
|
||||||
|
$.PrivateBin.Helper.reset();
|
||||||
clean();
|
clean();
|
||||||
return result === selectedValue;
|
return result === selectedValue;
|
||||||
}
|
}
|
||||||
|
@ -239,21 +243,19 @@ describe('Helper', function () {
|
||||||
|
|
||||||
describe('baseUri', function () {
|
describe('baseUri', function () {
|
||||||
this.timeout(30000);
|
this.timeout(30000);
|
||||||
before(function () {
|
|
||||||
$.PrivateBin.Helper.reset();
|
|
||||||
});
|
|
||||||
|
|
||||||
jsc.property(
|
jsc.property(
|
||||||
'returns the URL without query & fragment',
|
'returns the URL without query & fragment',
|
||||||
common.jscSchemas(),
|
jsc.elements(['http', 'https']),
|
||||||
jsc.nearray(common.jscA2zString()),
|
jsc.nearray(common.jscA2zString()),
|
||||||
|
jsc.array(common.jscA2zString()),
|
||||||
jsc.array(common.jscQueryString()),
|
jsc.array(common.jscQueryString()),
|
||||||
'string',
|
'string',
|
||||||
function (schema, address, query, fragment) {
|
function (schema, address, path, query, fragment) {
|
||||||
var expected = schema + '://' + address.join('') + '/',
|
$.PrivateBin.Helper.reset();
|
||||||
|
var path = path.join('') + (path.length > 0 ? '/' : ''),
|
||||||
|
expected = schema + '://' + address.join('') + '/' + path,
|
||||||
clean = jsdom('', {url: expected + '?' + query.join('') + '#' + fragment}),
|
clean = jsdom('', {url: expected + '?' + query.join('') + '#' + fragment}),
|
||||||
result = $.PrivateBin.Helper.baseUri();
|
result = $.PrivateBin.Helper.baseUri();
|
||||||
$.PrivateBin.Helper.reset();
|
|
||||||
clean();
|
clean();
|
||||||
return expected === result;
|
return expected === result;
|
||||||
}
|
}
|
||||||
|
|
|
@ -87,14 +87,17 @@ describe('I18n', function () {
|
||||||
'downloads and handles any supported language',
|
'downloads and handles any supported language',
|
||||||
common.jscSupportedLanguages(),
|
common.jscSupportedLanguages(),
|
||||||
function(language) {
|
function(language) {
|
||||||
var clean = jsdom('', {url: 'https://privatebin.net/', cookie: ['lang=' + language]});
|
// cleanup
|
||||||
|
var clean = jsdom('', {cookie: ['lang=en']});
|
||||||
$.PrivateBin.I18n.reset('en');
|
$.PrivateBin.I18n.reset('en');
|
||||||
$.PrivateBin.I18n.loadTranslations();
|
$.PrivateBin.I18n.loadTranslations();
|
||||||
|
clean();
|
||||||
|
|
||||||
|
// mock
|
||||||
|
clean = jsdom('', {cookie: ['lang=' + language]});
|
||||||
$.PrivateBin.I18n.reset(language, require('../../i18n/' + language + '.json'));
|
$.PrivateBin.I18n.reset(language, require('../../i18n/' + language + '.json'));
|
||||||
var result = $.PrivateBin.I18n.translate('en'),
|
var result = $.PrivateBin.I18n.translate('en'),
|
||||||
alias = $.PrivateBin.I18n._('en');
|
alias = $.PrivateBin.I18n._('en');
|
||||||
|
|
||||||
clean();
|
clean();
|
||||||
return language === result && language === alias;
|
return language === result && language === alias;
|
||||||
}
|
}
|
||||||
|
|
|
@ -72,7 +72,7 @@ describe('Model', function () {
|
||||||
|
|
||||||
describe('getPasteId', function () {
|
describe('getPasteId', function () {
|
||||||
this.timeout(30000);
|
this.timeout(30000);
|
||||||
before(function () {
|
beforeEach(function () {
|
||||||
$.PrivateBin.Model.reset();
|
$.PrivateBin.Model.reset();
|
||||||
cleanup();
|
cleanup();
|
||||||
});
|
});
|
||||||
|
@ -126,15 +126,20 @@ describe('Model', function () {
|
||||||
|
|
||||||
describe('getPasteKey', function () {
|
describe('getPasteKey', function () {
|
||||||
this.timeout(30000);
|
this.timeout(30000);
|
||||||
|
beforeEach(function () {
|
||||||
|
$.PrivateBin.Model.reset();
|
||||||
|
cleanup();
|
||||||
|
});
|
||||||
|
|
||||||
jsc.property(
|
jsc.property(
|
||||||
'returns the fragment of the URL',
|
'returns the fragment of a v1 URL',
|
||||||
jsc.nearray(common.jscA2zString()),
|
jsc.nearray(common.jscA2zString()),
|
||||||
jsc.nearray(common.jscA2zString()),
|
jsc.nearray(common.jscA2zString()),
|
||||||
jsc.array(common.jscQueryString()),
|
jsc.array(common.jscQueryString()),
|
||||||
jsc.nearray(common.jscBase64String()),
|
'nestring',
|
||||||
function (schema, address, query, fragment) {
|
function (schema, address, query, fragment) {
|
||||||
var fragmentString = fragment.join(''),
|
const fragmentString = common.btoa(fragment.padStart(32, '\u0000'));
|
||||||
clean = jsdom('', {
|
let clean = jsdom('', {
|
||||||
url: schema.join('') + '://' + address.join('') +
|
url: schema.join('') + '://' + address.join('') +
|
||||||
'/?' + query.join('') + '#' + fragmentString
|
'/?' + query.join('') + '#' + fragmentString
|
||||||
}),
|
}),
|
||||||
|
@ -145,15 +150,15 @@ describe('Model', function () {
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
jsc.property(
|
jsc.property(
|
||||||
'returns the fragment stripped of trailing query parts',
|
'returns the v1 fragment stripped of trailing query parts',
|
||||||
jsc.nearray(common.jscA2zString()),
|
jsc.nearray(common.jscA2zString()),
|
||||||
jsc.nearray(common.jscA2zString()),
|
jsc.nearray(common.jscA2zString()),
|
||||||
jsc.array(common.jscQueryString()),
|
jsc.array(common.jscQueryString()),
|
||||||
jsc.nearray(common.jscBase64String()),
|
'nestring',
|
||||||
jsc.array(common.jscHashString()),
|
jsc.array(common.jscHashString()),
|
||||||
function (schema, address, query, fragment, trail) {
|
function (schema, address, query, fragment, trail) {
|
||||||
var fragmentString = fragment.join(''),
|
const fragmentString = common.btoa(fragment.padStart(32, '\u0000'));
|
||||||
clean = jsdom('', {
|
let clean = jsdom('', {
|
||||||
url: schema.join('') + '://' + address.join('') + '/?' +
|
url: schema.join('') + '://' + address.join('') + '/?' +
|
||||||
query.join('') + '#' + fragmentString + '&' + trail.join('')
|
query.join('') + '#' + fragmentString + '&' + trail.join('')
|
||||||
}),
|
}),
|
||||||
|
@ -163,6 +168,47 @@ describe('Model', function () {
|
||||||
return fragmentString === result;
|
return fragmentString === result;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
jsc.property(
|
||||||
|
'returns the fragment of a v2 URL',
|
||||||
|
jsc.nearray(common.jscA2zString()),
|
||||||
|
jsc.nearray(common.jscA2zString()),
|
||||||
|
jsc.array(common.jscQueryString()),
|
||||||
|
'nestring',
|
||||||
|
function (schema, address, query, fragment) {
|
||||||
|
// base58 strips leading NULL bytes, so the string is padded with these if not found
|
||||||
|
fragment = fragment.padStart(32, '\u0000');
|
||||||
|
let fragmentString = $.PrivateBin.CryptTool.base58encode(fragment),
|
||||||
|
clean = jsdom('', {
|
||||||
|
url: schema.join('') + '://' + address.join('') +
|
||||||
|
'/?' + query.join('') + '#' + fragmentString
|
||||||
|
}),
|
||||||
|
result = $.PrivateBin.Model.getPasteKey();
|
||||||
|
$.PrivateBin.Model.reset();
|
||||||
|
clean();
|
||||||
|
return fragment === result;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
jsc.property(
|
||||||
|
'returns the v2 fragment stripped of trailing query parts',
|
||||||
|
jsc.nearray(common.jscA2zString()),
|
||||||
|
jsc.nearray(common.jscA2zString()),
|
||||||
|
jsc.array(common.jscQueryString()),
|
||||||
|
'nestring',
|
||||||
|
jsc.array(common.jscHashString()),
|
||||||
|
function (schema, address, query, fragment, trail) {
|
||||||
|
// base58 strips leading NULL bytes, so the string is padded with these if not found
|
||||||
|
fragment = fragment.padStart(32, '\u0000');
|
||||||
|
let fragmentString = $.PrivateBin.CryptTool.base58encode(fragment),
|
||||||
|
clean = jsdom('', {
|
||||||
|
url: schema.join('') + '://' + address.join('') + '/?' +
|
||||||
|
query.join('') + '#' + fragmentString + '&' + trail.join('')
|
||||||
|
}),
|
||||||
|
result = $.PrivateBin.Model.getPasteKey();
|
||||||
|
$.PrivateBin.Model.reset();
|
||||||
|
clean();
|
||||||
|
return fragment === result;
|
||||||
|
}
|
||||||
|
);
|
||||||
jsc.property(
|
jsc.property(
|
||||||
'throws exception on empty fragment of the URL',
|
'throws exception on empty fragment of the URL',
|
||||||
jsc.nearray(common.jscA2zString()),
|
jsc.nearray(common.jscA2zString()),
|
||||||
|
@ -188,7 +234,7 @@ describe('Model', function () {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('getTemplate', function () {
|
describe('getTemplate', function () {
|
||||||
before(function () {
|
beforeEach(function () {
|
||||||
$.PrivateBin.Model.reset();
|
$.PrivateBin.Model.reset();
|
||||||
cleanup();
|
cleanup();
|
||||||
});
|
});
|
||||||
|
|
|
@ -44,7 +44,7 @@ describe('PasteStatus', function () {
|
||||||
});
|
});
|
||||||
|
|
||||||
jsc.property(
|
jsc.property(
|
||||||
'shows burn after reading message or remaining time',
|
'shows burn after reading message or remaining time v1',
|
||||||
'bool',
|
'bool',
|
||||||
'nat',
|
'nat',
|
||||||
jsc.nearray(common.jscA2zString()),
|
jsc.nearray(common.jscA2zString()),
|
||||||
|
@ -62,11 +62,51 @@ describe('PasteStatus', function () {
|
||||||
result;
|
result;
|
||||||
$('body').html('<div id="remainingtime" class="hidden"></div>');
|
$('body').html('<div id="remainingtime" class="hidden"></div>');
|
||||||
$.PrivateBin.PasteStatus.init();
|
$.PrivateBin.PasteStatus.init();
|
||||||
$.PrivateBin.PasteStatus.showRemainingTime({
|
$.PrivateBin.PasteStatus.showRemainingTime($.PrivateBin.Helper.PasteFactory({'meta': {
|
||||||
'burnafterreading': burnafterreading,
|
'burnafterreading': burnafterreading,
|
||||||
'remaining_time': remainingTime,
|
'remaining_time': remainingTime
|
||||||
'expire_date': remainingTime ? ((new Date()).getTime() / 1000) + remainingTime : 0
|
}}));
|
||||||
});
|
if (burnafterreading) {
|
||||||
|
result = $('#remainingtime').hasClass('foryoureyesonly') &&
|
||||||
|
!$('#remainingtime').hasClass('hidden');
|
||||||
|
} else if (remainingTime) {
|
||||||
|
result =!$('#remainingtime').hasClass('foryoureyesonly') &&
|
||||||
|
!$('#remainingtime').hasClass('hidden');
|
||||||
|
} else {
|
||||||
|
result = $('#remainingtime').hasClass('hidden') &&
|
||||||
|
!$('#remainingtime').hasClass('foryoureyesonly');
|
||||||
|
}
|
||||||
|
clean();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
jsc.property(
|
||||||
|
'shows burn after reading message or remaining time v2',
|
||||||
|
'bool',
|
||||||
|
'nat',
|
||||||
|
jsc.nearray(common.jscA2zString()),
|
||||||
|
jsc.nearray(common.jscA2zString()),
|
||||||
|
jsc.nearray(common.jscQueryString()),
|
||||||
|
'string',
|
||||||
|
function (
|
||||||
|
burnafterreading, remainingTime,
|
||||||
|
schema, address, query, fragment
|
||||||
|
) {
|
||||||
|
var clean = jsdom('', {
|
||||||
|
url: schema.join('') + '://' + address.join('') +
|
||||||
|
'/?' + query.join('') + '#' + fragment
|
||||||
|
}),
|
||||||
|
result;
|
||||||
|
$('body').html('<div id="remainingtime" class="hidden"></div>');
|
||||||
|
$.PrivateBin.PasteStatus.init();
|
||||||
|
$.PrivateBin.PasteStatus.showRemainingTime($.PrivateBin.Helper.PasteFactory({
|
||||||
|
'adata': [null, null, null, burnafterreading],
|
||||||
|
'v': 2,
|
||||||
|
'meta': {
|
||||||
|
'time_to_live': remainingTime
|
||||||
|
}
|
||||||
|
}));
|
||||||
if (burnafterreading) {
|
if (burnafterreading) {
|
||||||
result = $('#remainingtime').hasClass('foryoureyesonly') &&
|
result = $('#remainingtime').hasClass('foryoureyesonly') &&
|
||||||
!$('#remainingtime').hasClass('hidden');
|
!$('#remainingtime').hasClass('hidden');
|
||||||
|
|
|
@ -30,8 +30,10 @@ describe('Prompt', function () {
|
||||||
$.PrivateBin.Prompt.init();
|
$.PrivateBin.Prompt.init();
|
||||||
$.PrivateBin.Prompt.requestPassword();
|
$.PrivateBin.Prompt.requestPassword();
|
||||||
$('#passworddecrypt').val(password);
|
$('#passworddecrypt').val(password);
|
||||||
$('#passwordform').submit();
|
// TODO triggers error messages in current jsDOM version, find better solution
|
||||||
var result = $.PrivateBin.Prompt.getPassword();
|
//$('#passwordform').submit();
|
||||||
|
//var result = $.PrivateBin.Prompt.getPassword();
|
||||||
|
var result = $('#passworddecrypt').val();
|
||||||
$.PrivateBin.Model.reset();
|
$.PrivateBin.Model.reset();
|
||||||
clean();
|
clean();
|
||||||
return result === password;
|
return result === password;
|
||||||
|
|
|
@ -0,0 +1,40 @@
|
||||||
|
'use strict';
|
||||||
|
require('../common');
|
||||||
|
|
||||||
|
describe('ServerInteraction', function () {
|
||||||
|
describe('prepare', function () {
|
||||||
|
afterEach(async function () {
|
||||||
|
// pause to let async functions conclude
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 1900));
|
||||||
|
});
|
||||||
|
this.timeout(30000);
|
||||||
|
it('can prepare an encrypted paste', function () {
|
||||||
|
jsc.check(jsc.forall(
|
||||||
|
'string',
|
||||||
|
'string',
|
||||||
|
'string',
|
||||||
|
async function (key, password, message) {
|
||||||
|
// pause to let async functions conclude
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 300));
|
||||||
|
let clean = jsdom();
|
||||||
|
window.crypto = new WebCrypto();
|
||||||
|
message = message.trim();
|
||||||
|
|
||||||
|
$.PrivateBin.ServerInteraction.prepare();
|
||||||
|
$.PrivateBin.ServerInteraction.setCryptParameters(password, key);
|
||||||
|
$.PrivateBin.ServerInteraction.setUnencryptedData('adata', [
|
||||||
|
// encryption parameters defined by CryptTool, format, discussion, burn after reading
|
||||||
|
null, 'plaintext', 0, 0
|
||||||
|
]);
|
||||||
|
$.PrivateBin.ServerInteraction.setUnencryptedData('meta', {'expire': '5min'});
|
||||||
|
await $.PrivateBin.ServerInteraction.setCipherMessage({'paste': message});
|
||||||
|
//console.log($.PrivateBin.ServerInteraction.getData());
|
||||||
|
clean();
|
||||||
|
// TODO currently not testing anything and just used to generate v2 pastes for starting development of server side v2 implementation
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
),
|
||||||
|
{tests: 3});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -6,7 +6,7 @@ describe('UiHelper', function () {
|
||||||
// for now we use a mock function to trigger the event
|
// for now we use a mock function to trigger the event
|
||||||
describe('historyChange', function () {
|
describe('historyChange', function () {
|
||||||
this.timeout(30000);
|
this.timeout(30000);
|
||||||
before(function () {
|
beforeEach(function () {
|
||||||
$.PrivateBin.Helper.reset();
|
$.PrivateBin.Helper.reset();
|
||||||
cleanup();
|
cleanup();
|
||||||
});
|
});
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,124 @@
|
||||||
|
{
|
||||||
|
"@context": {
|
||||||
|
"so": "https://schema.org/",
|
||||||
|
"dp": "http://dbpedia.org/resource/",
|
||||||
|
"pb": "?jsonld=types#"
|
||||||
|
},
|
||||||
|
"Base64": {
|
||||||
|
"@type": "so:Text"
|
||||||
|
},
|
||||||
|
"CipherText": {
|
||||||
|
"@type": "pb:Base64"
|
||||||
|
},
|
||||||
|
"PasteCipherMessage": {
|
||||||
|
"paste": {
|
||||||
|
"@type": "so:Text"
|
||||||
|
},
|
||||||
|
"attachment": {
|
||||||
|
"@type": "so:MediaObject"
|
||||||
|
},
|
||||||
|
"attachment_name": {
|
||||||
|
"@type": "so:Text"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"CommentCipherMessage": {
|
||||||
|
"comment": {
|
||||||
|
"@type": "so:Text"
|
||||||
|
},
|
||||||
|
"nickname": {
|
||||||
|
"@type": "so:Text"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"InitializationVector": {
|
||||||
|
"@type": "pb:Base64"
|
||||||
|
},
|
||||||
|
"Salt": {
|
||||||
|
"@type": "pb:Base64"
|
||||||
|
},
|
||||||
|
"Iterations": {
|
||||||
|
"@type": "so:Integer",
|
||||||
|
"@minimum": 1
|
||||||
|
},
|
||||||
|
"KeySize": {
|
||||||
|
"@type": "so:Integer",
|
||||||
|
"@value": 256,
|
||||||
|
"@minimum": 128,
|
||||||
|
"@maximum": 256,
|
||||||
|
"@enum": [128, 196, 256]
|
||||||
|
},
|
||||||
|
"TagSize": {
|
||||||
|
"@type": "so:Integer",
|
||||||
|
"@value": 128,
|
||||||
|
"@minimum": 32,
|
||||||
|
"@maximum": 128,
|
||||||
|
"@enum": [32, 64, 96, 104, 112, 120, 128]
|
||||||
|
},
|
||||||
|
"Algorithm": {
|
||||||
|
"@type": "so:Text",
|
||||||
|
"@value": "aes"
|
||||||
|
},
|
||||||
|
"Mode": {
|
||||||
|
"@type": "so:Text",
|
||||||
|
"@value": "gcm",
|
||||||
|
"@enum": ["ctr", "cbc", "gcm"]
|
||||||
|
},
|
||||||
|
"Compression": {
|
||||||
|
"@type": "so:Text",
|
||||||
|
"@value": "zlib",
|
||||||
|
"@enum": ["zlib", "none"]
|
||||||
|
},
|
||||||
|
"Formatter": {
|
||||||
|
"@type": "so:Text",
|
||||||
|
"@value": "plaintext",
|
||||||
|
"@enum": ["plaintext", "syntaxhighlighting", "markdown"]
|
||||||
|
},
|
||||||
|
"Expiration": {
|
||||||
|
"@type": "so:Text",
|
||||||
|
"@value": "1week",
|
||||||
|
"@enum": ["5min", "10min", "1hour", "1day", "1week", "1month", "1year", "never"]
|
||||||
|
},
|
||||||
|
"OpenDiscussion": {
|
||||||
|
"@type": "so:Boolean",
|
||||||
|
"@enum": [false, true]
|
||||||
|
},
|
||||||
|
"BurnAfterReading": {
|
||||||
|
"@type": "so:Boolean",
|
||||||
|
"@enum": [false, true]
|
||||||
|
},
|
||||||
|
"CreationTime": {
|
||||||
|
"@type": "dp:Unix_time"
|
||||||
|
},
|
||||||
|
"RemainingSeconds": {
|
||||||
|
"@type": "dp:Second",
|
||||||
|
"@minimum": 1
|
||||||
|
},
|
||||||
|
"CipherParameters": {
|
||||||
|
"@container": "@list",
|
||||||
|
"@value": [
|
||||||
|
{
|
||||||
|
"@type": "pb:InitializationVector"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"@type": "pb:Salt"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"@type": "pb:Iterations"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"@type": "pb:KeySize"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"@type": "pb:TagSize"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"@type": "pb:Algorithm"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"@type": "pb:Mode"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"@type": "pb:Compression"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,146 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
(function() {
|
||||||
|
let ret;
|
||||||
|
|
||||||
|
async function initialize() {
|
||||||
|
if (ret) return ret;
|
||||||
|
|
||||||
|
const COMPRESSION_LEVEL = 7;
|
||||||
|
const NO_ZLIB_HEADER = -1;
|
||||||
|
const CHUNK_SIZE = 32 * 1024;
|
||||||
|
const map = {};
|
||||||
|
const memory = new WebAssembly.Memory({
|
||||||
|
initial: 1,
|
||||||
|
maximum: 1024, // 64MB
|
||||||
|
});
|
||||||
|
const env = {
|
||||||
|
memory,
|
||||||
|
writeToJs(ptr, size) {
|
||||||
|
const o = map[ptr];
|
||||||
|
o.onData(new Uint8Array(memory.buffer, dstPtr, size));
|
||||||
|
},
|
||||||
|
_abort: errno => { console.error(`Error: ${errno}`) },
|
||||||
|
_grow: () => { },
|
||||||
|
};
|
||||||
|
|
||||||
|
let buff;
|
||||||
|
if (typeof fetch === 'undefined') {
|
||||||
|
buff = fs.readFileSync('zlib-1.2.11.wasm');
|
||||||
|
} else {
|
||||||
|
const resp = await fetch('js/zlib-1.2.11.wasm');
|
||||||
|
buff = await resp.arrayBuffer();
|
||||||
|
}
|
||||||
|
const module = await WebAssembly.compile(buff);
|
||||||
|
const ins = await WebAssembly.instantiate(module, { env });
|
||||||
|
|
||||||
|
const srcPtr = ins.exports._malloc(CHUNK_SIZE);
|
||||||
|
const dstPtr = ins.exports._malloc(CHUNK_SIZE);
|
||||||
|
|
||||||
|
class RawDef {
|
||||||
|
constructor() {
|
||||||
|
this.zstreamPtr = ins.exports._createDeflateContext(COMPRESSION_LEVEL, NO_ZLIB_HEADER);
|
||||||
|
map[this.zstreamPtr] = this;
|
||||||
|
this.offset = 0;
|
||||||
|
this.buff = new Uint8Array(CHUNK_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
deflate(chunk, flush) {
|
||||||
|
const src = new Uint8Array(memory.buffer, srcPtr, chunk.length);
|
||||||
|
src.set(chunk);
|
||||||
|
ins.exports._deflate(this.zstreamPtr, srcPtr, dstPtr, chunk.length, CHUNK_SIZE, flush);
|
||||||
|
}
|
||||||
|
|
||||||
|
onData(chunk) {
|
||||||
|
if (this.buff.length < this.offset + chunk.length) {
|
||||||
|
const buff = this.buff;
|
||||||
|
this.buff = new Uint8Array(this.buff.length * 2);
|
||||||
|
this.buff.set(buff);
|
||||||
|
}
|
||||||
|
this.buff.set(chunk, this.offset);
|
||||||
|
this.offset += chunk.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
destroy() {
|
||||||
|
ins.exports._freeDeflateContext(this.zstreamPtr);
|
||||||
|
delete map[this.zstreamPtr];
|
||||||
|
this.buff = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
getBuffer() {
|
||||||
|
const res = new Uint8Array(this.offset);
|
||||||
|
for (let i = 0; i < this.offset; ++i) {
|
||||||
|
res[i] = this.buff[i];
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class RawInf {
|
||||||
|
constructor() {
|
||||||
|
this.zstreamPtr = ins.exports._createInflateContext(NO_ZLIB_HEADER);
|
||||||
|
map[this.zstreamPtr] = this;
|
||||||
|
this.offset = 0;
|
||||||
|
this.buff = new Uint8Array(CHUNK_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
inflate(chunk) {
|
||||||
|
const src = new Uint8Array(memory.buffer, srcPtr, chunk.length);
|
||||||
|
src.set(chunk);
|
||||||
|
ins.exports._inflate(this.zstreamPtr, srcPtr, dstPtr, chunk.length, CHUNK_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
onData(chunk) {
|
||||||
|
if (this.buff.length < this.offset + chunk.length) {
|
||||||
|
const buff = this.buff;
|
||||||
|
this.buff = new Uint8Array(this.buff.length * 2);
|
||||||
|
this.buff.set(buff);
|
||||||
|
}
|
||||||
|
this.buff.set(chunk, this.offset);
|
||||||
|
this.offset += chunk.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
destroy() {
|
||||||
|
ins.exports._freeInflateContext(this.zstreamPtr);
|
||||||
|
delete map[this.zstreamPtr];
|
||||||
|
this.buff = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
getBuffer() {
|
||||||
|
const res = new Uint8Array(this.offset);
|
||||||
|
for (let i = 0; i < this.offset; ++i) {
|
||||||
|
res[i] = this.buff[i];
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = {
|
||||||
|
inflate(rawDeflateBuffer) {
|
||||||
|
const rawInf = new RawInf();
|
||||||
|
for (let offset = 0; offset < rawDeflateBuffer.length; offset += CHUNK_SIZE) {
|
||||||
|
const end = Math.min(offset + CHUNK_SIZE, rawDeflateBuffer.length);
|
||||||
|
const chunk = rawDeflateBuffer.subarray(offset, end);
|
||||||
|
rawInf.inflate(chunk);
|
||||||
|
}
|
||||||
|
const ret = rawInf.getBuffer();
|
||||||
|
rawInf.destroy();
|
||||||
|
return ret;
|
||||||
|
},
|
||||||
|
deflate(rawInflateBuffer) {
|
||||||
|
const rawDef = new RawDef();
|
||||||
|
for (let offset = 0; offset < rawInflateBuffer.length; offset += CHUNK_SIZE) {
|
||||||
|
const end = Math.min(offset + CHUNK_SIZE, rawInflateBuffer.length);
|
||||||
|
const chunk = rawInflateBuffer.subarray(offset, end);
|
||||||
|
rawDef.deflate(chunk, rawInflateBuffer.length <= offset + CHUNK_SIZE);
|
||||||
|
}
|
||||||
|
const ret = rawDef.getBuffer();
|
||||||
|
rawDef.destroy();
|
||||||
|
return ret;
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
this.zlib = initialize();
|
||||||
|
}).call(this);
|
Binary file not shown.
|
@ -53,7 +53,7 @@ class Configuration
|
||||||
'urlshortener' => '',
|
'urlshortener' => '',
|
||||||
'qrcode' => true,
|
'qrcode' => true,
|
||||||
'icon' => 'identicon',
|
'icon' => 'identicon',
|
||||||
'cspheader' => 'default-src \'none\'; manifest-src \'self\'; connect-src *; script-src \'self\'; style-src \'self\'; font-src \'self\'; img-src \'self\' data:; media-src data:; object-src data:; Referrer-Policy: \'no-referrer\'; sandbox allow-same-origin allow-scripts allow-forms allow-popups allow-modals',
|
'cspheader' => 'default-src \'none\'; manifest-src \'self\'; connect-src *; script-src \'self\' \'unsafe-eval\'; style-src \'self\'; font-src \'self\'; img-src \'self\' data:; media-src data:; object-src data:; Referrer-Policy: \'no-referrer\'; sandbox allow-same-origin allow-scripts allow-forms allow-popups allow-modals',
|
||||||
'zerobincompatibility' => false,
|
'zerobincompatibility' => false,
|
||||||
),
|
),
|
||||||
'expire' => array(
|
'expire' => array(
|
||||||
|
|
|
@ -154,6 +154,7 @@ class Controller
|
||||||
* initialize PrivateBin
|
* initialize PrivateBin
|
||||||
*
|
*
|
||||||
* @access private
|
* @access private
|
||||||
|
* @throws Exception
|
||||||
*/
|
*/
|
||||||
private function _init()
|
private function _init()
|
||||||
{
|
{
|
||||||
|
@ -177,16 +178,16 @@ class Controller
|
||||||
* Store new paste or comment
|
* Store new paste or comment
|
||||||
*
|
*
|
||||||
* POST contains one or both:
|
* POST contains one or both:
|
||||||
* data = json encoded SJCL encrypted text (containing keys: iv,v,iter,ks,ts,mode,adata,cipher,salt,ct)
|
* data = json encoded FormatV2 encrypted text (containing keys: iv,v,iter,ks,ts,mode,adata,cipher,salt,ct)
|
||||||
* attachment = json encoded SJCL encrypted text (containing keys: iv,v,iter,ks,ts,mode,adata,cipher,salt,ct)
|
* attachment = json encoded FormatV2 encrypted text (containing keys: iv,v,iter,ks,ts,mode,adata,cipher,salt,ct)
|
||||||
*
|
*
|
||||||
* All optional data will go to meta information:
|
* All optional data will go to meta information:
|
||||||
* expire (optional) = expiration delay (never,5min,10min,1hour,1day,1week,1month,1year,burn) (default:never)
|
* expire (optional) = expiration delay (never,5min,10min,1hour,1day,1week,1month,1year,burn) (default:never)
|
||||||
* formatter (optional) = format to display the paste as (plaintext,syntaxhighlighting,markdown) (default:syntaxhighlighting)
|
* formatter (optional) = format to display the paste as (plaintext,syntaxhighlighting,markdown) (default:syntaxhighlighting)
|
||||||
* burnafterreading (optional) = if this paste may only viewed once ? (0/1) (default:0)
|
* burnafterreading (optional) = if this paste may only viewed once ? (0/1) (default:0)
|
||||||
* opendiscusssion (optional) = is the discussion allowed on this paste ? (0/1) (default:0)
|
* opendiscusssion (optional) = is the discussion allowed on this paste ? (0/1) (default:0)
|
||||||
* attachmentname = json encoded SJCL encrypted text (containing keys: iv,v,iter,ks,ts,mode,adata,cipher,salt,ct)
|
* attachmentname = json encoded FormatV2 encrypted text (containing keys: iv,v,iter,ks,ts,mode,adata,cipher,salt,ct)
|
||||||
* nickname (optional) = in discussion, encoded SJCL encrypted text nickname of author of comment (containing keys: iv,v,iter,ks,ts,mode,adata,cipher,salt,ct)
|
* nickname (optional) = in discussion, encoded FormatV2 encrypted text nickname of author of comment (containing keys: iv,v,iter,ks,ts,mode,adata,cipher,salt,ct)
|
||||||
* parentid (optional) = in discussion, which comment this comment replies to.
|
* parentid (optional) = in discussion, which comment this comment replies to.
|
||||||
* pasteid (optional) = in discussion, which paste this comment belongs to.
|
* pasteid (optional) = in discussion, which paste this comment belongs to.
|
||||||
*
|
*
|
||||||
|
@ -198,59 +199,52 @@ class Controller
|
||||||
// Ensure last paste from visitors IP address was more than configured amount of seconds ago.
|
// Ensure last paste from visitors IP address was more than configured amount of seconds ago.
|
||||||
TrafficLimiter::setConfiguration($this->_conf);
|
TrafficLimiter::setConfiguration($this->_conf);
|
||||||
if (!TrafficLimiter::canPass()) {
|
if (!TrafficLimiter::canPass()) {
|
||||||
return $this->_return_message(
|
$this->_return_message(
|
||||||
1, I18n::_(
|
1, I18n::_(
|
||||||
'Please wait %d seconds between each post.',
|
'Please wait %d seconds between each post.',
|
||||||
$this->_conf->getKey('limit', 'traffic')
|
$this->_conf->getKey('limit', 'traffic')
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$data = $this->_request->getParam('data');
|
$data = $this->_request->getData();
|
||||||
$attachment = $this->_request->getParam('attachment');
|
$isComment = array_key_exists('pasteid', $data) &&
|
||||||
$attachmentname = $this->_request->getParam('attachmentname');
|
!empty($data['pasteid']) &&
|
||||||
|
array_key_exists('parentid', $data) &&
|
||||||
// Ensure content is not too big.
|
!empty($data['parentid']);
|
||||||
|
if (!FormatV2::isValid($data, $isComment)) {
|
||||||
|
$this->_return_message(1, I18n::_('Invalid data.'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
$sizelimit = $this->_conf->getKey('sizelimit');
|
$sizelimit = $this->_conf->getKey('sizelimit');
|
||||||
if (
|
// Ensure content is not too big.
|
||||||
strlen($data) + strlen($attachment) + strlen($attachmentname) > $sizelimit
|
if (strlen($data['ct']) > $sizelimit) {
|
||||||
) {
|
$this->_return_message(
|
||||||
return $this->_return_message(
|
1,
|
||||||
1,
|
I18n::_(
|
||||||
I18n::_(
|
'Paste is limited to %s of encrypted data.',
|
||||||
'Paste is limited to %s of encrypted data.',
|
Filter::formatHumanReadableSize($sizelimit)
|
||||||
Filter::formatHumanReadableSize($sizelimit)
|
)
|
||||||
)
|
);
|
||||||
);
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure attachment did not get lost due to webserver limits or Suhosin
|
|
||||||
if (strlen($attachmentname) > 0 && strlen($attachment) == 0) {
|
|
||||||
return $this->_return_message(1, 'Attachment missing in data received by server. Please check your webserver or suhosin configuration for maximum POST parameter limitations.');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// The user posts a comment.
|
// The user posts a comment.
|
||||||
$pasteid = $this->_request->getParam('pasteid');
|
if ($isComment) {
|
||||||
$parentid = $this->_request->getParam('parentid');
|
$paste = $this->_model->getPaste($data['pasteid']);
|
||||||
if (!empty($pasteid) && !empty($parentid)) {
|
|
||||||
$paste = $this->_model->getPaste($pasteid);
|
|
||||||
if ($paste->exists()) {
|
if ($paste->exists()) {
|
||||||
try {
|
try {
|
||||||
$comment = $paste->getComment($parentid);
|
$comment = $paste->getComment($data['parentid']);
|
||||||
|
|
||||||
$nickname = $this->_request->getParam('nickname');
|
|
||||||
if (!empty($nickname)) {
|
|
||||||
$comment->setNickname($nickname);
|
|
||||||
}
|
|
||||||
|
|
||||||
$comment->setData($data);
|
$comment->setData($data);
|
||||||
$comment->store();
|
$comment->store();
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
return $this->_return_message(1, $e->getMessage());
|
$this->_return_message(1, $e->getMessage());
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
$this->_return_message(0, $comment->getId());
|
$this->_return_message(0, $comment->getId());
|
||||||
} else {
|
} else {
|
||||||
$this->_return_message(1, 'Invalid data.');
|
$this->_return_message(1, I18n::_('Invalid data.'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// The user posts a standard paste.
|
// The user posts a standard paste.
|
||||||
|
@ -259,34 +253,6 @@ class Controller
|
||||||
$paste = $this->_model->getPaste();
|
$paste = $this->_model->getPaste();
|
||||||
try {
|
try {
|
||||||
$paste->setData($data);
|
$paste->setData($data);
|
||||||
|
|
||||||
if (!empty($attachment)) {
|
|
||||||
$paste->setAttachment($attachment);
|
|
||||||
if (!empty($attachmentname)) {
|
|
||||||
$paste->setAttachmentName($attachmentname);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$expire = $this->_request->getParam('expire');
|
|
||||||
if (!empty($expire)) {
|
|
||||||
$paste->setExpiration($expire);
|
|
||||||
}
|
|
||||||
|
|
||||||
$burnafterreading = $this->_request->getParam('burnafterreading');
|
|
||||||
if (!empty($burnafterreading)) {
|
|
||||||
$paste->setBurnafterreading($burnafterreading);
|
|
||||||
}
|
|
||||||
|
|
||||||
$opendiscussion = $this->_request->getParam('opendiscussion');
|
|
||||||
if (!empty($opendiscussion)) {
|
|
||||||
$paste->setOpendiscussion($opendiscussion);
|
|
||||||
}
|
|
||||||
|
|
||||||
$formatter = $this->_request->getParam('formatter');
|
|
||||||
if (!empty($formatter)) {
|
|
||||||
$paste->setFormatter($formatter);
|
|
||||||
}
|
|
||||||
|
|
||||||
$paste->store();
|
$paste->store();
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
return $this->_return_message(1, $e->getMessage());
|
return $this->_return_message(1, $e->getMessage());
|
||||||
|
@ -307,22 +273,17 @@ class Controller
|
||||||
try {
|
try {
|
||||||
$paste = $this->_model->getPaste($dataid);
|
$paste = $this->_model->getPaste($dataid);
|
||||||
if ($paste->exists()) {
|
if ($paste->exists()) {
|
||||||
// accessing this property ensures that the paste would be
|
// accessing this method ensures that the paste would be
|
||||||
// deleted if it has already expired
|
// deleted if it has already expired
|
||||||
$burnafterreading = $paste->isBurnafterreading();
|
$paste->get();
|
||||||
if (
|
if (
|
||||||
($burnafterreading && $deletetoken == 'burnafterreading') || // either we burn-after it has been read //@TODO: not needed anymore now?
|
Filter::slowEquals($deletetoken, $paste->getDeleteToken())
|
||||||
Filter::slowEquals($deletetoken, $paste->getDeleteToken()) // or we manually delete it with this secret token
|
|
||||||
) {
|
) {
|
||||||
// Paste exists and deletion token (if required) is valid: Delete the paste.
|
// Paste exists and deletion token is valid: Delete the paste.
|
||||||
$paste->delete();
|
$paste->delete();
|
||||||
$this->_status = 'Paste was properly deleted.';
|
$this->_status = 'Paste was properly deleted.';
|
||||||
} else {
|
} else {
|
||||||
if (!$burnafterreading && $deletetoken == 'burnafterreading') {
|
$this->_error = 'Wrong deletion token. Paste was not deleted.';
|
||||||
$this->_error = 'Paste is not of burn-after-reading type.';
|
|
||||||
} else {
|
|
||||||
$this->_error = 'Wrong deletion token. Paste was not deleted.';
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
$this->_error = self::GENERIC_ERROR;
|
$this->_error = self::GENERIC_ERROR;
|
||||||
|
@ -355,8 +316,8 @@ class Controller
|
||||||
$paste = $this->_model->getPaste($dataid);
|
$paste = $this->_model->getPaste($dataid);
|
||||||
if ($paste->exists()) {
|
if ($paste->exists()) {
|
||||||
$data = $paste->get();
|
$data = $paste->get();
|
||||||
if (property_exists($data->meta, 'salt')) {
|
if (array_key_exists('salt', $data['meta'])) {
|
||||||
unset($data->meta->salt);
|
unset($data['meta']['salt']);
|
||||||
}
|
}
|
||||||
$this->_return_message(0, $dataid, (array) $data);
|
$this->_return_message(0, $dataid, (array) $data);
|
||||||
} else {
|
} else {
|
||||||
|
@ -476,6 +437,6 @@ class Controller
|
||||||
$result['url'] = $this->_urlBase . '?' . $message;
|
$result['url'] = $this->_urlBase . '?' . $message;
|
||||||
}
|
}
|
||||||
$result += $other;
|
$result += $other;
|
||||||
$this->_json = json_encode($result);
|
$this->_json = Json::encode($result);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,8 +12,6 @@
|
||||||
|
|
||||||
namespace PrivateBin\Data;
|
namespace PrivateBin\Data;
|
||||||
|
|
||||||
use stdClass;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* AbstractData
|
* AbstractData
|
||||||
*
|
*
|
||||||
|
@ -60,7 +58,7 @@ abstract class AbstractData
|
||||||
* @param array $options
|
* @param array $options
|
||||||
* @return AbstractData
|
* @return AbstractData
|
||||||
*/
|
*/
|
||||||
public static function getInstance($options)
|
public static function getInstance(array $options)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -72,14 +70,14 @@ abstract class AbstractData
|
||||||
* @param array $paste
|
* @param array $paste
|
||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
abstract public function create($pasteid, $paste);
|
abstract public function create($pasteid, array $paste);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Read a paste.
|
* Read a paste.
|
||||||
*
|
*
|
||||||
* @access public
|
* @access public
|
||||||
* @param string $pasteid
|
* @param string $pasteid
|
||||||
* @return stdClass|false
|
* @return array|false
|
||||||
*/
|
*/
|
||||||
abstract public function read($pasteid);
|
abstract public function read($pasteid);
|
||||||
|
|
||||||
|
@ -110,7 +108,7 @@ abstract class AbstractData
|
||||||
* @param array $comment
|
* @param array $comment
|
||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
abstract public function createComment($pasteid, $parentid, $commentid, $comment);
|
abstract public function createComment($pasteid, $parentid, $commentid, array $comment);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Read all comments of paste.
|
* Read all comments of paste.
|
||||||
|
@ -163,12 +161,12 @@ abstract class AbstractData
|
||||||
/**
|
/**
|
||||||
* Get next free slot for comment from postdate.
|
* Get next free slot for comment from postdate.
|
||||||
*
|
*
|
||||||
* @access public
|
* @access protected
|
||||||
* @param array $comments
|
* @param array $comments
|
||||||
* @param int|string $postdate
|
* @param int|string $postdate
|
||||||
* @return int|string
|
* @return int|string
|
||||||
*/
|
*/
|
||||||
protected function getOpenSlot(&$comments, $postdate)
|
protected function getOpenSlot(array &$comments, $postdate)
|
||||||
{
|
{
|
||||||
if (array_key_exists($postdate, $comments)) {
|
if (array_key_exists($postdate, $comments)) {
|
||||||
$parts = explode('.', $postdate, 2);
|
$parts = explode('.', $postdate, 2);
|
||||||
|
@ -180,4 +178,25 @@ abstract class AbstractData
|
||||||
}
|
}
|
||||||
return $postdate;
|
return $postdate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Upgrade pre-version 1 pastes with attachment to version 1 format.
|
||||||
|
*
|
||||||
|
* @access protected
|
||||||
|
* @static
|
||||||
|
* @param array $paste
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
protected static function upgradePreV1Format(array $paste)
|
||||||
|
{
|
||||||
|
if (array_key_exists('attachment', $paste['meta'])) {
|
||||||
|
$paste['attachment'] = $paste['meta']['attachment'];
|
||||||
|
unset($paste['meta']['attachment']);
|
||||||
|
if (array_key_exists('attachmentname', $paste['meta'])) {
|
||||||
|
$paste['attachmentname'] = $paste['meta']['attachmentname'];
|
||||||
|
unset($paste['meta']['attachmentname']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $paste;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,7 @@ use Exception;
|
||||||
use PDO;
|
use PDO;
|
||||||
use PDOException;
|
use PDOException;
|
||||||
use PrivateBin\Controller;
|
use PrivateBin\Controller;
|
||||||
use stdClass;
|
use PrivateBin\Json;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Database
|
* Database
|
||||||
|
@ -68,80 +68,78 @@ class Database extends AbstractData
|
||||||
* @throws Exception
|
* @throws Exception
|
||||||
* @return Database
|
* @return Database
|
||||||
*/
|
*/
|
||||||
public static function getInstance($options = null)
|
public static function getInstance(array $options)
|
||||||
{
|
{
|
||||||
// if needed initialize the singleton
|
// if needed initialize the singleton
|
||||||
if (!(self::$_instance instanceof self)) {
|
if (!(self::$_instance instanceof self)) {
|
||||||
self::$_instance = new self;
|
self::$_instance = new self;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (is_array($options)) {
|
// set table prefix if given
|
||||||
// set table prefix if given
|
if (array_key_exists('tbl', $options)) {
|
||||||
if (array_key_exists('tbl', $options)) {
|
self::$_prefix = $options['tbl'];
|
||||||
self::$_prefix = $options['tbl'];
|
}
|
||||||
|
|
||||||
|
// initialize the db connection with new options
|
||||||
|
if (
|
||||||
|
array_key_exists('dsn', $options) &&
|
||||||
|
array_key_exists('usr', $options) &&
|
||||||
|
array_key_exists('pwd', $options) &&
|
||||||
|
array_key_exists('opt', $options)
|
||||||
|
) {
|
||||||
|
// set default options
|
||||||
|
$options['opt'][PDO::ATTR_ERRMODE] = PDO::ERRMODE_EXCEPTION;
|
||||||
|
$options['opt'][PDO::ATTR_EMULATE_PREPARES] = false;
|
||||||
|
$options['opt'][PDO::ATTR_PERSISTENT] = true;
|
||||||
|
$db_tables_exist = true;
|
||||||
|
|
||||||
|
// setup type and dabase connection
|
||||||
|
self::$_type = strtolower(
|
||||||
|
substr($options['dsn'], 0, strpos($options['dsn'], ':'))
|
||||||
|
);
|
||||||
|
$tableQuery = self::_getTableQuery(self::$_type);
|
||||||
|
self::$_db = new PDO(
|
||||||
|
$options['dsn'],
|
||||||
|
$options['usr'],
|
||||||
|
$options['pwd'],
|
||||||
|
$options['opt']
|
||||||
|
);
|
||||||
|
|
||||||
|
// check if the database contains the required tables
|
||||||
|
$tables = self::$_db->query($tableQuery)->fetchAll(PDO::FETCH_COLUMN, 0);
|
||||||
|
|
||||||
|
// create paste table if necessary
|
||||||
|
if (!in_array(self::_sanitizeIdentifier('paste'), $tables)) {
|
||||||
|
self::_createPasteTable();
|
||||||
|
$db_tables_exist = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// initialize the db connection with new options
|
// create comment table if necessary
|
||||||
if (
|
if (!in_array(self::_sanitizeIdentifier('comment'), $tables)) {
|
||||||
array_key_exists('dsn', $options) &&
|
self::_createCommentTable();
|
||||||
array_key_exists('usr', $options) &&
|
$db_tables_exist = false;
|
||||||
array_key_exists('pwd', $options) &&
|
}
|
||||||
array_key_exists('opt', $options)
|
|
||||||
) {
|
|
||||||
// set default options
|
|
||||||
$options['opt'][PDO::ATTR_ERRMODE] = PDO::ERRMODE_EXCEPTION;
|
|
||||||
$options['opt'][PDO::ATTR_EMULATE_PREPARES] = false;
|
|
||||||
$options['opt'][PDO::ATTR_PERSISTENT] = true;
|
|
||||||
$db_tables_exist = true;
|
|
||||||
|
|
||||||
// setup type and dabase connection
|
// create config table if necessary
|
||||||
self::$_type = strtolower(
|
$db_version = Controller::VERSION;
|
||||||
substr($options['dsn'], 0, strpos($options['dsn'], ':'))
|
if (!in_array(self::_sanitizeIdentifier('config'), $tables)) {
|
||||||
);
|
self::_createConfigTable();
|
||||||
$tableQuery = self::_getTableQuery(self::$_type);
|
// if we only needed to create the config table, the DB is older then 0.22
|
||||||
self::$_db = new PDO(
|
if ($db_tables_exist) {
|
||||||
$options['dsn'],
|
$db_version = '0.21';
|
||||||
$options['usr'],
|
|
||||||
$options['pwd'],
|
|
||||||
$options['opt']
|
|
||||||
);
|
|
||||||
|
|
||||||
// check if the database contains the required tables
|
|
||||||
$tables = self::$_db->query($tableQuery)->fetchAll(PDO::FETCH_COLUMN, 0);
|
|
||||||
|
|
||||||
// create paste table if necessary
|
|
||||||
if (!in_array(self::_sanitizeIdentifier('paste'), $tables)) {
|
|
||||||
self::_createPasteTable();
|
|
||||||
$db_tables_exist = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// create comment table if necessary
|
|
||||||
if (!in_array(self::_sanitizeIdentifier('comment'), $tables)) {
|
|
||||||
self::_createCommentTable();
|
|
||||||
$db_tables_exist = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// create config table if necessary
|
|
||||||
$db_version = Controller::VERSION;
|
|
||||||
if (!in_array(self::_sanitizeIdentifier('config'), $tables)) {
|
|
||||||
self::_createConfigTable();
|
|
||||||
// if we only needed to create the config table, the DB is older then 0.22
|
|
||||||
if ($db_tables_exist) {
|
|
||||||
$db_version = '0.21';
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$db_version = self::_getConfig('VERSION');
|
|
||||||
}
|
|
||||||
|
|
||||||
// update database structure if necessary
|
|
||||||
if (version_compare($db_version, Controller::VERSION, '<')) {
|
|
||||||
self::_upgradeDatabase($db_version);
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
throw new Exception(
|
$db_version = self::_getConfig('VERSION');
|
||||||
'Missing configuration for key dsn, usr, pwd or opt in the section model_options, please check your configuration file', 6
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// update database structure if necessary
|
||||||
|
if (version_compare($db_version, Controller::VERSION, '<')) {
|
||||||
|
self::_upgradeDatabase($db_version);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new Exception(
|
||||||
|
'Missing configuration for key dsn, usr, pwd or opt in the section model_options, please check your configuration file', 6
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return self::$_instance;
|
return self::$_instance;
|
||||||
|
@ -155,7 +153,7 @@ class Database extends AbstractData
|
||||||
* @param array $paste
|
* @param array $paste
|
||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
public function create($pasteid, $paste)
|
public function create($pasteid, array $paste)
|
||||||
{
|
{
|
||||||
if (
|
if (
|
||||||
array_key_exists($pasteid, self::$_cache)
|
array_key_exists($pasteid, self::$_cache)
|
||||||
|
@ -167,42 +165,50 @@ class Database extends AbstractData
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$opendiscussion = $burnafterreading = false;
|
$expire_date = 0;
|
||||||
$attachment = $attachmentname = '';
|
$opendiscussion = $burnafterreading = false;
|
||||||
$meta = $paste['meta'];
|
$attachment = $attachmentname = null;
|
||||||
unset($meta['postdate']);
|
$meta = $paste['meta'];
|
||||||
$expire_date = 0;
|
$isVersion1 = array_key_exists('data', $paste);
|
||||||
if (array_key_exists('expire_date', $paste['meta'])) {
|
list($createdKey) = self::_getVersionedKeys($isVersion1 ? 1 : 2);
|
||||||
$expire_date = (int) $paste['meta']['expire_date'];
|
$created = (int) $meta[$createdKey];
|
||||||
|
unset($meta[$createdKey], $paste['meta']);
|
||||||
|
if (array_key_exists('expire_date', $meta)) {
|
||||||
|
$expire_date = (int) $meta['expire_date'];
|
||||||
unset($meta['expire_date']);
|
unset($meta['expire_date']);
|
||||||
}
|
}
|
||||||
if (array_key_exists('opendiscussion', $paste['meta'])) {
|
if (array_key_exists('opendiscussion', $meta)) {
|
||||||
$opendiscussion = (bool) $paste['meta']['opendiscussion'];
|
$opendiscussion = $meta['opendiscussion'];
|
||||||
unset($meta['opendiscussion']);
|
unset($meta['opendiscussion']);
|
||||||
}
|
}
|
||||||
if (array_key_exists('burnafterreading', $paste['meta'])) {
|
if (array_key_exists('burnafterreading', $meta)) {
|
||||||
$burnafterreading = (bool) $paste['meta']['burnafterreading'];
|
$burnafterreading = $meta['burnafterreading'];
|
||||||
unset($meta['burnafterreading']);
|
unset($meta['burnafterreading']);
|
||||||
}
|
}
|
||||||
if (array_key_exists('attachment', $paste['meta'])) {
|
if ($isVersion1) {
|
||||||
$attachment = $paste['meta']['attachment'];
|
if (array_key_exists('attachment', $meta)) {
|
||||||
unset($meta['attachment']);
|
$attachment = $meta['attachment'];
|
||||||
}
|
unset($meta['attachment']);
|
||||||
if (array_key_exists('attachmentname', $paste['meta'])) {
|
}
|
||||||
$attachmentname = $paste['meta']['attachmentname'];
|
if (array_key_exists('attachmentname', $meta)) {
|
||||||
unset($meta['attachmentname']);
|
$attachmentname = $meta['attachmentname'];
|
||||||
|
unset($meta['attachmentname']);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$opendiscussion = $paste['adata'][2];
|
||||||
|
$burnafterreading = $paste['adata'][3];
|
||||||
}
|
}
|
||||||
return self::_exec(
|
return self::_exec(
|
||||||
'INSERT INTO ' . self::_sanitizeIdentifier('paste') .
|
'INSERT INTO ' . self::_sanitizeIdentifier('paste') .
|
||||||
' VALUES(?,?,?,?,?,?,?,?,?)',
|
' VALUES(?,?,?,?,?,?,?,?,?)',
|
||||||
array(
|
array(
|
||||||
$pasteid,
|
$pasteid,
|
||||||
$paste['data'],
|
$isVersion1 ? $paste['data'] : Json::encode($paste),
|
||||||
$paste['meta']['postdate'],
|
$created,
|
||||||
$expire_date,
|
$expire_date,
|
||||||
(int) $opendiscussion,
|
(int) $opendiscussion,
|
||||||
(int) $burnafterreading,
|
(int) $burnafterreading,
|
||||||
json_encode($meta),
|
Json::encode($meta),
|
||||||
$attachment,
|
$attachment,
|
||||||
$attachmentname,
|
$attachmentname,
|
||||||
)
|
)
|
||||||
|
@ -214,65 +220,63 @@ class Database extends AbstractData
|
||||||
*
|
*
|
||||||
* @access public
|
* @access public
|
||||||
* @param string $pasteid
|
* @param string $pasteid
|
||||||
* @return stdClass|false
|
* @return array|false
|
||||||
*/
|
*/
|
||||||
public function read($pasteid)
|
public function read($pasteid)
|
||||||
{
|
{
|
||||||
if (
|
if (array_key_exists($pasteid, self::$_cache)) {
|
||||||
!array_key_exists($pasteid, self::$_cache)
|
return self::$_cache[$pasteid];
|
||||||
) {
|
}
|
||||||
self::$_cache[$pasteid] = false;
|
|
||||||
$paste = self::_select(
|
|
||||||
'SELECT * FROM ' . self::_sanitizeIdentifier('paste') .
|
|
||||||
' WHERE dataid = ?', array($pasteid), true
|
|
||||||
);
|
|
||||||
|
|
||||||
if (false !== $paste) {
|
self::$_cache[$pasteid] = false;
|
||||||
// create object
|
$paste = self::_select(
|
||||||
self::$_cache[$pasteid] = new stdClass;
|
'SELECT * FROM ' . self::_sanitizeIdentifier('paste') .
|
||||||
self::$_cache[$pasteid]->data = $paste['data'];
|
' WHERE dataid = ?', array($pasteid), true
|
||||||
|
);
|
||||||
|
|
||||||
$meta = json_decode($paste['meta']);
|
if ($paste === false) {
|
||||||
if (!is_object($meta)) {
|
return false;
|
||||||
$meta = new stdClass;
|
}
|
||||||
}
|
// create array
|
||||||
|
$data = Json::decode($paste['data']);
|
||||||
|
$isVersion2 = array_key_exists('v', $data) && $data['v'] >= 2;
|
||||||
|
if ($isVersion2) {
|
||||||
|
self::$_cache[$pasteid] = $data;
|
||||||
|
list($createdKey) = self::_getVersionedKeys(2);
|
||||||
|
} else {
|
||||||
|
self::$_cache[$pasteid] = array('data' => $paste['data']);
|
||||||
|
list($createdKey) = self::_getVersionedKeys(1);
|
||||||
|
}
|
||||||
|
|
||||||
// support older attachments
|
try {
|
||||||
if (property_exists($meta, 'attachment')) {
|
$paste['meta'] = Json::decode($paste['meta']);
|
||||||
self::$_cache[$pasteid]->attachment = $meta->attachment;
|
} catch (Exception $e) {
|
||||||
unset($meta->attachment);
|
$paste['meta'] = array();
|
||||||
if (property_exists($meta, 'attachmentname')) {
|
}
|
||||||
self::$_cache[$pasteid]->attachmentname = $meta->attachmentname;
|
$paste = self::upgradePreV1Format($paste);
|
||||||
unset($meta->attachmentname);
|
self::$_cache[$pasteid]['meta'] = $paste['meta'];
|
||||||
}
|
self::$_cache[$pasteid]['meta'][$createdKey] = (int) $paste['postdate'];
|
||||||
}
|
$expire_date = (int) $paste['expiredate'];
|
||||||
// support current attachments
|
if ($expire_date > 0) {
|
||||||
elseif (array_key_exists('attachment', $paste) && strlen($paste['attachment'])) {
|
self::$_cache[$pasteid]['meta']['expire_date'] = $expire_date;
|
||||||
self::$_cache[$pasteid]->attachment = $paste['attachment'];
|
}
|
||||||
if (array_key_exists('attachmentname', $paste) && strlen($paste['attachmentname'])) {
|
if ($isVersion2) {
|
||||||
self::$_cache[$pasteid]->attachmentname = $paste['attachmentname'];
|
return self::$_cache[$pasteid];
|
||||||
}
|
}
|
||||||
}
|
|
||||||
self::$_cache[$pasteid]->meta = $meta;
|
// support v1 attachments
|
||||||
self::$_cache[$pasteid]->meta->postdate = (int) $paste['postdate'];
|
if (array_key_exists('attachment', $paste) && strlen($paste['attachment'])) {
|
||||||
$expire_date = (int) $paste['expiredate'];
|
self::$_cache[$pasteid]['attachment'] = $paste['attachment'];
|
||||||
if (
|
if (array_key_exists('attachmentname', $paste) && strlen($paste['attachmentname'])) {
|
||||||
$expire_date > 0
|
self::$_cache[$pasteid]['attachmentname'] = $paste['attachmentname'];
|
||||||
) {
|
|
||||||
self::$_cache[$pasteid]->meta->expire_date = $expire_date;
|
|
||||||
}
|
|
||||||
if (
|
|
||||||
$paste['opendiscussion']
|
|
||||||
) {
|
|
||||||
self::$_cache[$pasteid]->meta->opendiscussion = true;
|
|
||||||
}
|
|
||||||
if (
|
|
||||||
$paste['burnafterreading']
|
|
||||||
) {
|
|
||||||
self::$_cache[$pasteid]->meta->burnafterreading = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if ($paste['opendiscussion']) {
|
||||||
|
self::$_cache[$pasteid]['meta']['opendiscussion'] = true;
|
||||||
|
}
|
||||||
|
if ($paste['burnafterreading']) {
|
||||||
|
self::$_cache[$pasteid]['meta']['burnafterreading'] = true;
|
||||||
|
}
|
||||||
|
|
||||||
return self::$_cache[$pasteid];
|
return self::$_cache[$pasteid];
|
||||||
}
|
}
|
||||||
|
@ -327,11 +331,21 @@ class Database extends AbstractData
|
||||||
* @param array $comment
|
* @param array $comment
|
||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
public function createComment($pasteid, $parentid, $commentid, $comment)
|
public function createComment($pasteid, $parentid, $commentid, array $comment)
|
||||||
{
|
{
|
||||||
foreach (array('nickname', 'vizhash') as $key) {
|
if (array_key_exists('data', $comment)) {
|
||||||
if (!array_key_exists($key, $comment['meta'])) {
|
$version = 1;
|
||||||
$comment['meta'][$key] = null;
|
$data = $comment['data'];
|
||||||
|
} else {
|
||||||
|
$version = 2;
|
||||||
|
$data = Json::encode($comment);
|
||||||
|
}
|
||||||
|
list($createdKey, $iconKey) = self::_getVersionedKeys($version);
|
||||||
|
$meta = $comment['meta'];
|
||||||
|
unset($comment['meta']);
|
||||||
|
foreach (array('nickname', $iconKey) as $key) {
|
||||||
|
if (!array_key_exists($key, $meta)) {
|
||||||
|
$meta[$key] = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return self::_exec(
|
return self::_exec(
|
||||||
|
@ -341,10 +355,10 @@ class Database extends AbstractData
|
||||||
$commentid,
|
$commentid,
|
||||||
$pasteid,
|
$pasteid,
|
||||||
$parentid,
|
$parentid,
|
||||||
$comment['data'],
|
$data,
|
||||||
$comment['meta']['nickname'],
|
$meta['nickname'],
|
||||||
$comment['meta']['vizhash'],
|
$meta[$iconKey],
|
||||||
$comment['meta']['postdate'],
|
$meta[$createdKey],
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -367,16 +381,22 @@ class Database extends AbstractData
|
||||||
$comments = array();
|
$comments = array();
|
||||||
if (count($rows)) {
|
if (count($rows)) {
|
||||||
foreach ($rows as $row) {
|
foreach ($rows as $row) {
|
||||||
$i = $this->getOpenSlot($comments, (int) $row['postdate']);
|
$i = $this->getOpenSlot($comments, (int) $row['postdate']);
|
||||||
$comments[$i] = new stdClass;
|
$data = Json::decode($row['data']);
|
||||||
$comments[$i]->id = $row['dataid'];
|
if (array_key_exists('v', $data) && $data['v'] >= 2) {
|
||||||
$comments[$i]->parentid = $row['parentid'];
|
$version = 2;
|
||||||
$comments[$i]->data = $row['data'];
|
$comments[$i] = $data;
|
||||||
$comments[$i]->meta = new stdClass;
|
} else {
|
||||||
$comments[$i]->meta->postdate = (int) $row['postdate'];
|
$version = 1;
|
||||||
foreach (array('nickname', 'vizhash') as $key) {
|
$comments[$i] = array('data' => $row['data']);
|
||||||
if (array_key_exists($key, $row) && !empty($row[$key])) {
|
}
|
||||||
$comments[$i]->meta->$key = $row[$key];
|
list($createdKey, $iconKey) = self::_getVersionedKeys($version);
|
||||||
|
$comments[$i]['id'] = $row['dataid'];
|
||||||
|
$comments[$i]['parentid'] = $row['parentid'];
|
||||||
|
$comments[$i]['meta'] = array($createdKey => (int) $row['postdate']);
|
||||||
|
foreach (array('nickname' => 'nickname', 'vizhash' => $iconKey) as $rowKey => $commentKey) {
|
||||||
|
if (array_key_exists($rowKey, $row) && !empty($row[$rowKey])) {
|
||||||
|
$comments[$i]['meta'][$commentKey] = $row[$rowKey];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -415,7 +435,8 @@ class Database extends AbstractData
|
||||||
$pastes = array();
|
$pastes = array();
|
||||||
$rows = self::_select(
|
$rows = self::_select(
|
||||||
'SELECT dataid FROM ' . self::_sanitizeIdentifier('paste') .
|
'SELECT dataid FROM ' . self::_sanitizeIdentifier('paste') .
|
||||||
' WHERE expiredate < ? AND expiredate != ? LIMIT ?', array(time(), 0, $batchsize)
|
' WHERE expiredate < ? AND expiredate != ? LIMIT ?',
|
||||||
|
array(time(), 0, $batchsize)
|
||||||
);
|
);
|
||||||
if (count($rows)) {
|
if (count($rows)) {
|
||||||
foreach ($rows as $row) {
|
foreach ($rows as $row) {
|
||||||
|
@ -452,7 +473,7 @@ class Database extends AbstractData
|
||||||
* @param array $params
|
* @param array $params
|
||||||
* @param bool $firstOnly if only the first row should be returned
|
* @param bool $firstOnly if only the first row should be returned
|
||||||
* @throws PDOException
|
* @throws PDOException
|
||||||
* @return array
|
* @return array|false
|
||||||
*/
|
*/
|
||||||
private static function _select($sql, array $params, $firstOnly = false)
|
private static function _select($sql, array $params, $firstOnly = false)
|
||||||
{
|
{
|
||||||
|
@ -465,6 +486,22 @@ class Database extends AbstractData
|
||||||
return $result;
|
return $result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get version dependent key names
|
||||||
|
*
|
||||||
|
* @access private
|
||||||
|
* @static
|
||||||
|
* @param int $version
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
private static function _getVersionedKeys($version)
|
||||||
|
{
|
||||||
|
if ($version === 1) {
|
||||||
|
return array('postdate', 'vizhash');
|
||||||
|
}
|
||||||
|
return array('created', 'icon');
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* get table list query, depending on the database type
|
* get table list query, depending on the database type
|
||||||
*
|
*
|
||||||
|
@ -543,7 +580,7 @@ class Database extends AbstractData
|
||||||
*
|
*
|
||||||
* @access private
|
* @access private
|
||||||
* @static
|
* @static
|
||||||
* @param string $key
|
* @param string $key
|
||||||
* @return array
|
* @return array
|
||||||
*/
|
*/
|
||||||
private static function _getPrimaryKeyClauses($key = 'dataid')
|
private static function _getPrimaryKeyClauses($key = 'dataid')
|
||||||
|
@ -557,6 +594,30 @@ class Database extends AbstractData
|
||||||
return array($main_key, $after_key);
|
return array($main_key, $after_key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get the data type, depending on the database driver
|
||||||
|
*
|
||||||
|
* @access private
|
||||||
|
* @static
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
private static function _getDataType()
|
||||||
|
{
|
||||||
|
return self::$_type === 'pgsql' ? 'TEXT' : 'BLOB';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get the attachment type, depending on the database driver
|
||||||
|
*
|
||||||
|
* @access private
|
||||||
|
* @static
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
private static function _getAttachmentType()
|
||||||
|
{
|
||||||
|
return self::$_type === 'pgsql' ? 'TEXT' : 'MEDIUMBLOB';
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* create the paste table
|
* create the paste table
|
||||||
*
|
*
|
||||||
|
@ -566,7 +627,7 @@ class Database extends AbstractData
|
||||||
private static function _createPasteTable()
|
private static function _createPasteTable()
|
||||||
{
|
{
|
||||||
list($main_key, $after_key) = self::_getPrimaryKeyClauses();
|
list($main_key, $after_key) = self::_getPrimaryKeyClauses();
|
||||||
$dataType = self::$_type === 'pgsql' ? 'TEXT' : 'BLOB';
|
$dataType = self::_getDataType();
|
||||||
self::$_db->exec(
|
self::$_db->exec(
|
||||||
'CREATE TABLE ' . self::_sanitizeIdentifier('paste') . ' ( ' .
|
'CREATE TABLE ' . self::_sanitizeIdentifier('paste') . ' ( ' .
|
||||||
"dataid CHAR(16) NOT NULL$main_key, " .
|
"dataid CHAR(16) NOT NULL$main_key, " .
|
||||||
|
@ -576,7 +637,7 @@ class Database extends AbstractData
|
||||||
'opendiscussion INT, ' .
|
'opendiscussion INT, ' .
|
||||||
'burnafterreading INT, ' .
|
'burnafterreading INT, ' .
|
||||||
'meta TEXT, ' .
|
'meta TEXT, ' .
|
||||||
'attachment ' . (self::$_type === 'pgsql' ? 'TEXT' : 'MEDIUMBLOB') . ', ' .
|
'attachment ' . self::_getAttachmentType() . ', ' .
|
||||||
"attachmentname $dataType$after_key );"
|
"attachmentname $dataType$after_key );"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -590,7 +651,7 @@ class Database extends AbstractData
|
||||||
private static function _createCommentTable()
|
private static function _createCommentTable()
|
||||||
{
|
{
|
||||||
list($main_key, $after_key) = self::_getPrimaryKeyClauses();
|
list($main_key, $after_key) = self::_getPrimaryKeyClauses();
|
||||||
$dataType = self::$_type === 'pgsql' ? 'text' : 'BLOB';
|
$dataType = self::_getDataType();
|
||||||
self::$_db->exec(
|
self::$_db->exec(
|
||||||
'CREATE TABLE ' . self::_sanitizeIdentifier('comment') . ' ( ' .
|
'CREATE TABLE ' . self::_sanitizeIdentifier('comment') . ' ( ' .
|
||||||
"dataid CHAR(16) NOT NULL$main_key, " .
|
"dataid CHAR(16) NOT NULL$main_key, " .
|
||||||
|
@ -649,7 +710,7 @@ class Database extends AbstractData
|
||||||
*/
|
*/
|
||||||
private static function _upgradeDatabase($oldversion)
|
private static function _upgradeDatabase($oldversion)
|
||||||
{
|
{
|
||||||
$dataType = self::$_type === 'pgsql' ? 'TEXT' : 'BLOB';
|
$dataType = self::_getDataType();
|
||||||
switch ($oldversion) {
|
switch ($oldversion) {
|
||||||
case '0.21':
|
case '0.21':
|
||||||
// create the meta column if necessary (pre 0.21 change)
|
// create the meta column if necessary (pre 0.21 change)
|
||||||
|
@ -661,8 +722,7 @@ class Database extends AbstractData
|
||||||
// SQLite only allows one ALTER statement at a time...
|
// SQLite only allows one ALTER statement at a time...
|
||||||
self::$_db->exec(
|
self::$_db->exec(
|
||||||
'ALTER TABLE ' . self::_sanitizeIdentifier('paste') .
|
'ALTER TABLE ' . self::_sanitizeIdentifier('paste') .
|
||||||
' ADD COLUMN attachment ' .
|
' ADD COLUMN attachment ' . self::_getAttachmentType() . ';'
|
||||||
(self::$_type === 'pgsql' ? 'TEXT' : 'MEDIUMBLOB') . ';'
|
|
||||||
);
|
);
|
||||||
self::$_db->exec(
|
self::$_db->exec(
|
||||||
'ALTER TABLE ' . self::_sanitizeIdentifier('paste') . " ADD COLUMN attachmentname $dataType;"
|
'ALTER TABLE ' . self::_sanitizeIdentifier('paste') . " ADD COLUMN attachmentname $dataType;"
|
||||||
|
|
|
@ -29,7 +29,7 @@ class Filesystem extends AbstractData
|
||||||
* @param array $options
|
* @param array $options
|
||||||
* @return Filesystem
|
* @return Filesystem
|
||||||
*/
|
*/
|
||||||
public static function getInstance($options = null)
|
public static function getInstance(array $options)
|
||||||
{
|
{
|
||||||
// if needed initialize the singleton
|
// if needed initialize the singleton
|
||||||
if (!(self::$_instance instanceof self)) {
|
if (!(self::$_instance instanceof self)) {
|
||||||
|
@ -53,7 +53,7 @@ class Filesystem extends AbstractData
|
||||||
* @param array $paste
|
* @param array $paste
|
||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
public function create($pasteid, $paste)
|
public function create($pasteid, array $paste)
|
||||||
{
|
{
|
||||||
$storagedir = self::_dataid2path($pasteid);
|
$storagedir = self::_dataid2path($pasteid);
|
||||||
$file = $storagedir . $pasteid . '.php';
|
$file = $storagedir . $pasteid . '.php';
|
||||||
|
@ -71,23 +71,16 @@ class Filesystem extends AbstractData
|
||||||
*
|
*
|
||||||
* @access public
|
* @access public
|
||||||
* @param string $pasteid
|
* @param string $pasteid
|
||||||
* @return \stdClass|false
|
* @return array|false
|
||||||
*/
|
*/
|
||||||
public function read($pasteid)
|
public function read($pasteid)
|
||||||
{
|
{
|
||||||
if (!$this->exists($pasteid)) {
|
if (!$this->exists($pasteid)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
$paste = DataStore::get(self::_dataid2path($pasteid) . $pasteid . '.php');
|
return self::upgradePreV1Format(
|
||||||
if (property_exists($paste->meta, 'attachment')) {
|
DataStore::get(self::_dataid2path($pasteid) . $pasteid . '.php')
|
||||||
$paste->attachment = $paste->meta->attachment;
|
);
|
||||||
unset($paste->meta->attachment);
|
|
||||||
if (property_exists($paste->meta, 'attachmentname')) {
|
|
||||||
$paste->attachmentname = $paste->meta->attachmentname;
|
|
||||||
unset($paste->meta->attachmentname);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return $paste;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -162,7 +155,7 @@ class Filesystem extends AbstractData
|
||||||
* @param array $comment
|
* @param array $comment
|
||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
public function createComment($pasteid, $parentid, $commentid, $comment)
|
public function createComment($pasteid, $parentid, $commentid, array $comment)
|
||||||
{
|
{
|
||||||
$storagedir = self::_dataid2discussionpath($pasteid);
|
$storagedir = self::_dataid2discussionpath($pasteid);
|
||||||
$file = $storagedir . $pasteid . '.' . $commentid . '.' . $parentid . '.php';
|
$file = $storagedir . $pasteid . '.' . $commentid . '.' . $parentid . '.php';
|
||||||
|
@ -197,11 +190,11 @@ class Filesystem extends AbstractData
|
||||||
$comment = DataStore::get($discdir . $filename);
|
$comment = DataStore::get($discdir . $filename);
|
||||||
$items = explode('.', $filename);
|
$items = explode('.', $filename);
|
||||||
// Add some meta information not contained in file.
|
// Add some meta information not contained in file.
|
||||||
$comment->id = $items[1];
|
$comment['id'] = $items[1];
|
||||||
$comment->parentid = $items[2];
|
$comment['parentid'] = $items[2];
|
||||||
|
|
||||||
// Store in array
|
// Store in array
|
||||||
$key = $this->getOpenSlot($comments, (int) $comment->meta->postdate);
|
$key = $this->getOpenSlot($comments, (int) $comment['meta']['created']);
|
||||||
$comments[$key] = $comment;
|
$comments[$key] = $comment;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -290,8 +283,8 @@ class Filesystem extends AbstractData
|
||||||
if ($this->exists($pasteid)) {
|
if ($this->exists($pasteid)) {
|
||||||
$data = $this->read($pasteid);
|
$data = $this->read($pasteid);
|
||||||
if (
|
if (
|
||||||
property_exists($data->meta, 'expire_date') &&
|
array_key_exists('expire_date', $data['meta']) &&
|
||||||
$data->meta->expire_date < time()
|
$data['meta']['expire_date'] < time()
|
||||||
) {
|
) {
|
||||||
$pastes[] = $pasteid;
|
$pastes[] = $pasteid;
|
||||||
if (count($pastes) >= $batchsize) {
|
if (count($pastes) >= $batchsize) {
|
||||||
|
|
|
@ -0,0 +1,127 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* PrivateBin
|
||||||
|
*
|
||||||
|
* 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
|
||||||
|
* @version 1.2.1
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace PrivateBin;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* FormatV2
|
||||||
|
*
|
||||||
|
* Provides validation function for version 2 format of pastes & comments.
|
||||||
|
*/
|
||||||
|
class FormatV2
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* version 2 format validator
|
||||||
|
*
|
||||||
|
* Checks if the given array is a proper version 2 formatted, encrypted message.
|
||||||
|
*
|
||||||
|
* @access public
|
||||||
|
* @static
|
||||||
|
* @param array $message
|
||||||
|
* @param bool $isComment
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public static function isValid($message, $isComment = false)
|
||||||
|
{
|
||||||
|
$required_keys = array('adata', 'v', 'ct');
|
||||||
|
if ($isComment) {
|
||||||
|
$required_keys[] = 'pasteid';
|
||||||
|
$required_keys[] = 'parentid';
|
||||||
|
} else {
|
||||||
|
$required_keys[] = 'meta';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure no additionnal keys were added.
|
||||||
|
if (count(array_keys($message)) != count($required_keys)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure required fields are present.
|
||||||
|
foreach ($required_keys as $k) {
|
||||||
|
if (!array_key_exists($k, $message)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$cipherParams = $isComment ? $message['adata'] : $message['adata'][0];
|
||||||
|
|
||||||
|
// Make sure some fields are base64 data:
|
||||||
|
// - initialization vector
|
||||||
|
if (!base64_decode($cipherParams[0], true)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// - salt
|
||||||
|
if (!base64_decode($cipherParams[1], true)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// - cipher text
|
||||||
|
if (!($ct = base64_decode($message['ct'], true))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure some fields have a reasonable size:
|
||||||
|
// - initialization vector
|
||||||
|
if (strlen($cipherParams[0]) > 24) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// - salt
|
||||||
|
if (strlen($cipherParams[1]) > 14) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure some fields contain no unsupported values:
|
||||||
|
// - version
|
||||||
|
if (!(is_int($message['v']) || is_float($message['v'])) || (float) $message['v'] < 2) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// - iterations, refuse less then 10000 iterations (minimum NIST recommendation)
|
||||||
|
if (!is_int($cipherParams[2]) || $cipherParams[2] <= 10000) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// - key size
|
||||||
|
if (!in_array($cipherParams[3], array(128, 192, 256), true)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// - tag size
|
||||||
|
if (!in_array($cipherParams[4], array(64, 96, 128), true)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// - algorithm, must be AES
|
||||||
|
if ($cipherParams[5] !== 'aes') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// - mode
|
||||||
|
if (!in_array($cipherParams[6], array('ctr', 'cbc', 'gcm'), true)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// - compression
|
||||||
|
if (!in_array($cipherParams[7], array('zlib', 'none'), true)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reject data if entropy is too low
|
||||||
|
if (strlen($ct) > strlen(gzdeflate($ct))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// require only the key 'expire' in the metadata of pastes
|
||||||
|
if (!$isComment && (
|
||||||
|
count($message['meta']) === 0 ||
|
||||||
|
!array_key_exists('expire', $message['meta']) ||
|
||||||
|
count($message['meta']) > 1
|
||||||
|
)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
|
@ -156,9 +156,8 @@ class I18n
|
||||||
|
|
||||||
// load translations
|
// load translations
|
||||||
self::$_language = $match;
|
self::$_language = $match;
|
||||||
self::$_translations = ($match == 'en') ? array() : json_decode(
|
self::$_translations = ($match == 'en') ? array() : Json::decode(
|
||||||
file_get_contents(self::_getPath($match . '.json')),
|
file_get_contents(self::_getPath($match . '.json'))
|
||||||
true
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -244,7 +243,7 @@ class I18n
|
||||||
{
|
{
|
||||||
$file = self::_getPath('languages.json');
|
$file = self::_getPath('languages.json');
|
||||||
if (count(self::$_languageLabels) == 0 && is_readable($file)) {
|
if (count(self::$_languageLabels) == 0 && is_readable($file)) {
|
||||||
self::$_languageLabels = json_decode(file_get_contents($file), true);
|
self::$_languageLabels = Json::decode(file_get_contents($file));
|
||||||
}
|
}
|
||||||
if (count($languages) == 0) {
|
if (count($languages) == 0) {
|
||||||
return self::$_languageLabels;
|
return self::$_languageLabels;
|
||||||
|
|
34
lib/Json.php
34
lib/Json.php
|
@ -33,9 +33,39 @@ class Json
|
||||||
public static function encode($input)
|
public static function encode($input)
|
||||||
{
|
{
|
||||||
$jsonString = json_encode($input);
|
$jsonString = json_encode($input);
|
||||||
$errorCode = json_last_error();
|
self::_detectError();
|
||||||
|
return $jsonString;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an array with the contents as described in the given JSON input
|
||||||
|
*
|
||||||
|
* @access public
|
||||||
|
* @static
|
||||||
|
* @param string $input
|
||||||
|
* @throws Exception
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public static function decode($input)
|
||||||
|
{
|
||||||
|
$array = json_decode($input, true);
|
||||||
|
self::_detectError();
|
||||||
|
return $array;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Detects JSON errors and raises an exception if one is found
|
||||||
|
*
|
||||||
|
* @access private
|
||||||
|
* @static
|
||||||
|
* @throws Exception
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
private static function _detectError()
|
||||||
|
{
|
||||||
|
$errorCode = json_last_error();
|
||||||
if ($errorCode === JSON_ERROR_NONE) {
|
if ($errorCode === JSON_ERROR_NONE) {
|
||||||
return $jsonString;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$message = 'A JSON error occurred';
|
$message = 'A JSON error occurred';
|
||||||
|
|
|
@ -15,8 +15,6 @@ namespace PrivateBin\Model;
|
||||||
use Exception;
|
use Exception;
|
||||||
use PrivateBin\Configuration;
|
use PrivateBin\Configuration;
|
||||||
use PrivateBin\Data\AbstractData;
|
use PrivateBin\Data\AbstractData;
|
||||||
use PrivateBin\Sjcl;
|
|
||||||
use stdClass;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* AbstractModel
|
* AbstractModel
|
||||||
|
@ -37,9 +35,9 @@ abstract class AbstractModel
|
||||||
* Instance data.
|
* Instance data.
|
||||||
*
|
*
|
||||||
* @access protected
|
* @access protected
|
||||||
* @var stdClass
|
* @var array
|
||||||
*/
|
*/
|
||||||
protected $_data;
|
protected $_data = array('meta' => array());
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Configuration.
|
* Configuration.
|
||||||
|
@ -68,8 +66,6 @@ abstract class AbstractModel
|
||||||
{
|
{
|
||||||
$this->_conf = $configuration;
|
$this->_conf = $configuration;
|
||||||
$this->_store = $storage;
|
$this->_store = $storage;
|
||||||
$this->_data = new stdClass;
|
|
||||||
$this->_data->meta = new stdClass;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -102,28 +98,29 @@ abstract class AbstractModel
|
||||||
* Set data and recalculate ID.
|
* Set data and recalculate ID.
|
||||||
*
|
*
|
||||||
* @access public
|
* @access public
|
||||||
* @param string $data
|
* @param array $data
|
||||||
* @throws Exception
|
* @throws Exception
|
||||||
*/
|
*/
|
||||||
public function setData($data)
|
public function setData(array $data)
|
||||||
{
|
{
|
||||||
if (!Sjcl::isValid($data)) {
|
$data = $this->_sanitize($data);
|
||||||
throw new Exception('Invalid data.', 61);
|
$this->_validate($data);
|
||||||
}
|
$this->_data = $data;
|
||||||
$this->_data->data = $data;
|
|
||||||
|
|
||||||
// We just want a small hash to avoid collisions:
|
// calculate a 64 bit checksum to avoid collisions
|
||||||
// Half-MD5 (64 bits) will do the trick
|
$this->setId(hash(version_compare(PHP_VERSION, '5.6', '<') ? 'fnv164' : 'fnv1a64', $data['ct']));
|
||||||
$this->setId(substr(hash('md5', $data), 0, 16));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get instance data.
|
* Get instance data.
|
||||||
*
|
*
|
||||||
* @access public
|
* @access public
|
||||||
* @return stdClass
|
* @return array
|
||||||
*/
|
*/
|
||||||
abstract public function get();
|
public function get()
|
||||||
|
{
|
||||||
|
return $this->_data;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Store the instance's data.
|
* Store the instance's data.
|
||||||
|
@ -161,4 +158,24 @@ abstract class AbstractModel
|
||||||
{
|
{
|
||||||
return (bool) preg_match('#\A[a-f\d]{16}\z#', (string) $id);
|
return (bool) preg_match('#\A[a-f\d]{16}\z#', (string) $id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sanitizes data to conform with current configuration.
|
||||||
|
*
|
||||||
|
* @access protected
|
||||||
|
* @param array $data
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
abstract protected function _sanitize(array $data);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate data.
|
||||||
|
*
|
||||||
|
* @access protected
|
||||||
|
* @param array $data
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
protected function _validate(array $data)
|
||||||
|
{
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,6 @@ namespace PrivateBin\Model;
|
||||||
use Exception;
|
use Exception;
|
||||||
use Identicon\Identicon;
|
use Identicon\Identicon;
|
||||||
use PrivateBin\Persistence\TrafficLimiter;
|
use PrivateBin\Persistence\TrafficLimiter;
|
||||||
use PrivateBin\Sjcl;
|
|
||||||
use PrivateBin\Vizhash16x16;
|
use PrivateBin\Vizhash16x16;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -33,29 +32,6 @@ class Comment extends AbstractModel
|
||||||
*/
|
*/
|
||||||
private $_paste;
|
private $_paste;
|
||||||
|
|
||||||
/**
|
|
||||||
* Get comment data.
|
|
||||||
*
|
|
||||||
* @access public
|
|
||||||
* @throws Exception
|
|
||||||
* @return \stdClass
|
|
||||||
*/
|
|
||||||
public function get()
|
|
||||||
{
|
|
||||||
// @todo add support to read specific comment
|
|
||||||
$comments = $this->_store->readComments($this->getPaste()->getId());
|
|
||||||
foreach ($comments as $comment) {
|
|
||||||
if (
|
|
||||||
$comment->parentid == $this->getParentId() &&
|
|
||||||
$comment->id == $this->getId()
|
|
||||||
) {
|
|
||||||
$this->_data = $comment;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return $this->_data;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Store the comment's data.
|
* Store the comment's data.
|
||||||
*
|
*
|
||||||
|
@ -80,7 +56,7 @@ class Comment extends AbstractModel
|
||||||
throw new Exception('You are unlucky. Try again.', 69);
|
throw new Exception('You are unlucky. Try again.', 69);
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->_data->meta->postdate = time();
|
$this->_data['meta']['created'] = time();
|
||||||
|
|
||||||
// store comment
|
// store comment
|
||||||
if (
|
if (
|
||||||
|
@ -88,7 +64,7 @@ class Comment extends AbstractModel
|
||||||
$pasteid,
|
$pasteid,
|
||||||
$this->getParentId(),
|
$this->getParentId(),
|
||||||
$this->getId(),
|
$this->getId(),
|
||||||
json_decode(json_encode($this->_data), true)
|
$this->_data
|
||||||
) === false
|
) === false
|
||||||
) {
|
) {
|
||||||
throw new Exception('Error saving comment. Sorry.', 70);
|
throw new Exception('Error saving comment. Sorry.', 70);
|
||||||
|
@ -130,8 +106,8 @@ class Comment extends AbstractModel
|
||||||
*/
|
*/
|
||||||
public function setPaste(Paste $paste)
|
public function setPaste(Paste $paste)
|
||||||
{
|
{
|
||||||
$this->_paste = $paste;
|
$this->_paste = $paste;
|
||||||
$this->_data->meta->pasteid = $paste->getId();
|
$this->_data['pasteid'] = $paste->getId();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -157,7 +133,7 @@ class Comment extends AbstractModel
|
||||||
if (!self::isValidId($id)) {
|
if (!self::isValidId($id)) {
|
||||||
throw new Exception('Invalid paste ID.', 65);
|
throw new Exception('Invalid paste ID.', 65);
|
||||||
}
|
}
|
||||||
$this->_data->meta->parentid = $id;
|
$this->_data['parentid'] = $id;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -168,29 +144,22 @@ class Comment extends AbstractModel
|
||||||
*/
|
*/
|
||||||
public function getParentId()
|
public function getParentId()
|
||||||
{
|
{
|
||||||
if (!property_exists($this->_data->meta, 'parentid')) {
|
if (!array_key_exists('parentid', $this->_data)) {
|
||||||
$this->_data->meta->parentid = '';
|
$this->_data['parentid'] = $this->getPaste()->getId();
|
||||||
}
|
}
|
||||||
return $this->_data->meta->parentid;
|
return $this->_data['parentid'];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set nickname.
|
* Sanitizes data to conform with current configuration.
|
||||||
*
|
*
|
||||||
* @access public
|
* @access protected
|
||||||
* @param string $nickname
|
* @param array $data
|
||||||
* @throws Exception
|
* @return array
|
||||||
*/
|
*/
|
||||||
public function setNickname($nickname)
|
protected function _sanitize(array $data)
|
||||||
{
|
{
|
||||||
if (!Sjcl::isValid($nickname)) {
|
// we generate an icon based on a SHA512 HMAC of the users IP, if configured
|
||||||
throw new Exception('Invalid data.', 66);
|
|
||||||
}
|
|
||||||
$this->_data->meta->nickname = $nickname;
|
|
||||||
|
|
||||||
// If a nickname is provided, we generate an icon based on a SHA512 HMAC
|
|
||||||
// of the users IP. (We assume that if the user did not enter a nickname,
|
|
||||||
// the user wants to be anonymous and we will not generate an icon.)
|
|
||||||
$icon = $this->_conf->getKey('icon');
|
$icon = $this->_conf->getKey('icon');
|
||||||
if ($icon != 'none') {
|
if ($icon != 'none') {
|
||||||
$pngdata = '';
|
$pngdata = '';
|
||||||
|
@ -205,9 +174,12 @@ class Comment extends AbstractModel
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if ($pngdata != '') {
|
if ($pngdata != '') {
|
||||||
$this->_data->meta->vizhash = $pngdata;
|
if (!array_key_exists('meta', $data)) {
|
||||||
|
$data['meta'] = array();
|
||||||
|
}
|
||||||
|
$data['meta']['icon'] = $pngdata;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Once the icon is generated, we do not keep the IP address hash.
|
return $data;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,6 @@ namespace PrivateBin\Model;
|
||||||
use Exception;
|
use Exception;
|
||||||
use PrivateBin\Controller;
|
use PrivateBin\Controller;
|
||||||
use PrivateBin\Persistence\ServerSalt;
|
use PrivateBin\Persistence\ServerSalt;
|
||||||
use PrivateBin\Sjcl;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Paste
|
* Paste
|
||||||
|
@ -28,8 +27,8 @@ class Paste extends AbstractModel
|
||||||
* Get paste data.
|
* Get paste data.
|
||||||
*
|
*
|
||||||
* @access public
|
* @access public
|
||||||
* @throws \Exception
|
* @throws Exception
|
||||||
* @return \stdClass
|
* @return array
|
||||||
*/
|
*/
|
||||||
public function get()
|
public function get()
|
||||||
{
|
{
|
||||||
|
@ -39,44 +38,43 @@ class Paste extends AbstractModel
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if paste has expired and delete it if neccessary.
|
// check if paste has expired and delete it if neccessary.
|
||||||
if (property_exists($data->meta, 'expire_date')) {
|
if (array_key_exists('expire_date', $data['meta'])) {
|
||||||
if ($data->meta->expire_date < time()) {
|
if ($data['meta']['expire_date'] < time()) {
|
||||||
$this->delete();
|
$this->delete();
|
||||||
throw new Exception(Controller::GENERIC_ERROR, 63);
|
throw new Exception(Controller::GENERIC_ERROR, 63);
|
||||||
}
|
}
|
||||||
// We kindly provide the remaining time before expiration (in seconds)
|
// We kindly provide the remaining time before expiration (in seconds)
|
||||||
$data->meta->remaining_time = $data->meta->expire_date - time();
|
$data['meta']['time_to_live'] = $data['meta']['expire_date'] - time();
|
||||||
|
unset($data['meta']['expire_date']);
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if non-expired burn after reading paste needs to be deleted
|
// check if non-expired burn after reading paste needs to be deleted
|
||||||
if (property_exists($data->meta, 'burnafterreading') && $data->meta->burnafterreading) {
|
if (
|
||||||
|
(array_key_exists('adata', $data) && $data['adata'][3] === 1) ||
|
||||||
|
(array_key_exists('burnafterreading', $data['meta']) && $data['meta']['burnafterreading'])
|
||||||
|
) {
|
||||||
$this->delete();
|
$this->delete();
|
||||||
}
|
}
|
||||||
|
|
||||||
// set formatter for for the view.
|
// set formatter for the view in version 1 pastes.
|
||||||
if (!property_exists($data->meta, 'formatter')) {
|
if (array_key_exists('data', $data) && !array_key_exists('formatter', $data['meta'])) {
|
||||||
// support < 0.21 syntax highlighting
|
// support < 0.21 syntax highlighting
|
||||||
if (property_exists($data->meta, 'syntaxcoloring') && $data->meta->syntaxcoloring === true) {
|
if (array_key_exists('syntaxcoloring', $data['meta']) && $data['meta']['syntaxcoloring'] === true) {
|
||||||
$data->meta->formatter = 'syntaxhighlighting';
|
$data['meta']['formatter'] = 'syntaxhighlighting';
|
||||||
} else {
|
} else {
|
||||||
$data->meta->formatter = $this->_conf->getKey('defaultformatter');
|
$data['meta']['formatter'] = $this->_conf->getKey('defaultformatter');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// support old paste format with server wide salt
|
// support old paste format with server wide salt
|
||||||
if (!property_exists($data->meta, 'salt')) {
|
if (!array_key_exists('salt', $data['meta'])) {
|
||||||
$data->meta->salt = ServerSalt::get();
|
$data['meta']['salt'] = ServerSalt::get();
|
||||||
}
|
|
||||||
$data->comments = array_values($this->getComments());
|
|
||||||
$data->comment_count = count($data->comments);
|
|
||||||
$data->comment_offset = 0;
|
|
||||||
$data->{'@context'} = 'js/paste.jsonld';
|
|
||||||
$this->_data = $data;
|
|
||||||
|
|
||||||
// If the paste was meant to be read only once, delete it.
|
|
||||||
if ($this->isBurnafterreading()) {
|
|
||||||
$this->delete();
|
|
||||||
}
|
}
|
||||||
|
$data['comments'] = array_values($this->getComments());
|
||||||
|
$data['comment_count'] = count($data['comments']);
|
||||||
|
$data['comment_offset'] = 0;
|
||||||
|
$data['@context'] = 'js/paste.jsonld';
|
||||||
|
$this->_data = $data;
|
||||||
|
|
||||||
return $this->_data;
|
return $this->_data;
|
||||||
}
|
}
|
||||||
|
@ -94,14 +92,14 @@ class Paste extends AbstractModel
|
||||||
throw new Exception('You are unlucky. Try again.', 75);
|
throw new Exception('You are unlucky. Try again.', 75);
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->_data->meta->postdate = time();
|
$this->_data['meta']['created'] = time();
|
||||||
$this->_data->meta->salt = serversalt::generate();
|
$this->_data['meta']['salt'] = serversalt::generate();
|
||||||
|
|
||||||
// store paste
|
// store paste
|
||||||
if (
|
if (
|
||||||
$this->_store->create(
|
$this->_store->create(
|
||||||
$this->getId(),
|
$this->getId(),
|
||||||
json_decode(json_encode($this->_data), true)
|
$this->_data
|
||||||
) === false
|
) === false
|
||||||
) {
|
) {
|
||||||
throw new Exception('Error saving paste. Sorry.', 76);
|
throw new Exception('Error saving paste. Sorry.', 76);
|
||||||
|
@ -139,7 +137,7 @@ class Paste extends AbstractModel
|
||||||
* @throws Exception
|
* @throws Exception
|
||||||
* @return Comment
|
* @return Comment
|
||||||
*/
|
*/
|
||||||
public function getComment($parentId, $commentId = null)
|
public function getComment($parentId, $commentId = '')
|
||||||
{
|
{
|
||||||
if (!$this->exists()) {
|
if (!$this->exists()) {
|
||||||
throw new Exception('Invalid data.', 62);
|
throw new Exception('Invalid data.', 62);
|
||||||
|
@ -147,7 +145,7 @@ class Paste extends AbstractModel
|
||||||
$comment = new Comment($this->_conf, $this->_store);
|
$comment = new Comment($this->_conf, $this->_store);
|
||||||
$comment->setPaste($this);
|
$comment->setPaste($this);
|
||||||
$comment->setParentId($parentId);
|
$comment->setParentId($parentId);
|
||||||
if ($commentId !== null) {
|
if ($commentId !== '') {
|
||||||
$comment->setId($commentId);
|
$comment->setId($commentId);
|
||||||
}
|
}
|
||||||
return $comment;
|
return $comment;
|
||||||
|
@ -176,140 +174,16 @@ class Paste extends AbstractModel
|
||||||
*/
|
*/
|
||||||
public function getDeleteToken()
|
public function getDeleteToken()
|
||||||
{
|
{
|
||||||
if (!property_exists($this->_data->meta, 'salt')) {
|
if (!array_key_exists('salt', $this->_data['meta'])) {
|
||||||
$this->get();
|
$this->get();
|
||||||
}
|
}
|
||||||
return hash_hmac(
|
return hash_hmac(
|
||||||
$this->_conf->getKey('zerobincompatibility') ? 'sha1' : 'sha256',
|
$this->_conf->getKey('zerobincompatibility') ? 'sha1' : 'sha256',
|
||||||
$this->getId(),
|
$this->getId(),
|
||||||
$this->_data->meta->salt
|
$this->_data['meta']['salt']
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Set paste's attachment.
|
|
||||||
*
|
|
||||||
* @access public
|
|
||||||
* @param string $attachment
|
|
||||||
* @throws Exception
|
|
||||||
*/
|
|
||||||
public function setAttachment($attachment)
|
|
||||||
{
|
|
||||||
if (!$this->_conf->getKey('fileupload') || !Sjcl::isValid($attachment)) {
|
|
||||||
throw new Exception('Invalid attachment.', 71);
|
|
||||||
}
|
|
||||||
$this->_data->meta->attachment = $attachment;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set paste's attachment name.
|
|
||||||
*
|
|
||||||
* @access public
|
|
||||||
* @param string $attachmentname
|
|
||||||
* @throws Exception
|
|
||||||
*/
|
|
||||||
public function setAttachmentName($attachmentname)
|
|
||||||
{
|
|
||||||
if (!$this->_conf->getKey('fileupload') || !Sjcl::isValid($attachmentname)) {
|
|
||||||
throw new Exception('Invalid attachment.', 72);
|
|
||||||
}
|
|
||||||
$this->_data->meta->attachmentname = $attachmentname;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set paste expiration.
|
|
||||||
*
|
|
||||||
* @access public
|
|
||||||
* @param string $expiration
|
|
||||||
*/
|
|
||||||
public function setExpiration($expiration)
|
|
||||||
{
|
|
||||||
$expire_options = $this->_conf->getSection('expire_options');
|
|
||||||
if (array_key_exists($expiration, $expire_options)) {
|
|
||||||
$expire = $expire_options[$expiration];
|
|
||||||
} else {
|
|
||||||
// using getKey() to ensure a default value is present
|
|
||||||
$expire = $this->_conf->getKey($this->_conf->getKey('default', 'expire'), 'expire_options');
|
|
||||||
}
|
|
||||||
if ($expire > 0) {
|
|
||||||
$this->_data->meta->expire_date = time() + $expire;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set paste's burn-after-reading type.
|
|
||||||
*
|
|
||||||
* @access public
|
|
||||||
* @param string $burnafterreading
|
|
||||||
* @throws Exception
|
|
||||||
*/
|
|
||||||
public function setBurnafterreading($burnafterreading = '1')
|
|
||||||
{
|
|
||||||
if ($burnafterreading === '0') {
|
|
||||||
$this->_data->meta->burnafterreading = false;
|
|
||||||
} else {
|
|
||||||
if ($burnafterreading !== '1') {
|
|
||||||
throw new Exception('Invalid data.', 73);
|
|
||||||
}
|
|
||||||
$this->_data->meta->burnafterreading = true;
|
|
||||||
$this->_data->meta->opendiscussion = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set paste's discussion state.
|
|
||||||
*
|
|
||||||
* @access public
|
|
||||||
* @param string $opendiscussion
|
|
||||||
* @throws Exception
|
|
||||||
*/
|
|
||||||
public function setOpendiscussion($opendiscussion = '1')
|
|
||||||
{
|
|
||||||
if (
|
|
||||||
!$this->_conf->getKey('discussion') ||
|
|
||||||
$this->isBurnafterreading() ||
|
|
||||||
$opendiscussion === '0'
|
|
||||||
) {
|
|
||||||
$this->_data->meta->opendiscussion = false;
|
|
||||||
} else {
|
|
||||||
if ($opendiscussion !== '1') {
|
|
||||||
throw new Exception('Invalid data.', 74);
|
|
||||||
}
|
|
||||||
$this->_data->meta->opendiscussion = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set paste's format.
|
|
||||||
*
|
|
||||||
* @access public
|
|
||||||
* @param string $format
|
|
||||||
* @throws Exception
|
|
||||||
*/
|
|
||||||
public function setFormatter($format)
|
|
||||||
{
|
|
||||||
if (!array_key_exists($format, $this->_conf->getSection('formatter_options'))) {
|
|
||||||
$format = $this->_conf->getKey('defaultformatter');
|
|
||||||
}
|
|
||||||
$this->_data->meta->formatter = $format;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if paste is of burn-after-reading type.
|
|
||||||
*
|
|
||||||
* @access public
|
|
||||||
* @throws Exception
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
public function isBurnafterreading()
|
|
||||||
{
|
|
||||||
if (!property_exists($this->_data, 'data')) {
|
|
||||||
$this->get();
|
|
||||||
}
|
|
||||||
return property_exists($this->_data->meta, 'burnafterreading') &&
|
|
||||||
$this->_data->meta->burnafterreading === true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if paste has discussions enabled.
|
* Check if paste has discussions enabled.
|
||||||
*
|
*
|
||||||
|
@ -319,10 +193,66 @@ class Paste extends AbstractModel
|
||||||
*/
|
*/
|
||||||
public function isOpendiscussion()
|
public function isOpendiscussion()
|
||||||
{
|
{
|
||||||
if (!property_exists($this->_data, 'data')) {
|
if (!array_key_exists('adata', $this->_data) && !array_key_exists('data', $this->_data)) {
|
||||||
$this->get();
|
$this->get();
|
||||||
}
|
}
|
||||||
return property_exists($this->_data->meta, 'opendiscussion') &&
|
return
|
||||||
$this->_data->meta->opendiscussion === true;
|
(array_key_exists('adata', $this->_data) && $this->_data['adata'][2] === 1) ||
|
||||||
|
(array_key_exists('opendiscussion', $this->_data['meta']) && $this->_data['meta']['opendiscussion']);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sanitizes data to conform with current configuration.
|
||||||
|
*
|
||||||
|
* @access protected
|
||||||
|
* @param array $data
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
protected function _sanitize(array $data)
|
||||||
|
{
|
||||||
|
$expiration = $data['meta']['expire'];
|
||||||
|
unset($data['meta']['expire']);
|
||||||
|
$expire_options = $this->_conf->getSection('expire_options');
|
||||||
|
if (array_key_exists($expiration, $expire_options)) {
|
||||||
|
$expire = $expire_options[$expiration];
|
||||||
|
} else {
|
||||||
|
// using getKey() to ensure a default value is present
|
||||||
|
$expire = $this->_conf->getKey($this->_conf->getKey('default', 'expire'), 'expire_options');
|
||||||
|
}
|
||||||
|
if ($expire > 0) {
|
||||||
|
$data['meta']['expire_date'] = time() + $expire;
|
||||||
|
}
|
||||||
|
return $data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate data.
|
||||||
|
*
|
||||||
|
* @access protected
|
||||||
|
* @param array $data
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
protected function _validate(array $data)
|
||||||
|
{
|
||||||
|
// reject invalid or disabled formatters
|
||||||
|
if (!array_key_exists($data['adata'][1], $this->_conf->getSection('formatter_options'))) {
|
||||||
|
throw new Exception('Invalid data.', 75);
|
||||||
|
}
|
||||||
|
|
||||||
|
// discussion requested, but disabled in config or burn after reading requested as well, or invalid integer
|
||||||
|
if (
|
||||||
|
($data['adata'][2] === 1 && ( // open discussion flag
|
||||||
|
!$this->_conf->getKey('discussion') ||
|
||||||
|
$data['adata'][3] === 1 // burn after reading flag
|
||||||
|
)) ||
|
||||||
|
($data['adata'][2] !== 0 && $data['adata'][2] !== 1)
|
||||||
|
) {
|
||||||
|
throw new Exception('Invalid data.', 74);
|
||||||
|
}
|
||||||
|
|
||||||
|
// reject invalid burn after reading
|
||||||
|
if ($data['adata'][3] !== 0 && $data['adata'][3] !== 1) {
|
||||||
|
throw new Exception('Invalid data.', 73);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,7 +45,10 @@ class DataStore extends AbstractPersistence
|
||||||
$filename = substr($filename, strlen($path));
|
$filename = substr($filename, strlen($path));
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
self::_store($filename, self::PROTECTION_LINE . PHP_EOL . Json::encode($data));
|
self::_store(
|
||||||
|
$filename,
|
||||||
|
self::PROTECTION_LINE . PHP_EOL . Json::encode($data)
|
||||||
|
);
|
||||||
return true;
|
return true;
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -58,11 +61,16 @@ class DataStore extends AbstractPersistence
|
||||||
* @access public
|
* @access public
|
||||||
* @static
|
* @static
|
||||||
* @param string $filename
|
* @param string $filename
|
||||||
* @return \stdClass|false $data
|
* @return array|false $data
|
||||||
*/
|
*/
|
||||||
public static function get($filename)
|
public static function get($filename)
|
||||||
{
|
{
|
||||||
return json_decode(substr(file_get_contents($filename), strlen(self::PROTECTION_LINE . PHP_EOL)));
|
return Json::decode(
|
||||||
|
substr(
|
||||||
|
file_get_contents($filename),
|
||||||
|
strlen(self::PROTECTION_LINE . PHP_EOL)
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -107,10 +107,10 @@ class Request
|
||||||
switch (array_key_exists('REQUEST_METHOD', $_SERVER) ? $_SERVER['REQUEST_METHOD'] : 'GET') {
|
switch (array_key_exists('REQUEST_METHOD', $_SERVER) ? $_SERVER['REQUEST_METHOD'] : 'GET') {
|
||||||
case 'DELETE':
|
case 'DELETE':
|
||||||
case 'PUT':
|
case 'PUT':
|
||||||
parse_str(file_get_contents(self::$_inputStream), $this->_params);
|
|
||||||
break;
|
|
||||||
case 'POST':
|
case 'POST':
|
||||||
$this->_params = $_POST;
|
$this->_params = Json::decode(
|
||||||
|
file_get_contents(self::$_inputStream)
|
||||||
|
);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
$this->_params = $_GET;
|
$this->_params = $_GET;
|
||||||
|
@ -126,8 +126,8 @@ class Request
|
||||||
|
|
||||||
// prepare operation, depending on current parameters
|
// prepare operation, depending on current parameters
|
||||||
if (
|
if (
|
||||||
(array_key_exists('data', $this->_params) && !empty($this->_params['data'])) ||
|
array_key_exists('ct', $this->_params) &&
|
||||||
(array_key_exists('attachment', $this->_params) && !empty($this->_params['attachment']))
|
!empty($this->_params['ct'])
|
||||||
) {
|
) {
|
||||||
$this->_operation = 'create';
|
$this->_operation = 'create';
|
||||||
} elseif (array_key_exists('pasteid', $this->_params) && !empty($this->_params['pasteid'])) {
|
} elseif (array_key_exists('pasteid', $this->_params) && !empty($this->_params['pasteid'])) {
|
||||||
|
@ -152,6 +152,33 @@ class Request
|
||||||
return $this->_operation;
|
return $this->_operation;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get data of paste or comment
|
||||||
|
*
|
||||||
|
* @access public
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getData()
|
||||||
|
{
|
||||||
|
$data = array(
|
||||||
|
'adata' => $this->getParam('adata'),
|
||||||
|
);
|
||||||
|
$required_keys = array('v', 'ct');
|
||||||
|
$meta = $this->getParam('meta');
|
||||||
|
if (empty($meta)) {
|
||||||
|
$required_keys[] = 'pasteid';
|
||||||
|
$required_keys[] = 'parentid';
|
||||||
|
} else {
|
||||||
|
$data['meta'] = $meta;
|
||||||
|
}
|
||||||
|
foreach ($required_keys as $key) {
|
||||||
|
$data[$key] = $this->getParam($key);
|
||||||
|
}
|
||||||
|
// forcing a cast to int or float
|
||||||
|
$data['v'] = $data['v'] + 0;
|
||||||
|
return $data;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a request parameter
|
* Get a request parameter
|
||||||
*
|
*
|
||||||
|
@ -175,7 +202,9 @@ class Request
|
||||||
public function getRequestUri()
|
public function getRequestUri()
|
||||||
{
|
{
|
||||||
return array_key_exists('REQUEST_URI', $_SERVER) ?
|
return array_key_exists('REQUEST_URI', $_SERVER) ?
|
||||||
htmlspecialchars($_SERVER['REQUEST_URI']) : '/';
|
htmlspecialchars(
|
||||||
|
parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH)
|
||||||
|
) : '/';
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
103
lib/Sjcl.php
103
lib/Sjcl.php
|
@ -1,103 +0,0 @@
|
||||||
<?php
|
|
||||||
/**
|
|
||||||
* PrivateBin
|
|
||||||
*
|
|
||||||
* 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
|
|
||||||
* @version 1.2.1
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace PrivateBin;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sjcl
|
|
||||||
*
|
|
||||||
* Provides SJCL validation function.
|
|
||||||
*/
|
|
||||||
class Sjcl
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* SJCL validator
|
|
||||||
*
|
|
||||||
* Checks if a json string is a proper SJCL encrypted message.
|
|
||||||
*
|
|
||||||
* @access public
|
|
||||||
* @static
|
|
||||||
* @param string $encoded JSON
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
public static function isValid($encoded)
|
|
||||||
{
|
|
||||||
$accepted_keys = array('iv', 'v', 'iter', 'ks', 'ts', 'mode', 'adata', 'cipher', 'salt', 'ct');
|
|
||||||
|
|
||||||
// Make sure content is valid json
|
|
||||||
$decoded = json_decode($encoded);
|
|
||||||
if (is_null($decoded)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
$decoded = (array) $decoded;
|
|
||||||
|
|
||||||
// Make sure no additionnal keys were added.
|
|
||||||
if (
|
|
||||||
count(array_keys($decoded)) != count($accepted_keys)
|
|
||||||
) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make sure required fields are present and contain base64 data.
|
|
||||||
foreach ($accepted_keys as $k) {
|
|
||||||
if (!array_key_exists($k, $decoded)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make sure some fields are base64 data.
|
|
||||||
if (!base64_decode($decoded['iv'], true)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (!base64_decode($decoded['salt'], true)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (!($ct = base64_decode($decoded['ct'], true))) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make sure some fields have a reasonable size.
|
|
||||||
if (strlen($decoded['iv']) > 24) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (strlen($decoded['salt']) > 14) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make sure some fields contain no unsupported values.
|
|
||||||
if (!(is_int($decoded['v']) || is_float($decoded['v'])) || (float) $decoded['v'] < 1) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (!is_int($decoded['iter']) || $decoded['iter'] <= 100) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (!in_array($decoded['ks'], array(128, 192, 256), true)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (!in_array($decoded['ts'], array(64, 96, 128), true)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (!in_array($decoded['mode'], array('ccm', 'ocb2', 'gcm'), true)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if ($decoded['cipher'] !== 'aes') {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reject data if entropy is too low
|
|
||||||
if (strlen($ct) > strlen(gzdeflate($ct))) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -43,7 +43,6 @@ endif;
|
||||||
?>
|
?>
|
||||||
<noscript><link type="text/css" rel="stylesheet" href="css/noscript.css" /></noscript>
|
<noscript><link type="text/css" rel="stylesheet" href="css/noscript.css" /></noscript>
|
||||||
<script type="text/javascript" data-cfasync="false" src="js/jquery-3.3.1.js" integrity="sha512-+NqPlbbtM1QqiK8ZAo4Yrj2c4lNQoGv8P79DPtKzj++l5jnN39rHA/xsqn8zE9l0uSoxaCdrOgFs6yjyfbBxSg==" crossorigin="anonymous"></script>
|
<script type="text/javascript" data-cfasync="false" src="js/jquery-3.3.1.js" integrity="sha512-+NqPlbbtM1QqiK8ZAo4Yrj2c4lNQoGv8P79DPtKzj++l5jnN39rHA/xsqn8zE9l0uSoxaCdrOgFs6yjyfbBxSg==" crossorigin="anonymous"></script>
|
||||||
<script type="text/javascript" data-cfasync="false" src="js/sjcl-1.0.7.js" integrity="sha512-J2eNenPwyfXkMVNMFz9Q54kKfYi5AA3mQWpNgtjSJzsKHtpbhUt/7bvcjGwwmzE8ZUVWMI/ndagIX1lG+SfxGA==" crossorigin="anonymous"></script>
|
|
||||||
<?php
|
<?php
|
||||||
if ($QRCODE):
|
if ($QRCODE):
|
||||||
?>
|
?>
|
||||||
|
@ -54,14 +53,10 @@ if ($ZEROBINCOMPATIBILITY):
|
||||||
?>
|
?>
|
||||||
<script type="text/javascript" data-cfasync="false" src="js/base64-1.7.js" integrity="sha512-JdwsSP3GyHR+jaCkns9CL9NTt4JUJqm/BsODGmYhBcj5EAPKcHYh+OiMfyHbcDLECe17TL0hjXADFkusAqiYgA==" crossorigin="anonymous"></script>
|
<script type="text/javascript" data-cfasync="false" src="js/base64-1.7.js" integrity="sha512-JdwsSP3GyHR+jaCkns9CL9NTt4JUJqm/BsODGmYhBcj5EAPKcHYh+OiMfyHbcDLECe17TL0hjXADFkusAqiYgA==" crossorigin="anonymous"></script>
|
||||||
<?php
|
<?php
|
||||||
else:
|
|
||||||
?>
|
|
||||||
<script type="text/javascript" data-cfasync="false" src="js/base64-2.4.5.js" integrity="sha512-YINE6agO8ZrYuzlrZZwQJTu0uqURJDxD4gjsfZ6mV4fP2gW5j8giNJ734iyJVTBrnF2XMiUBM/DSi7ON1V5RMQ==" crossorigin="anonymous"></script>
|
|
||||||
<?php
|
|
||||||
endif;
|
endif;
|
||||||
?>
|
?>
|
||||||
|
<script type="text/javascript" data-cfasync="false" src="js/zlib-1.2.11.js" integrity="sha512-Yey/0yoaVmSbqMEyyff3DIu8kCPwpHvHf7tY1AuZ1lrX9NPCMg87PwzngMi+VNbe4ilCApmePeuKT869RTcyCQ==" crossorigin="anonymous"></script>
|
||||||
<script type="text/javascript" data-cfasync="false" src="js/base-x-3.0.5.1.js" integrity="sha512-/zL3MWKMtl1IBF0URx3laql2jUw+rWfFFabNlILY/Qm+hUsQR/XULjUyNHkW/FkrV7A0sMQ7tsppH7sj5ht8wA==" crossorigin="anonymous"></script>
|
<script type="text/javascript" data-cfasync="false" src="js/base-x-3.0.5.1.js" integrity="sha512-/zL3MWKMtl1IBF0URx3laql2jUw+rWfFFabNlILY/Qm+hUsQR/XULjUyNHkW/FkrV7A0sMQ7tsppH7sj5ht8wA==" crossorigin="anonymous"></script>
|
||||||
<script type="text/javascript" data-cfasync="false" src="js/rawdeflate-0.5.js" integrity="sha512-tTdZ7qMr7tt5VQy4iCHu6/aGB12eRwbUy+AEI5rXntfsjcRfBeeqJloMsBU9FrGk1bIYLiuND/FhU42LO1bi0g==" crossorigin="anonymous"></script>
|
|
||||||
<script type="text/javascript" data-cfasync="false" src="js/rawinflate-0.3.js" integrity="sha512-g8uelGgJW9A/Z1tB6Izxab++oj5kdD7B4qC7DHwZkB6DGMXKyzx7v5mvap2HXueI2IIn08YlRYM56jwWdm2ucQ==" crossorigin="anonymous"></script>
|
<script type="text/javascript" data-cfasync="false" src="js/rawinflate-0.3.js" integrity="sha512-g8uelGgJW9A/Z1tB6Izxab++oj5kdD7B4qC7DHwZkB6DGMXKyzx7v5mvap2HXueI2IIn08YlRYM56jwWdm2ucQ==" crossorigin="anonymous"></script>
|
||||||
<script type="text/javascript" data-cfasync="false" src="js/bootstrap-3.3.7.js" integrity="sha512-iztkobsvnjKfAtTNdHkGVjAYTrrtlC7mGp/54c40wowO7LhURYl3gVzzcEqGl/qKXQltJ2HwMrdLcNUdo+N/RQ==" crossorigin="anonymous"></script>
|
<script type="text/javascript" data-cfasync="false" src="js/bootstrap-3.3.7.js" integrity="sha512-iztkobsvnjKfAtTNdHkGVjAYTrrtlC7mGp/54c40wowO7LhURYl3gVzzcEqGl/qKXQltJ2HwMrdLcNUdo+N/RQ==" crossorigin="anonymous"></script>
|
||||||
<?php
|
<?php
|
||||||
|
@ -77,7 +72,7 @@ if ($MARKDOWN):
|
||||||
endif;
|
endif;
|
||||||
?>
|
?>
|
||||||
<script type="text/javascript" data-cfasync="false" src="js/purify-1.0.7.js" integrity="sha512-VnKJHLosO8z2ojNvWk9BEKYqnhZyWK9rM90FgZUUEp/PRnUqR5OLLKE0a3BkVmn7YgB7LXRrjHgFHQYKd6DAIA==" crossorigin="anonymous"></script>
|
<script type="text/javascript" data-cfasync="false" src="js/purify-1.0.7.js" integrity="sha512-VnKJHLosO8z2ojNvWk9BEKYqnhZyWK9rM90FgZUUEp/PRnUqR5OLLKE0a3BkVmn7YgB7LXRrjHgFHQYKd6DAIA==" crossorigin="anonymous"></script>
|
||||||
<script type="text/javascript" data-cfasync="false" src="js/privatebin.js?<?php echo rawurlencode($VERSION); ?>" integrity="sha512-IFu+B5qYMnATrGKpfxXw3TdUoxBV31HiaGGP76RknlT62iiugvTHikKX9yW5AxqaRd+12mefuoF80GXgoa35Tg==" crossorigin="anonymous"></script>
|
<script type="text/javascript" data-cfasync="false" src="js/privatebin.js?<?php echo rawurlencode($VERSION); ?>" integrity="sha512-2I6gqibyMdzEM03U4c4T2h0Yv1omWkPT16VUURnv8s/rfTPIh/r9+GOKttWoaJUXYFJgJLWNkgzJRErPb53DDQ==" crossorigin="anonymous"></script>
|
||||||
<!--[if lt IE 10]>
|
<!--[if lt IE 10]>
|
||||||
<style type="text/css">body {padding-left:60px;padding-right:60px;} #ienotice {display:block;} #oldienotice {display:block;}</style>
|
<style type="text/css">body {padding-left:60px;padding-right:60px;} #ienotice {display:block;} #oldienotice {display:block;}</style>
|
||||||
<![endif]-->
|
<![endif]-->
|
||||||
|
|
|
@ -22,7 +22,6 @@ if ($SYNTAXHIGHLIGHTING):
|
||||||
endif;
|
endif;
|
||||||
?>
|
?>
|
||||||
<script type="text/javascript" data-cfasync="false" src="js/jquery-3.3.1.js" integrity="sha512-+NqPlbbtM1QqiK8ZAo4Yrj2c4lNQoGv8P79DPtKzj++l5jnN39rHA/xsqn8zE9l0uSoxaCdrOgFs6yjyfbBxSg==" crossorigin="anonymous"></script>
|
<script type="text/javascript" data-cfasync="false" src="js/jquery-3.3.1.js" integrity="sha512-+NqPlbbtM1QqiK8ZAo4Yrj2c4lNQoGv8P79DPtKzj++l5jnN39rHA/xsqn8zE9l0uSoxaCdrOgFs6yjyfbBxSg==" crossorigin="anonymous"></script>
|
||||||
<script type="text/javascript" data-cfasync="false" src="js/sjcl-1.0.7.js" integrity="sha512-J2eNenPwyfXkMVNMFz9Q54kKfYi5AA3mQWpNgtjSJzsKHtpbhUt/7bvcjGwwmzE8ZUVWMI/ndagIX1lG+SfxGA==" crossorigin="anonymous"></script>
|
|
||||||
<?php
|
<?php
|
||||||
if ($QRCODE):
|
if ($QRCODE):
|
||||||
?>
|
?>
|
||||||
|
@ -33,14 +32,10 @@ if ($ZEROBINCOMPATIBILITY):
|
||||||
?>
|
?>
|
||||||
<script type="text/javascript" data-cfasync="false" src="js/base64-1.7.js" integrity="sha512-JdwsSP3GyHR+jaCkns9CL9NTt4JUJqm/BsODGmYhBcj5EAPKcHYh+OiMfyHbcDLECe17TL0hjXADFkusAqiYgA==" crossorigin="anonymous"></script>
|
<script type="text/javascript" data-cfasync="false" src="js/base64-1.7.js" integrity="sha512-JdwsSP3GyHR+jaCkns9CL9NTt4JUJqm/BsODGmYhBcj5EAPKcHYh+OiMfyHbcDLECe17TL0hjXADFkusAqiYgA==" crossorigin="anonymous"></script>
|
||||||
<?php
|
<?php
|
||||||
else:
|
|
||||||
?>
|
|
||||||
<script type="text/javascript" data-cfasync="false" src="js/base64-2.4.5.js" integrity="sha512-YINE6agO8ZrYuzlrZZwQJTu0uqURJDxD4gjsfZ6mV4fP2gW5j8giNJ734iyJVTBrnF2XMiUBM/DSi7ON1V5RMQ==" crossorigin="anonymous"></script>
|
|
||||||
<?php
|
|
||||||
endif;
|
endif;
|
||||||
?>
|
?>
|
||||||
|
<script type="text/javascript" data-cfasync="false" src="js/zlib-1.2.11.js" integrity="sha512-Yey/0yoaVmSbqMEyyff3DIu8kCPwpHvHf7tY1AuZ1lrX9NPCMg87PwzngMi+VNbe4ilCApmePeuKT869RTcyCQ==" crossorigin="anonymous"></script>
|
||||||
<script type="text/javascript" data-cfasync="false" src="js/base-x-3.0.5.1.js" integrity="sha512-/zL3MWKMtl1IBF0URx3laql2jUw+rWfFFabNlILY/Qm+hUsQR/XULjUyNHkW/FkrV7A0sMQ7tsppH7sj5ht8wA==" crossorigin="anonymous"></script>
|
<script type="text/javascript" data-cfasync="false" src="js/base-x-3.0.5.1.js" integrity="sha512-/zL3MWKMtl1IBF0URx3laql2jUw+rWfFFabNlILY/Qm+hUsQR/XULjUyNHkW/FkrV7A0sMQ7tsppH7sj5ht8wA==" crossorigin="anonymous"></script>
|
||||||
<script type="text/javascript" data-cfasync="false" src="js/rawdeflate-0.5.js" integrity="sha512-tTdZ7qMr7tt5VQy4iCHu6/aGB12eRwbUy+AEI5rXntfsjcRfBeeqJloMsBU9FrGk1bIYLiuND/FhU42LO1bi0g==" crossorigin="anonymous"></script>
|
|
||||||
<script type="text/javascript" data-cfasync="false" src="js/rawinflate-0.3.js" integrity="sha512-g8uelGgJW9A/Z1tB6Izxab++oj5kdD7B4qC7DHwZkB6DGMXKyzx7v5mvap2HXueI2IIn08YlRYM56jwWdm2ucQ==" crossorigin="anonymous"></script>
|
<script type="text/javascript" data-cfasync="false" src="js/rawinflate-0.3.js" integrity="sha512-g8uelGgJW9A/Z1tB6Izxab++oj5kdD7B4qC7DHwZkB6DGMXKyzx7v5mvap2HXueI2IIn08YlRYM56jwWdm2ucQ==" crossorigin="anonymous"></script>
|
||||||
<?php
|
<?php
|
||||||
if ($SYNTAXHIGHLIGHTING):
|
if ($SYNTAXHIGHLIGHTING):
|
||||||
|
@ -55,7 +50,7 @@ if ($MARKDOWN):
|
||||||
endif;
|
endif;
|
||||||
?>
|
?>
|
||||||
<script type="text/javascript" data-cfasync="false" src="js/purify-1.0.7.js" integrity="sha512-VnKJHLosO8z2ojNvWk9BEKYqnhZyWK9rM90FgZUUEp/PRnUqR5OLLKE0a3BkVmn7YgB7LXRrjHgFHQYKd6DAIA==" crossorigin="anonymous"></script>
|
<script type="text/javascript" data-cfasync="false" src="js/purify-1.0.7.js" integrity="sha512-VnKJHLosO8z2ojNvWk9BEKYqnhZyWK9rM90FgZUUEp/PRnUqR5OLLKE0a3BkVmn7YgB7LXRrjHgFHQYKd6DAIA==" crossorigin="anonymous"></script>
|
||||||
<script type="text/javascript" data-cfasync="false" src="js/privatebin.js?<?php echo rawurlencode($VERSION); ?>" integrity="sha512-IFu+B5qYMnATrGKpfxXw3TdUoxBV31HiaGGP76RknlT62iiugvTHikKX9yW5AxqaRd+12mefuoF80GXgoa35Tg==" crossorigin="anonymous"></script>
|
<script type="text/javascript" data-cfasync="false" src="js/privatebin.js?<?php echo rawurlencode($VERSION); ?>" integrity="sha512-2I6gqibyMdzEM03U4c4T2h0Yv1omWkPT16VUURnv8s/rfTPIh/r9+GOKttWoaJUXYFJgJLWNkgzJRErPb53DDQ==" crossorigin="anonymous"></script>
|
||||||
<!--[if lt IE 10]>
|
<!--[if lt IE 10]>
|
||||||
<style type="text/css">body {padding-left:60px;padding-right:60px;} #ienotice {display:block;} #oldienotice {display:block;}</style>
|
<style type="text/css">body {padding-left:60px;padding-right:60px;} #ienotice {display:block;} #oldienotice {display:block;}</style>
|
||||||
<![endif]-->
|
<![endif]-->
|
||||||
|
|
|
@ -28,14 +28,14 @@ class Helper
|
||||||
*
|
*
|
||||||
* @var string
|
* @var string
|
||||||
*/
|
*/
|
||||||
private static $pasteid = '5e9bc25c89fb3bf9';
|
private static $pasteid = '5b65a01b43987bc2';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* example paste
|
* example paste version 1
|
||||||
*
|
*
|
||||||
* @var array
|
* @var array
|
||||||
*/
|
*/
|
||||||
private static $paste = array(
|
private static $pasteV1 = array(
|
||||||
'data' => '{"iv":"EN39/wd5Nk8HAiSG2K5AsQ","v":1,"iter":1000,"ks":128,"ts":64,"mode":"ccm","adata":"","cipher":"aes","salt":"QKN1DBXe5PI","ct":"8hA83xDdXjD7K2qfmw5NdA"}',
|
'data' => '{"iv":"EN39/wd5Nk8HAiSG2K5AsQ","v":1,"iter":1000,"ks":128,"ts":64,"mode":"ccm","adata":"","cipher":"aes","salt":"QKN1DBXe5PI","ct":"8hA83xDdXjD7K2qfmw5NdA"}',
|
||||||
'attachment' => '{"iv":"Pd4pOKWkmDTT9uPwVwd5Ag","v":1,"iter":1000,"ks":128,"ts":64,"mode":"ccm","adata":"","cipher":"aes","salt":"ZIUhFTliVz4","ct":"6nOCU3peNDclDDpFtJEBKA"}',
|
'attachment' => '{"iv":"Pd4pOKWkmDTT9uPwVwd5Ag","v":1,"iter":1000,"ks":128,"ts":64,"mode":"ccm","adata":"","cipher":"aes","salt":"ZIUhFTliVz4","ct":"6nOCU3peNDclDDpFtJEBKA"}',
|
||||||
'attachmentname' => '{"iv":"76MkAtOGC4oFogX/aSMxRA","v":1,"iter":1000,"ks":128,"ts":64,"mode":"ccm","adata":"","cipher":"aes","salt":"ZIUhFTliVz4","ct":"b6Ae/U1xJdsX/+lATud4sQ"}',
|
'attachmentname' => '{"iv":"76MkAtOGC4oFogX/aSMxRA","v":1,"iter":1000,"ks":128,"ts":64,"mode":"ccm","adata":"","cipher":"aes","salt":"ZIUhFTliVz4","ct":"b6Ae/U1xJdsX/+lATud4sQ"}',
|
||||||
|
@ -46,6 +46,35 @@ class Helper
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* example paste version 2
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
private static $pasteV2 = array(
|
||||||
|
'adata' => array(
|
||||||
|
array(
|
||||||
|
'gMSNoLOk4z0RnmsYwXZ8mw==',
|
||||||
|
'TZO+JWuIuxs=',
|
||||||
|
100000,
|
||||||
|
256,
|
||||||
|
128,
|
||||||
|
'aes',
|
||||||
|
'gcm',
|
||||||
|
'zlib',
|
||||||
|
),
|
||||||
|
'plaintext',
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
),
|
||||||
|
'meta' => array(
|
||||||
|
'expire' => '5min',
|
||||||
|
'created' => 1344803344,
|
||||||
|
),
|
||||||
|
'v' => 2,
|
||||||
|
'ct' => 'ME5JF/YBEijp2uYMzLZozbKtWc5wfy6R59NBb7SmRig=',
|
||||||
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* example ID of a comment
|
* example ID of a comment
|
||||||
*
|
*
|
||||||
|
@ -58,7 +87,7 @@ class Helper
|
||||||
*
|
*
|
||||||
* @var array
|
* @var array
|
||||||
*/
|
*/
|
||||||
private static $comment = array(
|
private static $commentV1 = array(
|
||||||
'data' => '{"iv":"Pd4pOKWkmDTT9uPwVwd5Ag","v":1,"iter":1000,"ks":128,"ts":64,"mode":"ccm","adata":"","cipher":"aes","salt":"ZIUhFTliVz4","ct":"6nOCU3peNDclDDpFtJEBKA"}',
|
'data' => '{"iv":"Pd4pOKWkmDTT9uPwVwd5Ag","v":1,"iter":1000,"ks":128,"ts":64,"mode":"ccm","adata":"","cipher":"aes","salt":"ZIUhFTliVz4","ct":"6nOCU3peNDclDDpFtJEBKA"}',
|
||||||
'meta' => array(
|
'meta' => array(
|
||||||
'nickname' => '{"iv":"76MkAtOGC4oFogX/aSMxRA","v":1,"iter":1000,"ks":128,"ts":64,"mode":"ccm","adata":"","cipher":"aes","salt":"ZIUhFTliVz4","ct":"b6Ae/U1xJdsX/+lATud4sQ"}',
|
'nickname' => '{"iv":"76MkAtOGC4oFogX/aSMxRA","v":1,"iter":1000,"ks":128,"ts":64,"mode":"ccm","adata":"","cipher":"aes","salt":"ZIUhFTliVz4","ct":"b6Ae/U1xJdsX/+lATud4sQ"}',
|
||||||
|
@ -81,52 +110,65 @@ class Helper
|
||||||
*/
|
*/
|
||||||
public static function getPasteId()
|
public static function getPasteId()
|
||||||
{
|
{
|
||||||
return self::$pasteid;
|
return version_compare(PHP_VERSION, '5.6', '<') ? hash('fnv164', self::$pasteV2['ct']) : self::$pasteid;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* get example paste
|
* get example paste, as stored on server
|
||||||
*
|
*
|
||||||
|
* @param int $version
|
||||||
|
* @param array $meta
|
||||||
* @return array
|
* @return array
|
||||||
*/
|
*/
|
||||||
public static function getPaste($meta = array())
|
public static function getPaste($version = 2, array $meta = array())
|
||||||
{
|
{
|
||||||
$example = self::getPasteWithAttachment($meta);
|
$example = self::getPasteWithAttachment($version, $meta);
|
||||||
unset($example['attachment'], $example['attachmentname']);
|
// v1 has the attachment stored in a separate property
|
||||||
|
if ($version === 1) {
|
||||||
|
unset($example['attachment'], $example['attachmentname']);
|
||||||
|
}
|
||||||
return $example;
|
return $example;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* get example paste
|
* get example paste with attachment, as stored on server
|
||||||
*
|
*
|
||||||
|
* @param int $version
|
||||||
|
* @param array $meta
|
||||||
* @return array
|
* @return array
|
||||||
*/
|
*/
|
||||||
public static function getPasteWithAttachment($meta = array())
|
public static function getPasteWithAttachment($version = 2, array $meta = array())
|
||||||
{
|
{
|
||||||
$example = self::$paste;
|
$example = $version === 1 ? self::$pasteV1 : self::$pasteV2;
|
||||||
$example['meta']['salt'] = ServerSalt::generate();
|
$example['meta']['salt'] = ServerSalt::generate();
|
||||||
$example['meta'] = array_merge($example['meta'], $meta);
|
$example['meta'] = array_merge($example['meta'], $meta);
|
||||||
return $example;
|
return $example;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* get example paste
|
* get example paste, as decoded from POST by the request object
|
||||||
*
|
*
|
||||||
|
* @param int $version
|
||||||
|
* @param array $meta
|
||||||
* @return array
|
* @return array
|
||||||
*/
|
*/
|
||||||
public static function getPasteAsJson($meta = array())
|
public static function getPastePost($version = 2, array $meta = array())
|
||||||
{
|
{
|
||||||
$example = self::getPaste();
|
$example = self::getPaste($version, $meta);
|
||||||
// the JSON shouldn't contain the salt
|
$example['meta'] = array('expire' => $example['meta']['expire']);
|
||||||
unset($example['meta']['salt']);
|
return $example;
|
||||||
if (count($meta)) {
|
}
|
||||||
$example['meta'] = $meta;
|
|
||||||
}
|
/**
|
||||||
$example['comments'] = array();
|
* get example paste, as received via POST by the user
|
||||||
$example['comment_count'] = 0;
|
*
|
||||||
$example['comment_offset'] = 0;
|
* @param int $version
|
||||||
$example['@context'] = 'js/paste.jsonld';
|
* @param array $meta
|
||||||
return json_encode($example);
|
* @return array
|
||||||
|
*/
|
||||||
|
public static function getPasteJson($version = 2, array $meta = array())
|
||||||
|
{
|
||||||
|
return json_encode(self::getPastePost($version, $meta));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -140,30 +182,50 @@ class Helper
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* get example comment
|
* get example comment, as stored on server
|
||||||
*
|
*
|
||||||
|
* @param int $version
|
||||||
|
* @param array $meta
|
||||||
* @return array
|
* @return array
|
||||||
*/
|
*/
|
||||||
public static function getComment($meta = array())
|
public static function getComment($version = 2, array $meta = array())
|
||||||
{
|
{
|
||||||
$example = self::$comment;
|
$example = $version === 1 ? self::$commentV1 : self::$pasteV2;
|
||||||
|
if ($version === 2) {
|
||||||
|
$example['adata'] = $example['adata'][0];
|
||||||
|
$example['pasteid'] = $example['parentid'] = self::getPasteId();
|
||||||
|
$example['meta']['created'] = self::$commentV1['meta']['postdate'];
|
||||||
|
$example['meta']['icon'] = self::$commentV1['meta']['vizhash'];
|
||||||
|
unset($example['meta']['expire']);
|
||||||
|
}
|
||||||
$example['meta'] = array_merge($example['meta'], $meta);
|
$example['meta'] = array_merge($example['meta'], $meta);
|
||||||
return $example;
|
return $example;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* get example comment
|
* get example comment, as decoded from POST by the request object
|
||||||
*
|
*
|
||||||
|
* @param int $version
|
||||||
* @return array
|
* @return array
|
||||||
*/
|
*/
|
||||||
public static function getCommentPost($meta = array())
|
public static function getCommentPost()
|
||||||
{
|
{
|
||||||
$example = self::getComment($meta);
|
$example = self::getComment();
|
||||||
$example['nickname'] = $example['meta']['nickname'];
|
unset($example['meta']);
|
||||||
unset($example['meta']['nickname']);
|
|
||||||
return $example;
|
return $example;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get example comment, as received via POST by user
|
||||||
|
*
|
||||||
|
* @param int $version
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public static function getCommentJson()
|
||||||
|
{
|
||||||
|
return json_encode(self::getCommentPost());
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* delete directory and all its contents recursively
|
* delete directory and all its contents recursively
|
||||||
*
|
*
|
||||||
|
@ -229,7 +291,7 @@ class Helper
|
||||||
* @param string $pathToFile
|
* @param string $pathToFile
|
||||||
* @param array $values
|
* @param array $values
|
||||||
*/
|
*/
|
||||||
public static function createIniFile($pathToFile, $values)
|
public static function createIniFile($pathToFile, array $values)
|
||||||
{
|
{
|
||||||
if (count($values)) {
|
if (count($values)) {
|
||||||
@unlink($pathToFile);
|
@unlink($pathToFile);
|
||||||
|
|
|
@ -516,7 +516,7 @@ EOT;
|
||||||
$this->_model->create(Helper::getPasteId(), Helper::getPaste());
|
$this->_model->create(Helper::getPasteId(), Helper::getPaste());
|
||||||
$this->assertTrue($this->_model->exists(Helper::getPasteId()), 'paste exists before deleting data');
|
$this->assertTrue($this->_model->exists(Helper::getPasteId()), 'paste exists before deleting data');
|
||||||
$_GET['pasteid'] = Helper::getPasteId();
|
$_GET['pasteid'] = Helper::getPasteId();
|
||||||
$_GET['deletetoken'] = hash_hmac('sha256', Helper::getPasteId(), $this->_model->read(Helper::getPasteId())->meta->salt);
|
$_GET['deletetoken'] = hash_hmac('sha256', Helper::getPasteId(), $this->_model->read(Helper::getPasteId())['meta']['salt']);
|
||||||
EOT;
|
EOT;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,10 +4,11 @@ use PrivateBin\Controller;
|
||||||
use PrivateBin\Data\Filesystem;
|
use PrivateBin\Data\Filesystem;
|
||||||
use PrivateBin\Persistence\ServerSalt;
|
use PrivateBin\Persistence\ServerSalt;
|
||||||
use PrivateBin\Persistence\TrafficLimiter;
|
use PrivateBin\Persistence\TrafficLimiter;
|
||||||
|
use PrivateBin\Request;
|
||||||
|
|
||||||
class ControllerTest extends PHPUnit_Framework_TestCase
|
class ControllerTest extends PHPUnit_Framework_TestCase
|
||||||
{
|
{
|
||||||
protected $_model;
|
protected $_data;
|
||||||
|
|
||||||
protected $_path;
|
protected $_path;
|
||||||
|
|
||||||
|
@ -15,7 +16,7 @@ class ControllerTest extends PHPUnit_Framework_TestCase
|
||||||
{
|
{
|
||||||
/* Setup Routine */
|
/* Setup Routine */
|
||||||
$this->_path = sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'privatebin_data';
|
$this->_path = sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'privatebin_data';
|
||||||
$this->_model = Filesystem::getInstance(array('dir' => $this->_path));
|
$this->_data = Filesystem::getInstance(array('dir' => $this->_path));
|
||||||
$this->reset();
|
$this->reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -32,8 +33,8 @@ class ControllerTest extends PHPUnit_Framework_TestCase
|
||||||
$_POST = array();
|
$_POST = array();
|
||||||
$_GET = array();
|
$_GET = array();
|
||||||
$_SERVER = array();
|
$_SERVER = array();
|
||||||
if ($this->_model->exists(Helper::getPasteId())) {
|
if ($this->_data->exists(Helper::getPasteId())) {
|
||||||
$this->_model->delete(Helper::getPasteId());
|
$this->_data->delete(Helper::getPasteId());
|
||||||
}
|
}
|
||||||
$options = parse_ini_file(CONF_SAMPLE, true);
|
$options = parse_ini_file(CONF_SAMPLE, true);
|
||||||
$options['purge']['dir'] = $this->_path;
|
$options['purge']['dir'] = $this->_path;
|
||||||
|
@ -131,10 +132,13 @@ class ControllerTest extends PHPUnit_Framework_TestCase
|
||||||
*/
|
*/
|
||||||
public function testHtaccess()
|
public function testHtaccess()
|
||||||
{
|
{
|
||||||
$file = $this->_path . DIRECTORY_SEPARATOR . '.htaccess';
|
$htaccess = $this->_path . DIRECTORY_SEPARATOR . '.htaccess';
|
||||||
@unlink($file);
|
@unlink($htaccess);
|
||||||
|
|
||||||
$_POST = Helper::getPaste();
|
$paste = Helper::getPasteJson();
|
||||||
|
$file = tempnam(sys_get_temp_dir(), 'FOO');
|
||||||
|
file_put_contents($file, $paste);
|
||||||
|
Request::setInputStream($file);
|
||||||
$_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
|
$_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
|
||||||
$_SERVER['REQUEST_METHOD'] = 'POST';
|
$_SERVER['REQUEST_METHOD'] = 'POST';
|
||||||
$_SERVER['REMOTE_ADDR'] = '::1';
|
$_SERVER['REMOTE_ADDR'] = '::1';
|
||||||
|
@ -142,7 +146,7 @@ class ControllerTest extends PHPUnit_Framework_TestCase
|
||||||
new Controller;
|
new Controller;
|
||||||
ob_end_clean();
|
ob_end_clean();
|
||||||
|
|
||||||
$this->assertFileExists($file, 'htaccess recreated');
|
$this->assertFileExists($htaccess, 'htaccess recreated');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -163,7 +167,10 @@ class ControllerTest extends PHPUnit_Framework_TestCase
|
||||||
$options = parse_ini_file(CONF, true);
|
$options = parse_ini_file(CONF, true);
|
||||||
$options['traffic']['limit'] = 0;
|
$options['traffic']['limit'] = 0;
|
||||||
Helper::createIniFile(CONF, $options);
|
Helper::createIniFile(CONF, $options);
|
||||||
$_POST = Helper::getPaste();
|
$paste = Helper::getPasteJson();
|
||||||
|
$file = tempnam(sys_get_temp_dir(), 'FOO');
|
||||||
|
file_put_contents($file, $paste);
|
||||||
|
Request::setInputStream($file);
|
||||||
$_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
|
$_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
|
||||||
$_SERVER['REQUEST_METHOD'] = 'POST';
|
$_SERVER['REQUEST_METHOD'] = 'POST';
|
||||||
$_SERVER['REMOTE_ADDR'] = '::1';
|
$_SERVER['REMOTE_ADDR'] = '::1';
|
||||||
|
@ -173,10 +180,10 @@ class ControllerTest extends PHPUnit_Framework_TestCase
|
||||||
ob_end_clean();
|
ob_end_clean();
|
||||||
$response = json_decode($content, true);
|
$response = json_decode($content, true);
|
||||||
$this->assertEquals(0, $response['status'], 'outputs status');
|
$this->assertEquals(0, $response['status'], 'outputs status');
|
||||||
$this->assertTrue($this->_model->exists($response['id']), 'paste exists after posting data');
|
$this->assertTrue($this->_data->exists($response['id']), 'paste exists after posting data');
|
||||||
$paste = $this->_model->read($response['id']);
|
$paste = $this->_data->read($response['id']);
|
||||||
$this->assertEquals(
|
$this->assertEquals(
|
||||||
hash_hmac('sha256', $response['id'], $paste->meta->salt),
|
hash_hmac('sha256', $response['id'], $paste['meta']['salt']),
|
||||||
$response['deletetoken'],
|
$response['deletetoken'],
|
||||||
'outputs valid delete token'
|
'outputs valid delete token'
|
||||||
);
|
);
|
||||||
|
@ -190,7 +197,10 @@ class ControllerTest extends PHPUnit_Framework_TestCase
|
||||||
$options = parse_ini_file(CONF, true);
|
$options = parse_ini_file(CONF, true);
|
||||||
$options['traffic']['limit'] = 0;
|
$options['traffic']['limit'] = 0;
|
||||||
Helper::createIniFile(CONF, $options);
|
Helper::createIniFile(CONF, $options);
|
||||||
$_POST = Helper::getPaste(array('expire' => 25));
|
$paste = Helper::getPasteJson(2, array('expire' => 25));
|
||||||
|
$file = tempnam(sys_get_temp_dir(), 'FOO');
|
||||||
|
file_put_contents($file, $paste);
|
||||||
|
Request::setInputStream($file);
|
||||||
$_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
|
$_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
|
||||||
$_SERVER['REQUEST_METHOD'] = 'POST';
|
$_SERVER['REQUEST_METHOD'] = 'POST';
|
||||||
$_SERVER['REMOTE_ADDR'] = '::1';
|
$_SERVER['REMOTE_ADDR'] = '::1';
|
||||||
|
@ -201,10 +211,10 @@ class ControllerTest extends PHPUnit_Framework_TestCase
|
||||||
ob_end_clean();
|
ob_end_clean();
|
||||||
$response = json_decode($content, true);
|
$response = json_decode($content, true);
|
||||||
$this->assertEquals(0, $response['status'], 'outputs status');
|
$this->assertEquals(0, $response['status'], 'outputs status');
|
||||||
$this->assertTrue($this->_model->exists($response['id']), 'paste exists after posting data');
|
$this->assertTrue($this->_data->exists($response['id']), 'paste exists after posting data');
|
||||||
$paste = $this->_model->read($response['id']);
|
$paste = $this->_data->read($response['id']);
|
||||||
$this->assertEquals(
|
$this->assertEquals(
|
||||||
hash_hmac('sha256', $response['id'], $paste->meta->salt),
|
hash_hmac('sha256', $response['id'], $paste['meta']['salt']),
|
||||||
$response['deletetoken'],
|
$response['deletetoken'],
|
||||||
'outputs valid delete token'
|
'outputs valid delete token'
|
||||||
);
|
);
|
||||||
|
@ -219,7 +229,10 @@ class ControllerTest extends PHPUnit_Framework_TestCase
|
||||||
$options['main']['sizelimit'] = 10;
|
$options['main']['sizelimit'] = 10;
|
||||||
$options['traffic']['limit'] = 0;
|
$options['traffic']['limit'] = 0;
|
||||||
Helper::createIniFile(CONF, $options);
|
Helper::createIniFile(CONF, $options);
|
||||||
$_POST = Helper::getPaste();
|
$paste = Helper::getPasteJson();
|
||||||
|
$file = tempnam(sys_get_temp_dir(), 'FOO');
|
||||||
|
file_put_contents($file, $paste);
|
||||||
|
Request::setInputStream($file);
|
||||||
$_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
|
$_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
|
||||||
$_SERVER['REQUEST_METHOD'] = 'POST';
|
$_SERVER['REQUEST_METHOD'] = 'POST';
|
||||||
$_SERVER['REMOTE_ADDR'] = '::1';
|
$_SERVER['REMOTE_ADDR'] = '::1';
|
||||||
|
@ -229,7 +242,7 @@ class ControllerTest extends PHPUnit_Framework_TestCase
|
||||||
ob_end_clean();
|
ob_end_clean();
|
||||||
$response = json_decode($content, true);
|
$response = json_decode($content, true);
|
||||||
$this->assertEquals(1, $response['status'], 'outputs error status');
|
$this->assertEquals(1, $response['status'], 'outputs error status');
|
||||||
$this->assertFalse($this->_model->exists(Helper::getPasteId()), 'paste exists after posting data');
|
$this->assertFalse($this->_data->exists(Helper::getPasteId()), 'paste exists after posting data');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -240,7 +253,10 @@ class ControllerTest extends PHPUnit_Framework_TestCase
|
||||||
$options = parse_ini_file(CONF, true);
|
$options = parse_ini_file(CONF, true);
|
||||||
$options['traffic']['header'] = 'X_FORWARDED_FOR';
|
$options['traffic']['header'] = 'X_FORWARDED_FOR';
|
||||||
Helper::createIniFile(CONF, $options);
|
Helper::createIniFile(CONF, $options);
|
||||||
$_POST = Helper::getPaste();
|
$paste = Helper::getPasteJson();
|
||||||
|
$file = tempnam(sys_get_temp_dir(), 'FOO');
|
||||||
|
file_put_contents($file, $paste);
|
||||||
|
Request::setInputStream($file);
|
||||||
$_SERVER['HTTP_X_FORWARDED_FOR'] = '::2';
|
$_SERVER['HTTP_X_FORWARDED_FOR'] = '::2';
|
||||||
$_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
|
$_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
|
||||||
$_SERVER['REQUEST_METHOD'] = 'POST';
|
$_SERVER['REQUEST_METHOD'] = 'POST';
|
||||||
|
@ -251,10 +267,10 @@ class ControllerTest extends PHPUnit_Framework_TestCase
|
||||||
ob_end_clean();
|
ob_end_clean();
|
||||||
$response = json_decode($content, true);
|
$response = json_decode($content, true);
|
||||||
$this->assertEquals(0, $response['status'], 'outputs status');
|
$this->assertEquals(0, $response['status'], 'outputs status');
|
||||||
$this->assertTrue($this->_model->exists($response['id']), 'paste exists after posting data');
|
$this->assertTrue($this->_data->exists($response['id']), 'paste exists after posting data');
|
||||||
$paste = $this->_model->read($response['id']);
|
$paste = $this->_data->read($response['id']);
|
||||||
$this->assertEquals(
|
$this->assertEquals(
|
||||||
hash_hmac('sha256', $response['id'], $paste->meta->salt),
|
hash_hmac('sha256', $response['id'], $paste['meta']['salt']),
|
||||||
$response['deletetoken'],
|
$response['deletetoken'],
|
||||||
'outputs valid delete token'
|
'outputs valid delete token'
|
||||||
);
|
);
|
||||||
|
@ -268,8 +284,11 @@ class ControllerTest extends PHPUnit_Framework_TestCase
|
||||||
$options = parse_ini_file(CONF, true);
|
$options = parse_ini_file(CONF, true);
|
||||||
$options['traffic']['limit'] = 0;
|
$options['traffic']['limit'] = 0;
|
||||||
Helper::createIniFile(CONF, $options);
|
Helper::createIniFile(CONF, $options);
|
||||||
$this->_model->create(Helper::getPasteId(), Helper::getPaste());
|
$this->_data->create(Helper::getPasteId(), Helper::getPaste());
|
||||||
$_POST = Helper::getPaste();
|
$paste = Helper::getPasteJson();
|
||||||
|
$file = tempnam(sys_get_temp_dir(), 'FOO');
|
||||||
|
file_put_contents($file, $paste);
|
||||||
|
Request::setInputStream($file);
|
||||||
$_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
|
$_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
|
||||||
$_SERVER['REQUEST_METHOD'] = 'POST';
|
$_SERVER['REQUEST_METHOD'] = 'POST';
|
||||||
$_SERVER['REMOTE_ADDR'] = '::1';
|
$_SERVER['REMOTE_ADDR'] = '::1';
|
||||||
|
@ -279,7 +298,7 @@ class ControllerTest extends PHPUnit_Framework_TestCase
|
||||||
ob_end_clean();
|
ob_end_clean();
|
||||||
$response = json_decode($content, true);
|
$response = json_decode($content, true);
|
||||||
$this->assertEquals(1, $response['status'], 'outputs error status');
|
$this->assertEquals(1, $response['status'], 'outputs error status');
|
||||||
$this->assertTrue($this->_model->exists(Helper::getPasteId()), 'paste exists after posting data');
|
$this->assertTrue($this->_data->exists(Helper::getPasteId()), 'paste exists after posting data');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -290,9 +309,10 @@ class ControllerTest extends PHPUnit_Framework_TestCase
|
||||||
$options = parse_ini_file(CONF, true);
|
$options = parse_ini_file(CONF, true);
|
||||||
$options['traffic']['limit'] = 0;
|
$options['traffic']['limit'] = 0;
|
||||||
Helper::createIniFile(CONF, $options);
|
Helper::createIniFile(CONF, $options);
|
||||||
$_POST = Helper::getPaste();
|
$paste = Helper::getPasteJson();
|
||||||
$_POST['expire'] = '5min';
|
$file = tempnam(sys_get_temp_dir(), 'FOO');
|
||||||
$_POST['formatter'] = 'foo';
|
file_put_contents($file, $paste);
|
||||||
|
Request::setInputStream($file);
|
||||||
$_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
|
$_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
|
||||||
$_SERVER['REQUEST_METHOD'] = 'POST';
|
$_SERVER['REQUEST_METHOD'] = 'POST';
|
||||||
$_SERVER['REMOTE_ADDR'] = '::1';
|
$_SERVER['REMOTE_ADDR'] = '::1';
|
||||||
|
@ -303,14 +323,14 @@ class ControllerTest extends PHPUnit_Framework_TestCase
|
||||||
ob_end_clean();
|
ob_end_clean();
|
||||||
$response = json_decode($content, true);
|
$response = json_decode($content, true);
|
||||||
$this->assertEquals(0, $response['status'], 'outputs status');
|
$this->assertEquals(0, $response['status'], 'outputs status');
|
||||||
$this->assertTrue($this->_model->exists($response['id']), 'paste exists after posting data');
|
$this->assertTrue($this->_data->exists($response['id']), 'paste exists after posting data');
|
||||||
$paste = $this->_model->read($response['id']);
|
$paste = $this->_data->read($response['id']);
|
||||||
$this->assertEquals(
|
$this->assertEquals(
|
||||||
hash_hmac('sha256', $response['id'], $paste->meta->salt),
|
hash_hmac('sha256', $response['id'], $paste['meta']['salt']),
|
||||||
$response['deletetoken'],
|
$response['deletetoken'],
|
||||||
'outputs valid delete token'
|
'outputs valid delete token'
|
||||||
);
|
);
|
||||||
$this->assertGreaterThanOrEqual($time + 300, $paste->meta->expire_date, 'time is set correctly');
|
$this->assertGreaterThanOrEqual($time + 300, $paste['meta']['expire_date'], 'time is set correctly');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -321,9 +341,10 @@ class ControllerTest extends PHPUnit_Framework_TestCase
|
||||||
$options = parse_ini_file(CONF, true);
|
$options = parse_ini_file(CONF, true);
|
||||||
$options['traffic']['limit'] = 0;
|
$options['traffic']['limit'] = 0;
|
||||||
Helper::createIniFile(CONF, $options);
|
Helper::createIniFile(CONF, $options);
|
||||||
$_POST = Helper::getPaste();
|
$paste = Helper::getPasteJson();
|
||||||
$_POST['expire'] = '5min';
|
$file = tempnam(sys_get_temp_dir(), 'FOO');
|
||||||
$_POST['opendiscussion'] = '1';
|
file_put_contents($file, $paste);
|
||||||
|
Request::setInputStream($file);
|
||||||
$_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
|
$_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
|
||||||
$_SERVER['REQUEST_METHOD'] = 'POST';
|
$_SERVER['REQUEST_METHOD'] = 'POST';
|
||||||
$_SERVER['REMOTE_ADDR'] = '::1';
|
$_SERVER['REMOTE_ADDR'] = '::1';
|
||||||
|
@ -334,15 +355,15 @@ class ControllerTest extends PHPUnit_Framework_TestCase
|
||||||
ob_end_clean();
|
ob_end_clean();
|
||||||
$response = json_decode($content, true);
|
$response = json_decode($content, true);
|
||||||
$this->assertEquals(0, $response['status'], 'outputs status');
|
$this->assertEquals(0, $response['status'], 'outputs status');
|
||||||
$this->assertTrue($this->_model->exists($response['id']), 'paste exists after posting data');
|
$this->assertTrue($this->_data->exists($response['id']), 'paste exists after posting data');
|
||||||
$paste = $this->_model->read($response['id']);
|
$paste = $this->_data->read($response['id']);
|
||||||
$this->assertEquals(
|
$this->assertEquals(
|
||||||
hash_hmac('sha256', $response['id'], $paste->meta->salt),
|
hash_hmac('sha256', $response['id'], $paste['meta']['salt']),
|
||||||
$response['deletetoken'],
|
$response['deletetoken'],
|
||||||
'outputs valid delete token'
|
'outputs valid delete token'
|
||||||
);
|
);
|
||||||
$this->assertGreaterThanOrEqual($time + 300, $paste->meta->expire_date, 'time is set correctly');
|
$this->assertGreaterThanOrEqual($time + 300, $paste['meta']['expire_date'], 'time is set correctly');
|
||||||
$this->assertEquals(1, $paste->meta->opendiscussion, 'discussion is enabled');
|
$this->assertEquals(1, $paste['adata'][2], 'discussion is enabled');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -353,8 +374,10 @@ class ControllerTest extends PHPUnit_Framework_TestCase
|
||||||
$options = parse_ini_file(CONF, true);
|
$options = parse_ini_file(CONF, true);
|
||||||
$options['traffic']['limit'] = 0;
|
$options['traffic']['limit'] = 0;
|
||||||
Helper::createIniFile(CONF, $options);
|
Helper::createIniFile(CONF, $options);
|
||||||
$_POST = Helper::getPaste();
|
$paste = Helper::getPasteJson(2, array('expire' => 'foo'));
|
||||||
$_POST['expire'] = 'foo';
|
$file = tempnam(sys_get_temp_dir(), 'FOO');
|
||||||
|
file_put_contents($file, $paste);
|
||||||
|
Request::setInputStream($file);
|
||||||
$_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
|
$_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
|
||||||
$_SERVER['REQUEST_METHOD'] = 'POST';
|
$_SERVER['REQUEST_METHOD'] = 'POST';
|
||||||
$_SERVER['REMOTE_ADDR'] = '::1';
|
$_SERVER['REMOTE_ADDR'] = '::1';
|
||||||
|
@ -364,10 +387,10 @@ class ControllerTest extends PHPUnit_Framework_TestCase
|
||||||
ob_end_clean();
|
ob_end_clean();
|
||||||
$response = json_decode($content, true);
|
$response = json_decode($content, true);
|
||||||
$this->assertEquals(0, $response['status'], 'outputs status');
|
$this->assertEquals(0, $response['status'], 'outputs status');
|
||||||
$this->assertTrue($this->_model->exists($response['id']), 'paste exists after posting data');
|
$this->assertTrue($this->_data->exists($response['id']), 'paste exists after posting data');
|
||||||
$paste = $this->_model->read($response['id']);
|
$paste = $this->_data->read($response['id']);
|
||||||
$this->assertEquals(
|
$this->assertEquals(
|
||||||
hash_hmac('sha256', $response['id'], $paste->meta->salt),
|
hash_hmac('sha256', $response['id'], $paste['meta']['salt']),
|
||||||
$response['deletetoken'],
|
$response['deletetoken'],
|
||||||
'outputs valid delete token'
|
'outputs valid delete token'
|
||||||
);
|
);
|
||||||
|
@ -381,8 +404,11 @@ class ControllerTest extends PHPUnit_Framework_TestCase
|
||||||
$options = parse_ini_file(CONF, true);
|
$options = parse_ini_file(CONF, true);
|
||||||
$options['traffic']['limit'] = 0;
|
$options['traffic']['limit'] = 0;
|
||||||
Helper::createIniFile(CONF, $options);
|
Helper::createIniFile(CONF, $options);
|
||||||
$_POST = Helper::getPaste();
|
$paste = Helper::getPastePost();
|
||||||
$_POST['burnafterreading'] = 'neither 1 nor 0';
|
$paste['adata'][3] = 'neither 1 nor 0';
|
||||||
|
$file = tempnam(sys_get_temp_dir(), 'FOO');
|
||||||
|
file_put_contents($file, json_encode($paste));
|
||||||
|
Request::setInputStream($file);
|
||||||
$_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
|
$_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
|
||||||
$_SERVER['REQUEST_METHOD'] = 'POST';
|
$_SERVER['REQUEST_METHOD'] = 'POST';
|
||||||
$_SERVER['REMOTE_ADDR'] = '::1';
|
$_SERVER['REMOTE_ADDR'] = '::1';
|
||||||
|
@ -392,7 +418,7 @@ class ControllerTest extends PHPUnit_Framework_TestCase
|
||||||
ob_end_clean();
|
ob_end_clean();
|
||||||
$response = json_decode($content, true);
|
$response = json_decode($content, true);
|
||||||
$this->assertEquals(1, $response['status'], 'outputs error status');
|
$this->assertEquals(1, $response['status'], 'outputs error status');
|
||||||
$this->assertFalse($this->_model->exists(Helper::getPasteId()), 'paste exists after posting data');
|
$this->assertFalse($this->_data->exists(Helper::getPasteId()), 'paste exists after posting data');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -403,8 +429,11 @@ class ControllerTest extends PHPUnit_Framework_TestCase
|
||||||
$options = parse_ini_file(CONF, true);
|
$options = parse_ini_file(CONF, true);
|
||||||
$options['traffic']['limit'] = 0;
|
$options['traffic']['limit'] = 0;
|
||||||
Helper::createIniFile(CONF, $options);
|
Helper::createIniFile(CONF, $options);
|
||||||
$_POST = Helper::getPaste();
|
$paste = Helper::getPastePost();
|
||||||
$_POST['opendiscussion'] = 'neither 1 nor 0';
|
$paste['adata'][2] = 'neither 1 nor 0';
|
||||||
|
$file = tempnam(sys_get_temp_dir(), 'FOO');
|
||||||
|
file_put_contents($file, json_encode($paste));
|
||||||
|
Request::setInputStream($file);
|
||||||
$_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
|
$_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
|
||||||
$_SERVER['REQUEST_METHOD'] = 'POST';
|
$_SERVER['REQUEST_METHOD'] = 'POST';
|
||||||
$_SERVER['REMOTE_ADDR'] = '::1';
|
$_SERVER['REMOTE_ADDR'] = '::1';
|
||||||
|
@ -414,40 +443,7 @@ class ControllerTest extends PHPUnit_Framework_TestCase
|
||||||
ob_end_clean();
|
ob_end_clean();
|
||||||
$response = json_decode($content, true);
|
$response = json_decode($content, true);
|
||||||
$this->assertEquals(1, $response['status'], 'outputs error status');
|
$this->assertEquals(1, $response['status'], 'outputs error status');
|
||||||
$this->assertFalse($this->_model->exists(Helper::getPasteId()), 'paste exists after posting data');
|
$this->assertFalse($this->_data->exists(Helper::getPasteId()), 'paste exists after posting data');
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @runInSeparateProcess
|
|
||||||
*/
|
|
||||||
public function testCreateAttachment()
|
|
||||||
{
|
|
||||||
$options = parse_ini_file(CONF, true);
|
|
||||||
$options['traffic']['limit'] = 0;
|
|
||||||
$options['main']['fileupload'] = true;
|
|
||||||
Helper::createIniFile(CONF, $options);
|
|
||||||
$_POST = Helper::getPasteWithAttachment();
|
|
||||||
$_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
|
|
||||||
$_SERVER['REQUEST_METHOD'] = 'POST';
|
|
||||||
$_SERVER['REMOTE_ADDR'] = '::1';
|
|
||||||
$this->assertFalse($this->_model->exists(Helper::getPasteId()), 'paste does not exists before posting data');
|
|
||||||
ob_start();
|
|
||||||
new Controller;
|
|
||||||
$content = ob_get_contents();
|
|
||||||
ob_end_clean();
|
|
||||||
$response = json_decode($content, true);
|
|
||||||
$this->assertEquals(0, $response['status'], 'outputs status');
|
|
||||||
$this->assertTrue($this->_model->exists($response['id']), 'paste exists after posting data');
|
|
||||||
$original = json_decode(json_encode($_POST));
|
|
||||||
$stored = $this->_model->read($response['id']);
|
|
||||||
foreach (array('data', 'attachment', 'attachmentname') as $key) {
|
|
||||||
$this->assertEquals($original->$key, $stored->$key);
|
|
||||||
}
|
|
||||||
$this->assertEquals(
|
|
||||||
hash_hmac('sha256', $response['id'], $stored->meta->salt),
|
|
||||||
$response['deletetoken'],
|
|
||||||
'outputs valid delete token'
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -455,26 +451,21 @@ class ControllerTest extends PHPUnit_Framework_TestCase
|
||||||
* silently removed, check that this case is handled
|
* silently removed, check that this case is handled
|
||||||
*
|
*
|
||||||
* @runInSeparateProcess
|
* @runInSeparateProcess
|
||||||
|
* @expectedException Exception
|
||||||
|
* @expectedExceptionCode 90
|
||||||
*/
|
*/
|
||||||
public function testCreateBrokenAttachmentUpload()
|
public function testCreateBrokenUpload()
|
||||||
{
|
{
|
||||||
$options = parse_ini_file(CONF, true);
|
$paste = substr(Helper::getPasteJson(), 0, -10);
|
||||||
$options['traffic']['limit'] = 0;
|
$file = tempnam(sys_get_temp_dir(), 'FOO');
|
||||||
$options['main']['fileupload'] = true;
|
file_put_contents($file, $paste);
|
||||||
Helper::createIniFile(CONF, $options);
|
Request::setInputStream($file);
|
||||||
$_POST = Helper::getPasteWithAttachment();
|
|
||||||
unset($_POST['attachment']);
|
|
||||||
$_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
|
$_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
|
||||||
$_SERVER['REQUEST_METHOD'] = 'POST';
|
$_SERVER['REQUEST_METHOD'] = 'POST';
|
||||||
$_SERVER['REMOTE_ADDR'] = '::1';
|
$_SERVER['REMOTE_ADDR'] = '::1';
|
||||||
$this->assertFalse($this->_model->exists(Helper::getPasteId()), 'paste does not exists before posting data');
|
$this->assertFalse($this->_data->exists(Helper::getPasteId()), 'paste does not exists before posting data');
|
||||||
ob_start();
|
|
||||||
new Controller;
|
new Controller;
|
||||||
$content = ob_get_contents();
|
$this->assertFalse($this->_data->exists(Helper::getPasteId()), 'paste exists after posting data');
|
||||||
ob_end_clean();
|
|
||||||
$response = json_decode($content, true);
|
|
||||||
$this->assertEquals(1, $response['status'], 'outputs error status');
|
|
||||||
$this->assertFalse($this->_model->exists(Helper::getPasteId()), 'paste exists after posting data');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -482,74 +473,24 @@ class ControllerTest extends PHPUnit_Framework_TestCase
|
||||||
*/
|
*/
|
||||||
public function testCreateTooSoon()
|
public function testCreateTooSoon()
|
||||||
{
|
{
|
||||||
$_POST = Helper::getPaste();
|
$paste = Helper::getPasteJson();
|
||||||
|
$file = tempnam(sys_get_temp_dir(), 'FOO');
|
||||||
|
file_put_contents($file, $paste);
|
||||||
|
Request::setInputStream($file);
|
||||||
$_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
|
$_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
|
||||||
$_SERVER['REQUEST_METHOD'] = 'POST';
|
$_SERVER['REQUEST_METHOD'] = 'POST';
|
||||||
$_SERVER['REMOTE_ADDR'] = '::1';
|
$_SERVER['REMOTE_ADDR'] = '::1';
|
||||||
ob_start();
|
ob_start();
|
||||||
new Controller;
|
new Controller;
|
||||||
ob_end_clean();
|
ob_end_clean();
|
||||||
$this->_model->delete(Helper::getPasteId());
|
$this->_data->delete(Helper::getPasteId());
|
||||||
ob_start();
|
ob_start();
|
||||||
new Controller;
|
new Controller;
|
||||||
$content = ob_get_contents();
|
$content = ob_get_contents();
|
||||||
ob_end_clean();
|
ob_end_clean();
|
||||||
$response = json_decode($content, true);
|
$response = json_decode($content, true);
|
||||||
$this->assertEquals(1, $response['status'], 'outputs error status');
|
$this->assertEquals(1, $response['status'], 'outputs error status');
|
||||||
$this->assertFalse($this->_model->exists(Helper::getPasteId()), 'paste exists after posting data');
|
$this->assertFalse($this->_data->exists(Helper::getPasteId()), 'paste exists after posting data');
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @runInSeparateProcess
|
|
||||||
*/
|
|
||||||
public function testCreateValidNick()
|
|
||||||
{
|
|
||||||
$options = parse_ini_file(CONF, true);
|
|
||||||
$options['traffic']['limit'] = 0;
|
|
||||||
Helper::createIniFile(CONF, $options);
|
|
||||||
$_POST = Helper::getPaste();
|
|
||||||
$_POST['nickname'] = Helper::getComment()['meta']['nickname'];
|
|
||||||
$_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
|
|
||||||
$_SERVER['REQUEST_METHOD'] = 'POST';
|
|
||||||
$_SERVER['REMOTE_ADDR'] = '::1';
|
|
||||||
ob_start();
|
|
||||||
new Controller;
|
|
||||||
$content = ob_get_contents();
|
|
||||||
ob_end_clean();
|
|
||||||
$response = json_decode($content, true);
|
|
||||||
$this->assertEquals(0, $response['status'], 'outputs status');
|
|
||||||
$this->assertTrue($this->_model->exists($response['id']), 'paste exists after posting data');
|
|
||||||
$paste = $this->_model->read($response['id']);
|
|
||||||
$this->assertEquals(
|
|
||||||
hash_hmac('sha256', $response['id'], $paste->meta->salt),
|
|
||||||
$response['deletetoken'],
|
|
||||||
'outputs valid delete token'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @runInSeparateProcess
|
|
||||||
*/
|
|
||||||
public function testCreateInvalidNick()
|
|
||||||
{
|
|
||||||
$options = parse_ini_file(CONF, true);
|
|
||||||
$options['traffic']['limit'] = 0;
|
|
||||||
Helper::createIniFile(CONF, $options);
|
|
||||||
$_POST = Helper::getCommentPost();
|
|
||||||
$_POST['pasteid'] = Helper::getPasteId();
|
|
||||||
$_POST['parentid'] = Helper::getPasteId();
|
|
||||||
$_POST['nickname'] = 'foo';
|
|
||||||
$_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
|
|
||||||
$_SERVER['REQUEST_METHOD'] = 'POST';
|
|
||||||
$_SERVER['REMOTE_ADDR'] = '::1';
|
|
||||||
$this->_model->create(Helper::getPasteId(), Helper::getPaste());
|
|
||||||
ob_start();
|
|
||||||
new Controller;
|
|
||||||
$content = ob_get_contents();
|
|
||||||
ob_end_clean();
|
|
||||||
$response = json_decode($content, true);
|
|
||||||
$this->assertEquals(1, $response['status'], 'outputs error status');
|
|
||||||
$this->assertTrue($this->_model->exists(Helper::getPasteId()), 'paste exists after posting data');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -560,20 +501,21 @@ class ControllerTest extends PHPUnit_Framework_TestCase
|
||||||
$options = parse_ini_file(CONF, true);
|
$options = parse_ini_file(CONF, true);
|
||||||
$options['traffic']['limit'] = 0;
|
$options['traffic']['limit'] = 0;
|
||||||
Helper::createIniFile(CONF, $options);
|
Helper::createIniFile(CONF, $options);
|
||||||
$_POST = Helper::getCommentPost();
|
$comment = Helper::getCommentJson();
|
||||||
$_POST['pasteid'] = Helper::getPasteId();
|
$file = tempnam(sys_get_temp_dir(), 'FOO');
|
||||||
$_POST['parentid'] = Helper::getPasteId();
|
file_put_contents($file, $comment);
|
||||||
|
Request::setInputStream($file);
|
||||||
$_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
|
$_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
|
||||||
$_SERVER['REQUEST_METHOD'] = 'POST';
|
$_SERVER['REQUEST_METHOD'] = 'POST';
|
||||||
$_SERVER['REMOTE_ADDR'] = '::1';
|
$_SERVER['REMOTE_ADDR'] = '::1';
|
||||||
$this->_model->create(Helper::getPasteId(), Helper::getPaste());
|
$this->_data->create(Helper::getPasteId(), Helper::getPaste());
|
||||||
ob_start();
|
ob_start();
|
||||||
new Controller;
|
new Controller;
|
||||||
$content = ob_get_contents();
|
$content = ob_get_contents();
|
||||||
ob_end_clean();
|
ob_end_clean();
|
||||||
$response = json_decode($content, true);
|
$response = json_decode($content, true);
|
||||||
$this->assertEquals(0, $response['status'], 'outputs status');
|
$this->assertEquals(0, $response['status'], 'outputs status');
|
||||||
$this->assertTrue($this->_model->existsComment(Helper::getPasteId(), Helper::getPasteId(), $response['id']), 'paste exists after posting data');
|
$this->assertTrue($this->_data->existsComment(Helper::getPasteId(), Helper::getPasteId(), $response['id']), 'paste exists after posting data');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -584,20 +526,22 @@ class ControllerTest extends PHPUnit_Framework_TestCase
|
||||||
$options = parse_ini_file(CONF, true);
|
$options = parse_ini_file(CONF, true);
|
||||||
$options['traffic']['limit'] = 0;
|
$options['traffic']['limit'] = 0;
|
||||||
Helper::createIniFile(CONF, $options);
|
Helper::createIniFile(CONF, $options);
|
||||||
$_POST = Helper::getCommentPost();
|
$comment = Helper::getCommentPost();
|
||||||
$_POST['pasteid'] = Helper::getPasteId();
|
$comment['parentid'] = 'foo';
|
||||||
$_POST['parentid'] = 'foo';
|
$file = tempnam(sys_get_temp_dir(), 'FOO');
|
||||||
|
file_put_contents($file, json_encode($comment));
|
||||||
|
Request::setInputStream($file);
|
||||||
$_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
|
$_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
|
||||||
$_SERVER['REQUEST_METHOD'] = 'POST';
|
$_SERVER['REQUEST_METHOD'] = 'POST';
|
||||||
$_SERVER['REMOTE_ADDR'] = '::1';
|
$_SERVER['REMOTE_ADDR'] = '::1';
|
||||||
$this->_model->create(Helper::getPasteId(), Helper::getPaste());
|
$this->_data->create(Helper::getPasteId(), Helper::getPaste());
|
||||||
ob_start();
|
ob_start();
|
||||||
new Controller;
|
new Controller;
|
||||||
$content = ob_get_contents();
|
$content = ob_get_contents();
|
||||||
ob_end_clean();
|
ob_end_clean();
|
||||||
$response = json_decode($content, true);
|
$response = json_decode($content, true);
|
||||||
$this->assertEquals(1, $response['status'], 'outputs error status');
|
$this->assertEquals(1, $response['status'], 'outputs error status');
|
||||||
$this->assertFalse($this->_model->existsComment(Helper::getPasteId(), Helper::getPasteId(), Helper::getCommentId()), 'paste exists after posting data');
|
$this->assertFalse($this->_data->existsComment(Helper::getPasteId(), Helper::getPasteId(), Helper::getCommentId()), 'paste exists after posting data');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -608,21 +552,23 @@ class ControllerTest extends PHPUnit_Framework_TestCase
|
||||||
$options = parse_ini_file(CONF, true);
|
$options = parse_ini_file(CONF, true);
|
||||||
$options['traffic']['limit'] = 0;
|
$options['traffic']['limit'] = 0;
|
||||||
Helper::createIniFile(CONF, $options);
|
Helper::createIniFile(CONF, $options);
|
||||||
$_POST = Helper::getCommentPost();
|
$comment = Helper::getCommentJson();
|
||||||
$_POST['pasteid'] = Helper::getPasteId();
|
$file = tempnam(sys_get_temp_dir(), 'FOO');
|
||||||
$_POST['parentid'] = Helper::getPasteId();
|
file_put_contents($file, $comment);
|
||||||
|
Request::setInputStream($file);
|
||||||
$_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
|
$_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
|
||||||
$_SERVER['REQUEST_METHOD'] = 'POST';
|
$_SERVER['REQUEST_METHOD'] = 'POST';
|
||||||
$_SERVER['REMOTE_ADDR'] = '::1';
|
$_SERVER['REMOTE_ADDR'] = '::1';
|
||||||
$paste = Helper::getPaste(array('opendiscussion' => false));
|
$paste = Helper::getPaste();
|
||||||
$this->_model->create(Helper::getPasteId(), $paste);
|
$paste['adata'][2] = 0;
|
||||||
|
$this->_data->create(Helper::getPasteId(), $paste);
|
||||||
ob_start();
|
ob_start();
|
||||||
new Controller;
|
new Controller;
|
||||||
$content = ob_get_contents();
|
$content = ob_get_contents();
|
||||||
ob_end_clean();
|
ob_end_clean();
|
||||||
$response = json_decode($content, true);
|
$response = json_decode($content, true);
|
||||||
$this->assertEquals(1, $response['status'], 'outputs error status');
|
$this->assertEquals(1, $response['status'], 'outputs error status');
|
||||||
$this->assertFalse($this->_model->existsComment(Helper::getPasteId(), Helper::getPasteId(), Helper::getCommentId()), 'paste exists after posting data');
|
$this->assertFalse($this->_data->existsComment(Helper::getPasteId(), Helper::getPasteId(), Helper::getCommentId()), 'paste exists after posting data');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -633,9 +579,10 @@ class ControllerTest extends PHPUnit_Framework_TestCase
|
||||||
$options = parse_ini_file(CONF, true);
|
$options = parse_ini_file(CONF, true);
|
||||||
$options['traffic']['limit'] = 0;
|
$options['traffic']['limit'] = 0;
|
||||||
Helper::createIniFile(CONF, $options);
|
Helper::createIniFile(CONF, $options);
|
||||||
$_POST = Helper::getCommentPost();
|
$comment = Helper::getCommentJson();
|
||||||
$_POST['pasteid'] = Helper::getPasteId();
|
$file = tempnam(sys_get_temp_dir(), 'FOO');
|
||||||
$_POST['parentid'] = Helper::getPasteId();
|
file_put_contents($file, $comment);
|
||||||
|
Request::setInputStream($file);
|
||||||
$_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
|
$_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
|
||||||
$_SERVER['REQUEST_METHOD'] = 'POST';
|
$_SERVER['REQUEST_METHOD'] = 'POST';
|
||||||
$_SERVER['REMOTE_ADDR'] = '::1';
|
$_SERVER['REMOTE_ADDR'] = '::1';
|
||||||
|
@ -645,7 +592,7 @@ class ControllerTest extends PHPUnit_Framework_TestCase
|
||||||
ob_end_clean();
|
ob_end_clean();
|
||||||
$response = json_decode($content, true);
|
$response = json_decode($content, true);
|
||||||
$this->assertEquals(1, $response['status'], 'outputs error status');
|
$this->assertEquals(1, $response['status'], 'outputs error status');
|
||||||
$this->assertFalse($this->_model->existsComment(Helper::getPasteId(), Helper::getPasteId(), Helper::getCommentId()), 'paste exists after posting data');
|
$this->assertFalse($this->_data->existsComment(Helper::getPasteId(), Helper::getPasteId(), Helper::getCommentId()), 'paste exists after posting data');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -656,12 +603,13 @@ class ControllerTest extends PHPUnit_Framework_TestCase
|
||||||
$options = parse_ini_file(CONF, true);
|
$options = parse_ini_file(CONF, true);
|
||||||
$options['traffic']['limit'] = 0;
|
$options['traffic']['limit'] = 0;
|
||||||
Helper::createIniFile(CONF, $options);
|
Helper::createIniFile(CONF, $options);
|
||||||
$this->_model->create(Helper::getPasteId(), Helper::getPaste());
|
$this->_data->create(Helper::getPasteId(), Helper::getPaste());
|
||||||
$this->_model->createComment(Helper::getPasteId(), Helper::getPasteId(), Helper::getCommentId(), Helper::getComment());
|
$this->_data->createComment(Helper::getPasteId(), Helper::getPasteId(), Helper::getPasteId(), Helper::getComment());
|
||||||
$this->assertTrue($this->_model->existsComment(Helper::getPasteId(), Helper::getPasteId(), Helper::getCommentId()), 'comment exists before posting data');
|
$this->assertTrue($this->_data->existsComment(Helper::getPasteId(), Helper::getPasteId(), Helper::getPasteId()), 'comment exists before posting data');
|
||||||
$_POST = Helper::getCommentPost();
|
$comment = Helper::getCommentJson();
|
||||||
$_POST['pasteid'] = Helper::getPasteId();
|
$file = tempnam(sys_get_temp_dir(), 'FOO');
|
||||||
$_POST['parentid'] = Helper::getPasteId();
|
file_put_contents($file, $comment);
|
||||||
|
Request::setInputStream($file);
|
||||||
$_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
|
$_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
|
||||||
$_SERVER['REQUEST_METHOD'] = 'POST';
|
$_SERVER['REQUEST_METHOD'] = 'POST';
|
||||||
$_SERVER['REMOTE_ADDR'] = '::1';
|
$_SERVER['REMOTE_ADDR'] = '::1';
|
||||||
|
@ -671,7 +619,7 @@ class ControllerTest extends PHPUnit_Framework_TestCase
|
||||||
ob_end_clean();
|
ob_end_clean();
|
||||||
$response = json_decode($content, true);
|
$response = json_decode($content, true);
|
||||||
$this->assertEquals(1, $response['status'], 'outputs error status');
|
$this->assertEquals(1, $response['status'], 'outputs error status');
|
||||||
$this->assertTrue($this->_model->existsComment(Helper::getPasteId(), Helper::getPasteId(), Helper::getCommentId()), 'paste exists after posting data');
|
$this->assertTrue($this->_data->existsComment(Helper::getPasteId(), Helper::getPasteId(), Helper::getPasteId()), 'paste exists after posting data');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -713,8 +661,8 @@ class ControllerTest extends PHPUnit_Framework_TestCase
|
||||||
*/
|
*/
|
||||||
public function testReadExpired()
|
public function testReadExpired()
|
||||||
{
|
{
|
||||||
$expiredPaste = Helper::getPaste(array('expire_date' => 1344803344));
|
$expiredPaste = Helper::getPaste(2, array('expire_date' => 1344803344));
|
||||||
$this->_model->create(Helper::getPasteId(), $expiredPaste);
|
$this->_data->create(Helper::getPasteId(), $expiredPaste);
|
||||||
$_SERVER['QUERY_STRING'] = Helper::getPasteId();
|
$_SERVER['QUERY_STRING'] = Helper::getPasteId();
|
||||||
$_GET[Helper::getPasteId()] = '';
|
$_GET[Helper::getPasteId()] = '';
|
||||||
$_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
|
$_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
|
||||||
|
@ -732,8 +680,9 @@ class ControllerTest extends PHPUnit_Framework_TestCase
|
||||||
*/
|
*/
|
||||||
public function testReadBurn()
|
public function testReadBurn()
|
||||||
{
|
{
|
||||||
$paste = Helper::getPaste(array('burnafterreading' => true));
|
$paste = Helper::getPaste();
|
||||||
$this->_model->create(Helper::getPasteId(), $paste);
|
$paste['adata'][3] = 1;
|
||||||
|
$this->_data->create(Helper::getPasteId(), $paste);
|
||||||
$_SERVER['QUERY_STRING'] = Helper::getPasteId();
|
$_SERVER['QUERY_STRING'] = Helper::getPasteId();
|
||||||
$_GET[Helper::getPasteId()] = '';
|
$_GET[Helper::getPasteId()] = '';
|
||||||
$_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
|
$_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
|
||||||
|
@ -745,15 +694,15 @@ class ControllerTest extends PHPUnit_Framework_TestCase
|
||||||
$this->assertEquals(0, $response['status'], 'outputs success status');
|
$this->assertEquals(0, $response['status'], 'outputs success status');
|
||||||
$this->assertEquals(Helper::getPasteId(), $response['id'], 'outputs data correctly');
|
$this->assertEquals(Helper::getPasteId(), $response['id'], 'outputs data correctly');
|
||||||
$this->assertStringEndsWith('?' . $response['id'], $response['url'], 'returned URL points to new paste');
|
$this->assertStringEndsWith('?' . $response['id'], $response['url'], 'returned URL points to new paste');
|
||||||
$this->assertEquals($paste['data'], $response['data'], 'outputs data correctly');
|
$this->assertEquals($paste['ct'], $response['ct'], 'outputs ct correctly');
|
||||||
$this->assertEquals($paste['meta']['formatter'], $response['meta']['formatter'], 'outputs format correctly');
|
$this->assertEquals($paste['adata'][1], $response['adata'][1], 'outputs formatter correctly');
|
||||||
$this->assertEquals($paste['meta']['postdate'], $response['meta']['postdate'], 'outputs postdate correctly');
|
$this->assertEquals($paste['adata'][2], $response['adata'][2], 'outputs opendiscussion correctly');
|
||||||
$this->assertEquals($paste['meta']['opendiscussion'], $response['meta']['opendiscussion'], 'outputs opendiscussion correctly');
|
$this->assertEquals($paste['adata'][3], $response['adata'][3], 'outputs burnafterreading correctly');
|
||||||
$this->assertEquals(1, $response['meta']['burnafterreading'], 'outputs burnafterreading correctly');
|
$this->assertEquals($paste['meta']['created'], $response['meta']['created'], 'outputs created correctly');
|
||||||
$this->assertEquals(0, $response['comment_count'], 'outputs comment_count correctly');
|
$this->assertEquals(0, $response['comment_count'], 'outputs comment_count correctly');
|
||||||
$this->assertEquals(0, $response['comment_offset'], 'outputs comment_offset correctly');
|
$this->assertEquals(0, $response['comment_offset'], 'outputs comment_offset correctly');
|
||||||
// by default it will be deleted instantly after it is read
|
// by default it will be deleted instantly after it is read
|
||||||
$this->assertFalse($this->_model->exists(Helper::getPasteId()), 'paste exists after reading');
|
$this->assertFalse($this->_data->exists(Helper::getPasteId()), 'paste exists after reading');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -762,7 +711,7 @@ class ControllerTest extends PHPUnit_Framework_TestCase
|
||||||
public function testReadJson()
|
public function testReadJson()
|
||||||
{
|
{
|
||||||
$paste = Helper::getPaste();
|
$paste = Helper::getPaste();
|
||||||
$this->_model->create(Helper::getPasteId(), $paste);
|
$this->_data->create(Helper::getPasteId(), $paste);
|
||||||
$_SERVER['QUERY_STRING'] = Helper::getPasteId();
|
$_SERVER['QUERY_STRING'] = Helper::getPasteId();
|
||||||
$_GET[Helper::getPasteId()] = '';
|
$_GET[Helper::getPasteId()] = '';
|
||||||
$_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
|
$_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
|
||||||
|
@ -774,10 +723,11 @@ class ControllerTest extends PHPUnit_Framework_TestCase
|
||||||
$this->assertEquals(0, $response['status'], 'outputs success status');
|
$this->assertEquals(0, $response['status'], 'outputs success status');
|
||||||
$this->assertEquals(Helper::getPasteId(), $response['id'], 'outputs data correctly');
|
$this->assertEquals(Helper::getPasteId(), $response['id'], 'outputs data correctly');
|
||||||
$this->assertStringEndsWith('?' . $response['id'], $response['url'], 'returned URL points to new paste');
|
$this->assertStringEndsWith('?' . $response['id'], $response['url'], 'returned URL points to new paste');
|
||||||
$this->assertEquals($paste['data'], $response['data'], 'outputs data correctly');
|
$this->assertEquals($paste['ct'], $response['ct'], 'outputs ct correctly');
|
||||||
$this->assertEquals($paste['meta']['formatter'], $response['meta']['formatter'], 'outputs format correctly');
|
$this->assertEquals($paste['adata'][1], $response['adata'][1], 'outputs formatter correctly');
|
||||||
$this->assertEquals($paste['meta']['postdate'], $response['meta']['postdate'], 'outputs postdate correctly');
|
$this->assertEquals($paste['adata'][2], $response['adata'][2], 'outputs opendiscussion correctly');
|
||||||
$this->assertEquals($paste['meta']['opendiscussion'], $response['meta']['opendiscussion'], 'outputs opendiscussion correctly');
|
$this->assertEquals($paste['adata'][3], $response['adata'][3], 'outputs burnafterreading correctly');
|
||||||
|
$this->assertEquals($paste['meta']['created'], $response['meta']['created'], 'outputs created correctly');
|
||||||
$this->assertEquals(0, $response['comment_count'], 'outputs comment_count correctly');
|
$this->assertEquals(0, $response['comment_count'], 'outputs comment_count correctly');
|
||||||
$this->assertEquals(0, $response['comment_offset'], 'outputs comment_offset correctly');
|
$this->assertEquals(0, $response['comment_offset'], 'outputs comment_offset correctly');
|
||||||
}
|
}
|
||||||
|
@ -787,13 +737,13 @@ class ControllerTest extends PHPUnit_Framework_TestCase
|
||||||
*/
|
*/
|
||||||
public function testReadOldSyntax()
|
public function testReadOldSyntax()
|
||||||
{
|
{
|
||||||
$paste = Helper::getPaste();
|
$paste = Helper::getPaste(1);
|
||||||
$paste['meta'] = array(
|
$paste['meta'] = array(
|
||||||
'syntaxcoloring' => true,
|
'syntaxcoloring' => true,
|
||||||
'postdate' => $paste['meta']['postdate'],
|
'postdate' => $paste['meta']['postdate'],
|
||||||
'opendiscussion' => $paste['meta']['opendiscussion'],
|
'opendiscussion' => $paste['meta']['opendiscussion'],
|
||||||
);
|
);
|
||||||
$this->_model->create(Helper::getPasteId(), $paste);
|
$this->_data->create(Helper::getPasteId(), $paste);
|
||||||
$_SERVER['QUERY_STRING'] = Helper::getPasteId();
|
$_SERVER['QUERY_STRING'] = Helper::getPasteId();
|
||||||
$_GET[Helper::getPasteId()] = '';
|
$_GET[Helper::getPasteId()] = '';
|
||||||
$_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
|
$_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
|
||||||
|
@ -813,16 +763,37 @@ class ControllerTest extends PHPUnit_Framework_TestCase
|
||||||
$this->assertEquals(0, $response['comment_offset'], 'outputs comment_offset correctly');
|
$this->assertEquals(0, $response['comment_offset'], 'outputs comment_offset correctly');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @runInSeparateProcess
|
||||||
|
*/
|
||||||
|
public function testReadBurnAfterReading()
|
||||||
|
{
|
||||||
|
$burnPaste = Helper::getPaste();
|
||||||
|
$burnPaste['adata'][3] = 1;
|
||||||
|
$this->_data->create(Helper::getPasteId(), $burnPaste);
|
||||||
|
$this->assertTrue($this->_data->exists(Helper::getPasteId()), 'paste exists before deleting data');
|
||||||
|
$_SERVER['QUERY_STRING'] = Helper::getPasteId();
|
||||||
|
$_GET[Helper::getPasteId()] = '';
|
||||||
|
$_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
|
||||||
|
ob_start();
|
||||||
|
new Controller;
|
||||||
|
$content = ob_get_contents();
|
||||||
|
ob_end_clean();
|
||||||
|
$response = json_decode($content, true);
|
||||||
|
$this->assertEquals(0, $response['status'], 'outputs status');
|
||||||
|
$this->assertFalse($this->_data->exists(Helper::getPasteId()), 'paste successfully deleted');
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @runInSeparateProcess
|
* @runInSeparateProcess
|
||||||
*/
|
*/
|
||||||
public function testDelete()
|
public function testDelete()
|
||||||
{
|
{
|
||||||
$this->_model->create(Helper::getPasteId(), Helper::getPaste());
|
$this->_data->create(Helper::getPasteId(), Helper::getPaste());
|
||||||
$this->assertTrue($this->_model->exists(Helper::getPasteId()), 'paste exists before deleting data');
|
$this->assertTrue($this->_data->exists(Helper::getPasteId()), 'paste exists before deleting data');
|
||||||
$paste = $this->_model->read(Helper::getPasteId());
|
$paste = $this->_data->read(Helper::getPasteId());
|
||||||
$_GET['pasteid'] = Helper::getPasteId();
|
$_GET['pasteid'] = Helper::getPasteId();
|
||||||
$_GET['deletetoken'] = hash_hmac('sha256', Helper::getPasteId(), $paste->meta->salt);
|
$_GET['deletetoken'] = hash_hmac('sha256', Helper::getPasteId(), $paste['meta']['salt']);
|
||||||
ob_start();
|
ob_start();
|
||||||
new Controller;
|
new Controller;
|
||||||
$content = ob_get_contents();
|
$content = ob_get_contents();
|
||||||
|
@ -832,7 +803,7 @@ class ControllerTest extends PHPUnit_Framework_TestCase
|
||||||
$content,
|
$content,
|
||||||
'outputs deleted status correctly'
|
'outputs deleted status correctly'
|
||||||
);
|
);
|
||||||
$this->assertFalse($this->_model->exists(Helper::getPasteId()), 'paste successfully deleted');
|
$this->assertFalse($this->_data->exists(Helper::getPasteId()), 'paste successfully deleted');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -840,7 +811,7 @@ class ControllerTest extends PHPUnit_Framework_TestCase
|
||||||
*/
|
*/
|
||||||
public function testDeleteInvalidId()
|
public function testDeleteInvalidId()
|
||||||
{
|
{
|
||||||
$this->_model->create(Helper::getPasteId(), Helper::getPaste());
|
$this->_data->create(Helper::getPasteId(), Helper::getPaste());
|
||||||
$_GET['pasteid'] = 'foo';
|
$_GET['pasteid'] = 'foo';
|
||||||
$_GET['deletetoken'] = 'bar';
|
$_GET['deletetoken'] = 'bar';
|
||||||
ob_start();
|
ob_start();
|
||||||
|
@ -852,7 +823,7 @@ class ControllerTest extends PHPUnit_Framework_TestCase
|
||||||
$content,
|
$content,
|
||||||
'outputs delete error correctly'
|
'outputs delete error correctly'
|
||||||
);
|
);
|
||||||
$this->assertTrue($this->_model->exists(Helper::getPasteId()), 'paste exists after failing to delete data');
|
$this->assertTrue($this->_data->exists(Helper::getPasteId()), 'paste exists after failing to delete data');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -878,7 +849,7 @@ class ControllerTest extends PHPUnit_Framework_TestCase
|
||||||
*/
|
*/
|
||||||
public function testDeleteInvalidToken()
|
public function testDeleteInvalidToken()
|
||||||
{
|
{
|
||||||
$this->_model->create(Helper::getPasteId(), Helper::getPaste());
|
$this->_data->create(Helper::getPasteId(), Helper::getPaste());
|
||||||
$_GET['pasteid'] = Helper::getPasteId();
|
$_GET['pasteid'] = Helper::getPasteId();
|
||||||
$_GET['deletetoken'] = 'bar';
|
$_GET['deletetoken'] = 'bar';
|
||||||
ob_start();
|
ob_start();
|
||||||
|
@ -890,29 +861,7 @@ class ControllerTest extends PHPUnit_Framework_TestCase
|
||||||
$content,
|
$content,
|
||||||
'outputs delete error correctly'
|
'outputs delete error correctly'
|
||||||
);
|
);
|
||||||
$this->assertTrue($this->_model->exists(Helper::getPasteId()), 'paste exists after failing to delete data');
|
$this->assertTrue($this->_data->exists(Helper::getPasteId()), 'paste exists after failing to delete data');
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @runInSeparateProcess
|
|
||||||
*/
|
|
||||||
public function testDeleteBurnAfterReading()
|
|
||||||
{
|
|
||||||
$burnPaste = Helper::getPaste(array('burnafterreading' => true));
|
|
||||||
$this->_model->create(Helper::getPasteId(), $burnPaste);
|
|
||||||
$this->assertTrue($this->_model->exists(Helper::getPasteId()), 'paste exists before deleting data');
|
|
||||||
$_POST['deletetoken'] = 'burnafterreading';
|
|
||||||
$_SERVER['QUERY_STRING'] = Helper::getPasteId();
|
|
||||||
$_GET[Helper::getPasteId()] = '';
|
|
||||||
$_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
|
|
||||||
$_SERVER['REQUEST_METHOD'] = 'POST';
|
|
||||||
ob_start();
|
|
||||||
new Controller;
|
|
||||||
$content = ob_get_contents();
|
|
||||||
ob_end_clean();
|
|
||||||
$response = json_decode($content, true);
|
|
||||||
$this->assertEquals(0, $response['status'], 'outputs status');
|
|
||||||
$this->assertFalse($this->_model->exists(Helper::getPasteId()), 'paste successfully deleted');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -920,9 +869,13 @@ class ControllerTest extends PHPUnit_Framework_TestCase
|
||||||
*/
|
*/
|
||||||
public function testDeleteInvalidBurnAfterReading()
|
public function testDeleteInvalidBurnAfterReading()
|
||||||
{
|
{
|
||||||
$this->_model->create(Helper::getPasteId(), Helper::getPaste());
|
$this->_data->create(Helper::getPasteId(), Helper::getPaste());
|
||||||
$this->assertTrue($this->_model->exists(Helper::getPasteId()), 'paste exists before deleting data');
|
$this->assertTrue($this->_data->exists(Helper::getPasteId()), 'paste exists before deleting data');
|
||||||
$_POST['deletetoken'] = 'burnafterreading';
|
$file = tempnam(sys_get_temp_dir(), 'FOO');
|
||||||
|
file_put_contents($file, json_encode(array(
|
||||||
|
'deletetoken' => 'burnafterreading',
|
||||||
|
)));
|
||||||
|
Request::setInputStream($file);
|
||||||
$_SERVER['QUERY_STRING'] = Helper::getPasteId();
|
$_SERVER['QUERY_STRING'] = Helper::getPasteId();
|
||||||
$_GET[Helper::getPasteId()] = '';
|
$_GET[Helper::getPasteId()] = '';
|
||||||
$_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
|
$_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
|
||||||
|
@ -933,7 +886,7 @@ class ControllerTest extends PHPUnit_Framework_TestCase
|
||||||
ob_end_clean();
|
ob_end_clean();
|
||||||
$response = json_decode($content, true);
|
$response = json_decode($content, true);
|
||||||
$this->assertEquals(1, $response['status'], 'outputs status');
|
$this->assertEquals(1, $response['status'], 'outputs status');
|
||||||
$this->assertTrue($this->_model->exists(Helper::getPasteId()), 'paste exists after failing to delete data');
|
$this->assertTrue($this->_data->exists(Helper::getPasteId()), 'paste exists after failing to delete data');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -941,10 +894,10 @@ class ControllerTest extends PHPUnit_Framework_TestCase
|
||||||
*/
|
*/
|
||||||
public function testDeleteExpired()
|
public function testDeleteExpired()
|
||||||
{
|
{
|
||||||
$expiredPaste = Helper::getPaste(array('expire_date' => 1000));
|
$expiredPaste = Helper::getPaste(2, array('expire_date' => 1000));
|
||||||
$this->assertFalse($this->_model->exists(Helper::getPasteId()), 'paste does not exist before being created');
|
$this->assertFalse($this->_data->exists(Helper::getPasteId()), 'paste does not exist before being created');
|
||||||
$this->_model->create(Helper::getPasteId(), $expiredPaste);
|
$this->_data->create(Helper::getPasteId(), $expiredPaste);
|
||||||
$this->assertTrue($this->_model->exists(Helper::getPasteId()), 'paste exists before deleting data');
|
$this->assertTrue($this->_data->exists(Helper::getPasteId()), 'paste exists before deleting data');
|
||||||
$_GET['pasteid'] = Helper::getPasteId();
|
$_GET['pasteid'] = Helper::getPasteId();
|
||||||
$_GET['deletetoken'] = 'does not matter in this context, but has to be set';
|
$_GET['deletetoken'] = 'does not matter in this context, but has to be set';
|
||||||
ob_start();
|
ob_start();
|
||||||
|
@ -956,7 +909,7 @@ class ControllerTest extends PHPUnit_Framework_TestCase
|
||||||
$content,
|
$content,
|
||||||
'outputs error correctly'
|
'outputs error correctly'
|
||||||
);
|
);
|
||||||
$this->assertFalse($this->_model->exists(Helper::getPasteId()), 'paste successfully deleted');
|
$this->assertFalse($this->_data->exists(Helper::getPasteId()), 'paste successfully deleted');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -966,8 +919,8 @@ class ControllerTest extends PHPUnit_Framework_TestCase
|
||||||
{
|
{
|
||||||
$paste = Helper::getPaste();
|
$paste = Helper::getPaste();
|
||||||
unset($paste['meta']['salt']);
|
unset($paste['meta']['salt']);
|
||||||
$this->_model->create(Helper::getPasteId(), $paste);
|
$this->_data->create(Helper::getPasteId(), $paste);
|
||||||
$this->assertTrue($this->_model->exists(Helper::getPasteId()), 'paste exists before deleting data');
|
$this->assertTrue($this->_data->exists(Helper::getPasteId()), 'paste exists before deleting data');
|
||||||
$_GET['pasteid'] = Helper::getPasteId();
|
$_GET['pasteid'] = Helper::getPasteId();
|
||||||
$_GET['deletetoken'] = hash_hmac('sha256', Helper::getPasteId(), ServerSalt::get());
|
$_GET['deletetoken'] = hash_hmac('sha256', Helper::getPasteId(), ServerSalt::get());
|
||||||
ob_start();
|
ob_start();
|
||||||
|
@ -979,6 +932,6 @@ class ControllerTest extends PHPUnit_Framework_TestCase
|
||||||
$content,
|
$content,
|
||||||
'outputs deleted status correctly'
|
'outputs deleted status correctly'
|
||||||
);
|
);
|
||||||
$this->assertFalse($this->_model->exists(Helper::getPasteId()), 'paste successfully deleted');
|
$this->assertFalse($this->_data->exists(Helper::getPasteId()), 'paste successfully deleted');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,7 +23,7 @@ class ControllerWithDbTest extends ControllerTest
|
||||||
mkdir($this->_path);
|
mkdir($this->_path);
|
||||||
}
|
}
|
||||||
$this->_options['dsn'] = 'sqlite:' . $this->_path . DIRECTORY_SEPARATOR . 'tst.sq3';
|
$this->_options['dsn'] = 'sqlite:' . $this->_path . DIRECTORY_SEPARATOR . 'tst.sq3';
|
||||||
$this->_model = Database::getInstance($this->_options);
|
$this->_data = Database::getInstance($this->_options);
|
||||||
$this->reset();
|
$this->reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -36,22 +36,31 @@ class DatabaseTest extends PHPUnit_Framework_TestCase
|
||||||
$this->_model->delete(Helper::getPasteId());
|
$this->_model->delete(Helper::getPasteId());
|
||||||
|
|
||||||
// storing pastes
|
// storing pastes
|
||||||
$paste = Helper::getPaste(array('expire_date' => 1344803344));
|
$paste = Helper::getPaste();
|
||||||
$this->assertFalse($this->_model->exists(Helper::getPasteId()), 'paste does not yet exist');
|
$this->assertFalse($this->_model->exists(Helper::getPasteId()), 'paste does not yet exist');
|
||||||
$this->assertTrue($this->_model->create(Helper::getPasteId(), $paste), 'store new paste');
|
$this->assertTrue($this->_model->create(Helper::getPasteId(), $paste), 'store new paste');
|
||||||
$this->assertTrue($this->_model->exists(Helper::getPasteId()), 'paste exists after storing it');
|
$this->assertTrue($this->_model->exists(Helper::getPasteId()), 'paste exists after storing it');
|
||||||
$this->assertFalse($this->_model->create(Helper::getPasteId(), $paste), 'unable to store the same paste twice');
|
$this->assertFalse($this->_model->create(Helper::getPasteId(), $paste), 'unable to store the same paste twice');
|
||||||
$this->assertEquals(json_decode(json_encode($paste)), $this->_model->read(Helper::getPasteId()));
|
$this->assertEquals($paste, $this->_model->read(Helper::getPasteId()));
|
||||||
|
|
||||||
// storing comments
|
// storing comments
|
||||||
$this->assertFalse($this->_model->existsComment(Helper::getPasteId(), Helper::getPasteId(), Helper::getCommentId()), 'comment does not yet exist');
|
$this->assertFalse($this->_model->existsComment(Helper::getPasteId(), Helper::getPasteId(), Helper::getCommentId()), 'v1 comment does not yet exist');
|
||||||
$this->assertTrue($this->_model->createComment(Helper::getPasteId(), Helper::getPasteId(), Helper::getCommentId(), Helper::getComment()) !== false, 'store comment');
|
$this->assertTrue($this->_model->createComment(Helper::getPasteId(), Helper::getPasteId(), Helper::getCommentId(), Helper::getComment(1)) !== false, 'store v1 comment');
|
||||||
$this->assertTrue($this->_model->existsComment(Helper::getPasteId(), Helper::getPasteId(), Helper::getCommentId()), 'comment exists after storing it');
|
$this->assertTrue($this->_model->existsComment(Helper::getPasteId(), Helper::getPasteId(), Helper::getCommentId()), 'v1 comment exists after storing it');
|
||||||
$comment = json_decode(json_encode(Helper::getComment()));
|
$this->assertFalse($this->_model->existsComment(Helper::getPasteId(), Helper::getPasteId(), Helper::getPasteId()), 'v2 comment does not yet exist');
|
||||||
$comment->id = Helper::getCommentId();
|
$this->assertTrue($this->_model->createComment(Helper::getPasteId(), Helper::getPasteId(), Helper::getPasteId(), Helper::getComment(2)) !== false, 'store v2 comment');
|
||||||
$comment->parentid = Helper::getPasteId();
|
$this->assertTrue($this->_model->existsComment(Helper::getPasteId(), Helper::getPasteId(), Helper::getPasteId()), 'v2 comment exists after storing it');
|
||||||
|
$comment1 = Helper::getComment(1);
|
||||||
|
$comment1['id'] = Helper::getCommentId();
|
||||||
|
$comment1['parentid'] = Helper::getPasteId();
|
||||||
|
$comment2 = Helper::getComment(2);
|
||||||
|
$comment2['id'] = Helper::getPasteId();
|
||||||
|
$comment2['parentid'] = Helper::getPasteId();
|
||||||
$this->assertEquals(
|
$this->assertEquals(
|
||||||
array($comment->meta->postdate => $comment),
|
array(
|
||||||
|
$comment1['meta']['postdate'] => $comment1,
|
||||||
|
$comment2['meta']['created'] . '.1' => $comment2,
|
||||||
|
),
|
||||||
$this->_model->readComments(Helper::getPasteId())
|
$this->_model->readComments(Helper::getPasteId())
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -64,8 +73,9 @@ class DatabaseTest extends PHPUnit_Framework_TestCase
|
||||||
|
|
||||||
public function testDatabaseBasedAttachmentStoreWorks()
|
public function testDatabaseBasedAttachmentStoreWorks()
|
||||||
{
|
{
|
||||||
|
// this assumes a version 1 formatted paste
|
||||||
$this->_model->delete(Helper::getPasteId());
|
$this->_model->delete(Helper::getPasteId());
|
||||||
$original = $paste = Helper::getPasteWithAttachment(array('expire_date' => 1344803344));
|
$original = $paste = Helper::getPasteWithAttachment(1, array('expire_date' => 1344803344));
|
||||||
$paste['meta']['burnafterreading'] = $original['meta']['burnafterreading'] = true;
|
$paste['meta']['burnafterreading'] = $original['meta']['burnafterreading'] = true;
|
||||||
$paste['meta']['attachment'] = $paste['attachment'];
|
$paste['meta']['attachment'] = $paste['attachment'];
|
||||||
$paste['meta']['attachmentname'] = $paste['attachmentname'];
|
$paste['meta']['attachmentname'] = $paste['attachmentname'];
|
||||||
|
@ -74,7 +84,7 @@ class DatabaseTest extends PHPUnit_Framework_TestCase
|
||||||
$this->assertTrue($this->_model->create(Helper::getPasteId(), $paste), 'store new paste');
|
$this->assertTrue($this->_model->create(Helper::getPasteId(), $paste), 'store new paste');
|
||||||
$this->assertTrue($this->_model->exists(Helper::getPasteId()), 'paste exists after storing it');
|
$this->assertTrue($this->_model->exists(Helper::getPasteId()), 'paste exists after storing it');
|
||||||
$this->assertFalse($this->_model->create(Helper::getPasteId(), $paste), 'unable to store the same paste twice');
|
$this->assertFalse($this->_model->create(Helper::getPasteId(), $paste), 'unable to store the same paste twice');
|
||||||
$this->assertEquals(json_decode(json_encode($original)), $this->_model->read(Helper::getPasteId()));
|
$this->assertEquals($original, $this->_model->read(Helper::getPasteId()));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -83,12 +93,12 @@ class DatabaseTest extends PHPUnit_Framework_TestCase
|
||||||
public function testPurge()
|
public function testPurge()
|
||||||
{
|
{
|
||||||
$this->_model->delete(Helper::getPasteId());
|
$this->_model->delete(Helper::getPasteId());
|
||||||
$expired = Helper::getPaste(array('expire_date' => 1344803344));
|
$expired = Helper::getPaste(2, array('expire_date' => 1344803344));
|
||||||
$paste = Helper::getPaste(array('expire_date' => time() + 3600));
|
$paste = Helper::getPaste(2, array('expire_date' => time() + 3600));
|
||||||
$keys = array('a', 'b', 'c', 'd', 'e', 'f', 'g', 'x', 'y', 'z');
|
$keys = array('a', 'b', 'c', 'd', 'e', 'f', 'g', 'x', 'y', 'z');
|
||||||
$ids = array();
|
$ids = array();
|
||||||
foreach ($keys as $key) {
|
foreach ($keys as $key) {
|
||||||
$ids[$key] = substr(md5($key), 0, 16);
|
$ids[$key] = hash('fnv164', $key);
|
||||||
$this->_model->delete($ids[$key]);
|
$this->_model->delete($ids[$key]);
|
||||||
$this->assertFalse($this->_model->exists($ids[$key]), "paste $key does not yet exist");
|
$this->assertFalse($this->_model->exists($ids[$key]), "paste $key does not yet exist");
|
||||||
if (in_array($key, array('y', 'z'))) {
|
if (in_array($key, array('y', 'z'))) {
|
||||||
|
@ -243,11 +253,11 @@ class DatabaseTest extends PHPUnit_Framework_TestCase
|
||||||
$this->_options['tbl'] = 'bar_';
|
$this->_options['tbl'] = 'bar_';
|
||||||
$model = Database::getInstance($this->_options);
|
$model = Database::getInstance($this->_options);
|
||||||
|
|
||||||
$original = $paste = Helper::getPasteWithAttachment(array('expire_date' => 1344803344));
|
$original = $paste = Helper::getPasteWithAttachment(1, array('expire_date' => 1344803344));
|
||||||
$paste['meta']['attachment'] = $paste['attachment'];
|
$meta = $paste['meta'];
|
||||||
$paste['meta']['attachmentname'] = $paste['attachmentname'];
|
$meta['attachment'] = $paste['attachment'];
|
||||||
|
$meta['attachmentname'] = $paste['attachmentname'];
|
||||||
unset($paste['attachment'], $paste['attachmentname']);
|
unset($paste['attachment'], $paste['attachmentname']);
|
||||||
$meta = $paste['meta'];
|
|
||||||
|
|
||||||
$db = new PDO(
|
$db = new PDO(
|
||||||
$this->_options['dsn'],
|
$this->_options['dsn'],
|
||||||
|
@ -261,7 +271,7 @@ class DatabaseTest extends PHPUnit_Framework_TestCase
|
||||||
Helper::getPasteId(),
|
Helper::getPasteId(),
|
||||||
$paste['data'],
|
$paste['data'],
|
||||||
$paste['meta']['postdate'],
|
$paste['meta']['postdate'],
|
||||||
1344803344,
|
$paste['meta']['expire_date'],
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
json_encode($meta),
|
json_encode($meta),
|
||||||
|
@ -272,7 +282,7 @@ class DatabaseTest extends PHPUnit_Framework_TestCase
|
||||||
$statement->closeCursor();
|
$statement->closeCursor();
|
||||||
|
|
||||||
$this->assertTrue($model->exists(Helper::getPasteId()), 'paste exists after storing it');
|
$this->assertTrue($model->exists(Helper::getPasteId()), 'paste exists after storing it');
|
||||||
$this->assertEquals(json_decode(json_encode($original)), $model->read(Helper::getPasteId()));
|
$this->assertEquals($original, $model->read(Helper::getPasteId()));
|
||||||
|
|
||||||
Helper::rmDir($this->_path);
|
Helper::rmDir($this->_path);
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,23 +36,23 @@ class FilesystemTest extends PHPUnit_Framework_TestCase
|
||||||
$this->_model->delete(Helper::getPasteId());
|
$this->_model->delete(Helper::getPasteId());
|
||||||
|
|
||||||
// storing pastes
|
// storing pastes
|
||||||
$paste = Helper::getPaste(array('expire_date' => 1344803344));
|
$paste = Helper::getPaste(2, array('expire_date' => 1344803344));
|
||||||
$this->assertFalse($this->_model->exists(Helper::getPasteId()), 'paste does not yet exist');
|
$this->assertFalse($this->_model->exists(Helper::getPasteId()), 'paste does not yet exist');
|
||||||
$this->assertTrue($this->_model->create(Helper::getPasteId(), $paste), 'store new paste');
|
$this->assertTrue($this->_model->create(Helper::getPasteId(), $paste), 'store new paste');
|
||||||
$this->assertTrue($this->_model->exists(Helper::getPasteId()), 'paste exists after storing it');
|
$this->assertTrue($this->_model->exists(Helper::getPasteId()), 'paste exists after storing it');
|
||||||
$this->assertFalse($this->_model->create(Helper::getPasteId(), $paste), 'unable to store the same paste twice');
|
$this->assertFalse($this->_model->create(Helper::getPasteId(), $paste), 'unable to store the same paste twice');
|
||||||
$this->assertEquals(json_decode(json_encode($paste)), $this->_model->read(Helper::getPasteId()));
|
$this->assertEquals($paste, $this->_model->read(Helper::getPasteId()));
|
||||||
|
|
||||||
// storing comments
|
// storing comments
|
||||||
$this->assertFalse($this->_model->existsComment(Helper::getPasteId(), Helper::getPasteId(), Helper::getCommentId()), 'comment does not yet exist');
|
$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->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->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');
|
$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 = Helper::getComment();
|
||||||
$comment->id = Helper::getCommentId();
|
$comment['id'] = Helper::getCommentId();
|
||||||
$comment->parentid = Helper::getPasteId();
|
$comment['parentid'] = Helper::getPasteId();
|
||||||
$this->assertEquals(
|
$this->assertEquals(
|
||||||
array($comment->meta->postdate => $comment),
|
array($comment['meta']['created'] => $comment),
|
||||||
$this->_model->readComments(Helper::getPasteId())
|
$this->_model->readComments(Helper::getPasteId())
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -66,7 +66,7 @@ class FilesystemTest extends PHPUnit_Framework_TestCase
|
||||||
public function testFileBasedAttachmentStoreWorks()
|
public function testFileBasedAttachmentStoreWorks()
|
||||||
{
|
{
|
||||||
$this->_model->delete(Helper::getPasteId());
|
$this->_model->delete(Helper::getPasteId());
|
||||||
$original = $paste = Helper::getPasteWithAttachment(array('expire_date' => 1344803344));
|
$original = $paste = Helper::getPasteWithAttachment(1, array('expire_date' => 1344803344));
|
||||||
$paste['meta']['attachment'] = $paste['attachment'];
|
$paste['meta']['attachment'] = $paste['attachment'];
|
||||||
$paste['meta']['attachmentname'] = $paste['attachmentname'];
|
$paste['meta']['attachmentname'] = $paste['attachmentname'];
|
||||||
unset($paste['attachment'], $paste['attachmentname']);
|
unset($paste['attachment'], $paste['attachmentname']);
|
||||||
|
@ -74,7 +74,7 @@ class FilesystemTest extends PHPUnit_Framework_TestCase
|
||||||
$this->assertTrue($this->_model->create(Helper::getPasteId(), $paste), 'store new paste');
|
$this->assertTrue($this->_model->create(Helper::getPasteId(), $paste), 'store new paste');
|
||||||
$this->assertTrue($this->_model->exists(Helper::getPasteId()), 'paste exists after storing it');
|
$this->assertTrue($this->_model->exists(Helper::getPasteId()), 'paste exists after storing it');
|
||||||
$this->assertFalse($this->_model->create(Helper::getPasteId(), $paste), 'unable to store the same paste twice');
|
$this->assertFalse($this->_model->create(Helper::getPasteId(), $paste), 'unable to store the same paste twice');
|
||||||
$this->assertEquals(json_decode(json_encode($original)), $this->_model->read(Helper::getPasteId()));
|
$this->assertEquals($original, $this->_model->read(Helper::getPasteId()));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -83,12 +83,12 @@ class FilesystemTest extends PHPUnit_Framework_TestCase
|
||||||
public function testPurge()
|
public function testPurge()
|
||||||
{
|
{
|
||||||
mkdir($this->_path . DIRECTORY_SEPARATOR . '00', 0777, true);
|
mkdir($this->_path . DIRECTORY_SEPARATOR . '00', 0777, true);
|
||||||
$expired = Helper::getPaste(array('expire_date' => 1344803344));
|
$expired = Helper::getPaste(2, array('expire_date' => 1344803344));
|
||||||
$paste = Helper::getPaste(array('expire_date' => time() + 3600));
|
$paste = Helper::getPaste(2, array('expire_date' => time() + 3600));
|
||||||
$keys = array('a', 'b', 'c', 'd', 'e', 'f', 'g', 'x', 'y', 'z');
|
$keys = array('a', 'b', 'c', 'd', 'e', 'f', 'g', 'x', 'y', 'z');
|
||||||
$ids = array();
|
$ids = array();
|
||||||
foreach ($keys as $key) {
|
foreach ($keys as $key) {
|
||||||
$ids[$key] = substr(md5($key), 0, 16);
|
$ids[$key] = hash('fnv164', $key);
|
||||||
$this->assertFalse($this->_model->exists($ids[$key]), "paste $key does not yet exist");
|
$this->assertFalse($this->_model->exists($ids[$key]), "paste $key does not yet exist");
|
||||||
if (in_array($key, array('x', 'y', 'z'))) {
|
if (in_array($key, array('x', 'y', 'z'))) {
|
||||||
$this->assertTrue($this->_model->create($ids[$key], $paste), "store $key paste");
|
$this->assertTrue($this->_model->create($ids[$key], $paste), "store $key paste");
|
||||||
|
@ -113,7 +113,7 @@ class FilesystemTest extends PHPUnit_Framework_TestCase
|
||||||
public function testErrorDetection()
|
public function testErrorDetection()
|
||||||
{
|
{
|
||||||
$this->_model->delete(Helper::getPasteId());
|
$this->_model->delete(Helper::getPasteId());
|
||||||
$paste = Helper::getPaste(array('formatter' => "Invalid UTF-8 sequence: \xB1\x31"));
|
$paste = Helper::getPaste(2, array('expire' => "Invalid UTF-8 sequence: \xB1\x31"));
|
||||||
$this->assertFalse($this->_model->exists(Helper::getPasteId()), 'paste does not yet exist');
|
$this->assertFalse($this->_model->exists(Helper::getPasteId()), 'paste does not yet exist');
|
||||||
$this->assertFalse($this->_model->create(Helper::getPasteId(), $paste), 'unable to store broken paste');
|
$this->assertFalse($this->_model->create(Helper::getPasteId(), $paste), 'unable to store broken paste');
|
||||||
$this->assertFalse($this->_model->exists(Helper::getPasteId()), 'paste does still not exist');
|
$this->assertFalse($this->_model->exists(Helper::getPasteId()), 'paste does still not exist');
|
||||||
|
@ -122,7 +122,7 @@ class FilesystemTest extends PHPUnit_Framework_TestCase
|
||||||
public function testCommentErrorDetection()
|
public function testCommentErrorDetection()
|
||||||
{
|
{
|
||||||
$this->_model->delete(Helper::getPasteId());
|
$this->_model->delete(Helper::getPasteId());
|
||||||
$comment = Helper::getComment(array('formatter' => "Invalid UTF-8 sequence: \xB1\x31"));
|
$comment = Helper::getComment(1, array('nickname' => "Invalid UTF-8 sequence: \xB1\x31"));
|
||||||
$this->assertFalse($this->_model->exists(Helper::getPasteId()), 'paste does not yet exist');
|
$this->assertFalse($this->_model->exists(Helper::getPasteId()), 'paste does not yet exist');
|
||||||
$this->assertTrue($this->_model->create(Helper::getPasteId(), Helper::getPaste()), 'store new paste');
|
$this->assertTrue($this->_model->create(Helper::getPasteId(), Helper::getPaste()), 'store new paste');
|
||||||
$this->assertTrue($this->_model->exists(Helper::getPasteId()), 'paste exists after storing it');
|
$this->assertTrue($this->_model->exists(Helper::getPasteId()), 'paste exists after storing it');
|
||||||
|
@ -163,16 +163,16 @@ class FilesystemTest extends PHPUnit_Framework_TestCase
|
||||||
$this->assertFileExists($storagedir . $dataid . '.php', "paste $dataid exists in new format");
|
$this->assertFileExists($storagedir . $dataid . '.php', "paste $dataid exists in new format");
|
||||||
$this->assertFileNotExists($storagedir . $dataid, "old format paste $dataid got removed");
|
$this->assertFileNotExists($storagedir . $dataid, "old format paste $dataid got removed");
|
||||||
$this->assertTrue($this->_model->exists($dataid), "paste $dataid exists");
|
$this->assertTrue($this->_model->exists($dataid), "paste $dataid exists");
|
||||||
$this->assertEquals($this->_model->read($dataid), json_decode(json_encode($paste)), "paste $dataid wasn't modified in the conversion");
|
$this->assertEquals($this->_model->read($dataid), $paste, "paste $dataid wasn't modified in the conversion");
|
||||||
|
|
||||||
$storagedir .= $dataid . '.discussion' . DIRECTORY_SEPARATOR;
|
$storagedir .= $dataid . '.discussion' . DIRECTORY_SEPARATOR;
|
||||||
$this->assertFileExists($storagedir . $dataid . '.' . $commentid . '.' . $dataid . '.php', "comment of $dataid exists in new format");
|
$this->assertFileExists($storagedir . $dataid . '.' . $commentid . '.' . $dataid . '.php', "comment of $dataid exists in new format");
|
||||||
$this->assertFileNotExists($storagedir . $dataid . '.' . $commentid . '.' . $dataid, "old format comment of $dataid got removed");
|
$this->assertFileNotExists($storagedir . $dataid . '.' . $commentid . '.' . $dataid, "old format comment of $dataid got removed");
|
||||||
$this->assertTrue($this->_model->existsComment($dataid, $dataid, $commentid), "comment in paste $dataid exists");
|
$this->assertTrue($this->_model->existsComment($dataid, $dataid, $commentid), "comment in paste $dataid exists");
|
||||||
$comment = json_decode(json_encode($comment));
|
$comment = $comment;
|
||||||
$comment->id = $commentid;
|
$comment['id'] = $commentid;
|
||||||
$comment->parentid = $dataid;
|
$comment['parentid'] = $dataid;
|
||||||
$this->assertEquals($this->_model->readComments($dataid), array($comment->meta->postdate => $comment), "comment of $dataid wasn't modified in the conversion");
|
$this->assertEquals($this->_model->readComments($dataid), array($comment['meta']['created'] => $comment), "comment of $dataid wasn't modified in the conversion");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,72 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use PrivateBin\FormatV2;
|
||||||
|
|
||||||
|
class FormatV2Test extends PHPUnit_Framework_TestCase
|
||||||
|
{
|
||||||
|
public function testFormatV2ValidatorValidatesCorrectly()
|
||||||
|
{
|
||||||
|
$this->assertTrue(FormatV2::isValid(Helper::getPastePost()), 'valid format');
|
||||||
|
$this->assertTrue(FormatV2::isValid(Helper::getCommentPost(), true), 'valid format');
|
||||||
|
|
||||||
|
$paste = Helper::getPastePost();
|
||||||
|
$paste['adata'][0][0] = '$';
|
||||||
|
$this->assertFalse(FormatV2::isValid($paste), 'invalid base64 encoding of iv');
|
||||||
|
|
||||||
|
$paste = Helper::getPastePost();
|
||||||
|
$paste['adata'][0][1] = '$';
|
||||||
|
$this->assertFalse(FormatV2::isValid($paste), 'invalid base64 encoding of salt');
|
||||||
|
|
||||||
|
$paste = Helper::getPastePost();
|
||||||
|
$paste['ct'] = '$';
|
||||||
|
$this->assertFalse(FormatV2::isValid($paste), 'invalid base64 encoding of ct');
|
||||||
|
|
||||||
|
$paste = Helper::getPastePost();
|
||||||
|
$paste['ct'] = 'bm9kYXRhbm9kYXRhbm9kYXRhbm9kYXRhbm9kYXRhCg==';
|
||||||
|
$this->assertFalse(FormatV2::isValid($paste), 'low ct entropy');
|
||||||
|
|
||||||
|
$paste = Helper::getPastePost();
|
||||||
|
$paste['adata'][0][0] = 'MTIzNDU2Nzg5MDEyMzQ1Njc4OTA=';
|
||||||
|
$this->assertFalse(FormatV2::isValid($paste), 'iv too long');
|
||||||
|
|
||||||
|
$paste = Helper::getPastePost();
|
||||||
|
$paste['adata'][0][1] = 'MTIzNDU2Nzg5MDEyMzQ1Njc4OTA=';
|
||||||
|
$this->assertFalse(FormatV2::isValid($paste), 'salt too long');
|
||||||
|
|
||||||
|
$paste = Helper::getPastePost();
|
||||||
|
$paste['foo'] = 'bar';
|
||||||
|
$this->assertFalse(FormatV2::isValid($paste), 'invalid additional key');
|
||||||
|
unset($paste['meta']);
|
||||||
|
$this->assertFalse(FormatV2::isValid($paste), 'invalid missing key');
|
||||||
|
|
||||||
|
$paste = Helper::getPastePost();
|
||||||
|
$paste['v'] = 0.9;
|
||||||
|
$this->assertFalse(FormatV2::isValid($paste), 'unsupported version');
|
||||||
|
|
||||||
|
$paste = Helper::getPastePost();
|
||||||
|
$paste['adata'][0][2] = 1000;
|
||||||
|
$this->assertFalse(FormatV2::isValid($paste), 'not enough iterations');
|
||||||
|
|
||||||
|
$paste = Helper::getPastePost();
|
||||||
|
$paste['adata'][0][3] = 127;
|
||||||
|
$this->assertFalse(FormatV2::isValid($paste), 'invalid key size');
|
||||||
|
|
||||||
|
$paste = Helper::getPastePost();
|
||||||
|
$paste['adata'][0][4] = 63;
|
||||||
|
$this->assertFalse(FormatV2::isValid($paste), 'invalid tag length');
|
||||||
|
|
||||||
|
$paste = Helper::getPastePost();
|
||||||
|
$paste['adata'][0][5] = '!#@';
|
||||||
|
$this->assertFalse(FormatV2::isValid($paste), 'invalid algorithm');
|
||||||
|
|
||||||
|
$paste = Helper::getPastePost();
|
||||||
|
$paste['adata'][0][6] = '!#@';
|
||||||
|
$this->assertFalse(FormatV2::isValid($paste), 'invalid mode');
|
||||||
|
|
||||||
|
$paste = Helper::getPastePost();
|
||||||
|
$paste['adata'][0][7] = '!#@';
|
||||||
|
$this->assertFalse(FormatV2::isValid($paste), 'invalid compression');
|
||||||
|
|
||||||
|
$this->assertFalse(FormatV2::isValid(Helper::getPaste()), 'invalid meta key');
|
||||||
|
}
|
||||||
|
}
|
|
@ -26,6 +26,7 @@ class I18nTest extends PHPUnit_Framework_TestCase
|
||||||
$messageId = 'It does not matter if the message ID exists';
|
$messageId = 'It does not matter if the message ID exists';
|
||||||
I18n::loadTranslations();
|
I18n::loadTranslations();
|
||||||
$this->assertEquals($messageId, I18n::_($messageId), 'fallback to en');
|
$this->assertEquals($messageId, I18n::_($messageId), 'fallback to en');
|
||||||
|
I18n::getLanguageLabels();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testCookieLanguageDeDetection()
|
public function testCookieLanguageDeDetection()
|
||||||
|
@ -40,7 +41,7 @@ class I18nTest extends PHPUnit_Framework_TestCase
|
||||||
|
|
||||||
public function testBrowserLanguageDeDetection()
|
public function testBrowserLanguageDeDetection()
|
||||||
{
|
{
|
||||||
$_SERVER['HTTP_ACCEPT_LANGUAGE'] = 'de-CH,de;q=0.8,en-GB;q=0.6,en-US;q=0.4,en;q=0.2';
|
$_SERVER['HTTP_ACCEPT_LANGUAGE'] = 'de-CH,de;q=0.8,en-GB;q=0.6,en-US;q=0.4,en;q=0.2,fr;q=0.0';
|
||||||
I18n::loadTranslations();
|
I18n::loadTranslations();
|
||||||
$this->assertEquals($this->_translations['en'], I18n::_('en'), 'browser language de');
|
$this->assertEquals($this->_translations['en'], I18n::_('en'), 'browser language de');
|
||||||
$this->assertEquals('0 Stunden', I18n::_('%d hours', 0), '0 hours in German');
|
$this->assertEquals('0 Stunden', I18n::_('%d hours', 0), '0 hours in German');
|
||||||
|
@ -50,7 +51,7 @@ class I18nTest extends PHPUnit_Framework_TestCase
|
||||||
|
|
||||||
public function testBrowserLanguageFrDetection()
|
public function testBrowserLanguageFrDetection()
|
||||||
{
|
{
|
||||||
$_SERVER['HTTP_ACCEPT_LANGUAGE'] = 'fr-CH,fr;q=0.8,en-GB;q=0.6,en-US;q=0.4,en;q=0.2';
|
$_SERVER['HTTP_ACCEPT_LANGUAGE'] = 'fr-CH,fr;q=0.8,en-GB;q=0.6,en-US;q=0.4,en;q=0.2,de;q=0.0';
|
||||||
I18n::loadTranslations();
|
I18n::loadTranslations();
|
||||||
$this->assertEquals('fr', I18n::_('en'), 'browser language fr');
|
$this->assertEquals('fr', I18n::_('en'), 'browser language fr');
|
||||||
$this->assertEquals('0 heure', I18n::_('%d hours', 0), '0 hours in French');
|
$this->assertEquals('0 heure', I18n::_('%d hours', 0), '0 hours in French');
|
||||||
|
|
|
@ -48,10 +48,14 @@ class JsonApiTest extends PHPUnit_Framework_TestCase
|
||||||
$options = parse_ini_file(CONF, true);
|
$options = parse_ini_file(CONF, true);
|
||||||
$options['traffic']['limit'] = 0;
|
$options['traffic']['limit'] = 0;
|
||||||
Helper::createIniFile(CONF, $options);
|
Helper::createIniFile(CONF, $options);
|
||||||
$_POST = Helper::getPaste();
|
$paste = Helper::getPasteJson();
|
||||||
|
$file = tempnam(sys_get_temp_dir(), 'FOO');
|
||||||
|
file_put_contents($file, $paste);
|
||||||
|
Request::setInputStream($file);
|
||||||
$_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
|
$_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
|
||||||
$_SERVER['REQUEST_METHOD'] = 'POST';
|
$_SERVER['REQUEST_METHOD'] = 'POST';
|
||||||
$_SERVER['REMOTE_ADDR'] = '::1';
|
$_SERVER['REMOTE_ADDR'] = '::1';
|
||||||
|
$_SERVER['REQUEST_URI'] = '/';
|
||||||
ob_start();
|
ob_start();
|
||||||
new Controller;
|
new Controller;
|
||||||
$content = ob_get_contents();
|
$content = ob_get_contents();
|
||||||
|
@ -62,7 +66,7 @@ class JsonApiTest extends PHPUnit_Framework_TestCase
|
||||||
$this->assertTrue($this->_model->exists($response['id']), 'paste exists after posting data');
|
$this->assertTrue($this->_model->exists($response['id']), 'paste exists after posting data');
|
||||||
$paste = $this->_model->read($response['id']);
|
$paste = $this->_model->read($response['id']);
|
||||||
$this->assertEquals(
|
$this->assertEquals(
|
||||||
hash_hmac('sha256', $response['id'], $paste->meta->salt),
|
hash_hmac('sha256', $response['id'], $paste['meta']['salt']),
|
||||||
$response['deletetoken'],
|
$response['deletetoken'],
|
||||||
'outputs valid delete token'
|
'outputs valid delete token'
|
||||||
);
|
);
|
||||||
|
@ -76,10 +80,9 @@ class JsonApiTest extends PHPUnit_Framework_TestCase
|
||||||
$options = parse_ini_file(CONF, true);
|
$options = parse_ini_file(CONF, true);
|
||||||
$options['traffic']['limit'] = 0;
|
$options['traffic']['limit'] = 0;
|
||||||
Helper::createIniFile(CONF, $options);
|
Helper::createIniFile(CONF, $options);
|
||||||
$paste = Helper::getPaste();
|
$paste = Helper::getPasteJson();
|
||||||
unset($paste['meta']);
|
$file = tempnam(sys_get_temp_dir(), 'FOO');
|
||||||
$file = tempnam(sys_get_temp_dir(), 'FOO');
|
file_put_contents($file, $paste);
|
||||||
file_put_contents($file, http_build_query($paste));
|
|
||||||
Request::setInputStream($file);
|
Request::setInputStream($file);
|
||||||
$_SERVER['QUERY_STRING'] = Helper::getPasteId();
|
$_SERVER['QUERY_STRING'] = Helper::getPasteId();
|
||||||
$_GET[Helper::getPasteId()] = '';
|
$_GET[Helper::getPasteId()] = '';
|
||||||
|
@ -98,7 +101,7 @@ class JsonApiTest extends PHPUnit_Framework_TestCase
|
||||||
$this->assertTrue($this->_model->exists($response['id']), 'paste exists after posting data');
|
$this->assertTrue($this->_model->exists($response['id']), 'paste exists after posting data');
|
||||||
$paste = $this->_model->read($response['id']);
|
$paste = $this->_model->read($response['id']);
|
||||||
$this->assertEquals(
|
$this->assertEquals(
|
||||||
hash_hmac('sha256', $response['id'], $paste->meta->salt),
|
hash_hmac('sha256', $response['id'], $paste['meta']['salt']),
|
||||||
$response['deletetoken'],
|
$response['deletetoken'],
|
||||||
'outputs valid delete token'
|
'outputs valid delete token'
|
||||||
);
|
);
|
||||||
|
@ -113,8 +116,8 @@ class JsonApiTest extends PHPUnit_Framework_TestCase
|
||||||
$this->assertTrue($this->_model->exists(Helper::getPasteId()), 'paste exists before deleting data');
|
$this->assertTrue($this->_model->exists(Helper::getPasteId()), 'paste exists before deleting data');
|
||||||
$paste = $this->_model->read(Helper::getPasteId());
|
$paste = $this->_model->read(Helper::getPasteId());
|
||||||
$file = tempnam(sys_get_temp_dir(), 'FOO');
|
$file = tempnam(sys_get_temp_dir(), 'FOO');
|
||||||
file_put_contents($file, http_build_query(array(
|
file_put_contents($file, json_encode(array(
|
||||||
'deletetoken' => hash_hmac('sha256', Helper::getPasteId(), $paste->meta->salt),
|
'deletetoken' => hash_hmac('sha256', Helper::getPasteId(), $paste['meta']['salt']),
|
||||||
)));
|
)));
|
||||||
Request::setInputStream($file);
|
Request::setInputStream($file);
|
||||||
$_SERVER['QUERY_STRING'] = Helper::getPasteId();
|
$_SERVER['QUERY_STRING'] = Helper::getPasteId();
|
||||||
|
@ -139,10 +142,12 @@ class JsonApiTest extends PHPUnit_Framework_TestCase
|
||||||
$this->_model->create(Helper::getPasteId(), Helper::getPaste());
|
$this->_model->create(Helper::getPasteId(), Helper::getPaste());
|
||||||
$this->assertTrue($this->_model->exists(Helper::getPasteId()), 'paste exists before deleting data');
|
$this->assertTrue($this->_model->exists(Helper::getPasteId()), 'paste exists before deleting data');
|
||||||
$paste = $this->_model->read(Helper::getPasteId());
|
$paste = $this->_model->read(Helper::getPasteId());
|
||||||
$_POST = array(
|
$file = tempnam(sys_get_temp_dir(), 'FOO');
|
||||||
|
file_put_contents($file, json_encode(array(
|
||||||
'pasteid' => Helper::getPasteId(),
|
'pasteid' => Helper::getPasteId(),
|
||||||
'deletetoken' => hash_hmac('sha256', Helper::getPasteId(), $paste->meta->salt),
|
'deletetoken' => hash_hmac('sha256', Helper::getPasteId(), $paste['meta']['salt']),
|
||||||
);
|
)));
|
||||||
|
Request::setInputStream($file);
|
||||||
$_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
|
$_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
|
||||||
$_SERVER['REQUEST_METHOD'] = 'POST';
|
$_SERVER['REQUEST_METHOD'] = 'POST';
|
||||||
ob_start();
|
ob_start();
|
||||||
|
@ -159,11 +164,7 @@ class JsonApiTest extends PHPUnit_Framework_TestCase
|
||||||
*/
|
*/
|
||||||
public function testRead()
|
public function testRead()
|
||||||
{
|
{
|
||||||
$paste = Helper::getPasteWithAttachment();
|
$paste = Helper::getPaste();
|
||||||
$paste['meta']['attachment'] = $paste['attachment'];
|
|
||||||
$paste['meta']['attachmentname'] = $paste['attachmentname'];
|
|
||||||
unset($paste['attachment']);
|
|
||||||
unset($paste['attachmentname']);
|
|
||||||
$this->_model->create(Helper::getPasteId(), $paste);
|
$this->_model->create(Helper::getPasteId(), $paste);
|
||||||
$_SERVER['QUERY_STRING'] = Helper::getPasteId();
|
$_SERVER['QUERY_STRING'] = Helper::getPasteId();
|
||||||
$_GET[Helper::getPasteId()] = '';
|
$_GET[Helper::getPasteId()] = '';
|
||||||
|
@ -176,12 +177,8 @@ class JsonApiTest extends PHPUnit_Framework_TestCase
|
||||||
$this->assertEquals(0, $response['status'], 'outputs success status');
|
$this->assertEquals(0, $response['status'], 'outputs success status');
|
||||||
$this->assertEquals(Helper::getPasteId(), $response['id'], 'outputs data correctly');
|
$this->assertEquals(Helper::getPasteId(), $response['id'], 'outputs data correctly');
|
||||||
$this->assertStringEndsWith('?' . $response['id'], $response['url'], 'returned URL points to new paste');
|
$this->assertStringEndsWith('?' . $response['id'], $response['url'], 'returned URL points to new paste');
|
||||||
$this->assertEquals($paste['data'], $response['data'], 'outputs data correctly');
|
$this->assertEquals($paste['ct'], $response['ct'], 'outputs data correctly');
|
||||||
$this->assertEquals($paste['meta']['attachment'], $response['attachment'], 'outputs attachment correctly');
|
$this->assertEquals($paste['meta']['created'], $response['meta']['created'], 'outputs postdate correctly');
|
||||||
$this->assertEquals($paste['meta']['attachmentname'], $response['attachmentname'], 'outputs attachmentname correctly');
|
|
||||||
$this->assertEquals($paste['meta']['formatter'], $response['meta']['formatter'], 'outputs format correctly');
|
|
||||||
$this->assertEquals($paste['meta']['postdate'], $response['meta']['postdate'], 'outputs postdate correctly');
|
|
||||||
$this->assertEquals($paste['meta']['opendiscussion'], $response['meta']['opendiscussion'], 'outputs opendiscussion correctly');
|
|
||||||
$this->assertEquals(0, $response['comment_count'], 'outputs comment_count correctly');
|
$this->assertEquals(0, $response['comment_count'], 'outputs comment_count correctly');
|
||||||
$this->assertEquals(0, $response['comment_offset'], 'outputs comment_offset correctly');
|
$this->assertEquals(0, $response['comment_offset'], 'outputs comment_offset correctly');
|
||||||
}
|
}
|
||||||
|
@ -191,7 +188,7 @@ class JsonApiTest extends PHPUnit_Framework_TestCase
|
||||||
*/
|
*/
|
||||||
public function testJsonLdPaste()
|
public function testJsonLdPaste()
|
||||||
{
|
{
|
||||||
$paste = Helper::getPasteWithAttachment();
|
$paste = Helper::getPaste();
|
||||||
$this->_model->create(Helper::getPasteId(), $paste);
|
$this->_model->create(Helper::getPasteId(), $paste);
|
||||||
$_GET['jsonld'] = 'paste';
|
$_GET['jsonld'] = 'paste';
|
||||||
ob_start();
|
ob_start();
|
||||||
|
@ -210,7 +207,7 @@ class JsonApiTest extends PHPUnit_Framework_TestCase
|
||||||
*/
|
*/
|
||||||
public function testJsonLdComment()
|
public function testJsonLdComment()
|
||||||
{
|
{
|
||||||
$paste = Helper::getPasteWithAttachment();
|
$paste = Helper::getPaste();
|
||||||
$this->_model->create(Helper::getPasteId(), $paste);
|
$this->_model->create(Helper::getPasteId(), $paste);
|
||||||
$_GET['jsonld'] = 'comment';
|
$_GET['jsonld'] = 'comment';
|
||||||
ob_start();
|
ob_start();
|
||||||
|
@ -229,7 +226,7 @@ class JsonApiTest extends PHPUnit_Framework_TestCase
|
||||||
*/
|
*/
|
||||||
public function testJsonLdPasteMeta()
|
public function testJsonLdPasteMeta()
|
||||||
{
|
{
|
||||||
$paste = Helper::getPasteWithAttachment();
|
$paste = Helper::getPaste();
|
||||||
$this->_model->create(Helper::getPasteId(), $paste);
|
$this->_model->create(Helper::getPasteId(), $paste);
|
||||||
$_GET['jsonld'] = 'pastemeta';
|
$_GET['jsonld'] = 'pastemeta';
|
||||||
ob_start();
|
ob_start();
|
||||||
|
@ -248,7 +245,7 @@ class JsonApiTest extends PHPUnit_Framework_TestCase
|
||||||
*/
|
*/
|
||||||
public function testJsonLdCommentMeta()
|
public function testJsonLdCommentMeta()
|
||||||
{
|
{
|
||||||
$paste = Helper::getPasteWithAttachment();
|
$paste = Helper::getPaste();
|
||||||
$this->_model->create(Helper::getPasteId(), $paste);
|
$this->_model->create(Helper::getPasteId(), $paste);
|
||||||
$_GET['jsonld'] = 'commentmeta';
|
$_GET['jsonld'] = 'commentmeta';
|
||||||
ob_start();
|
ob_start();
|
||||||
|
@ -267,7 +264,7 @@ class JsonApiTest extends PHPUnit_Framework_TestCase
|
||||||
*/
|
*/
|
||||||
public function testJsonLdInvalid()
|
public function testJsonLdInvalid()
|
||||||
{
|
{
|
||||||
$paste = Helper::getPasteWithAttachment();
|
$paste = Helper::getPaste();
|
||||||
$this->_model->create(Helper::getPasteId(), $paste);
|
$this->_model->create(Helper::getPasteId(), $paste);
|
||||||
$_GET['jsonld'] = CONF;
|
$_GET['jsonld'] = CONF;
|
||||||
ob_start();
|
ob_start();
|
||||||
|
|
|
@ -4,6 +4,7 @@ use Identicon\Identicon;
|
||||||
use PrivateBin\Configuration;
|
use PrivateBin\Configuration;
|
||||||
use PrivateBin\Data\Database;
|
use PrivateBin\Data\Database;
|
||||||
use PrivateBin\Model;
|
use PrivateBin\Model;
|
||||||
|
use PrivateBin\Model\Comment;
|
||||||
use PrivateBin\Model\Paste;
|
use PrivateBin\Model\Paste;
|
||||||
use PrivateBin\Persistence\ServerSalt;
|
use PrivateBin\Persistence\ServerSalt;
|
||||||
use PrivateBin\Persistence\TrafficLimiter;
|
use PrivateBin\Persistence\TrafficLimiter;
|
||||||
|
@ -54,42 +55,45 @@ class ModelTest extends PHPUnit_Framework_TestCase
|
||||||
public function testBasicWorkflow()
|
public function testBasicWorkflow()
|
||||||
{
|
{
|
||||||
// storing pastes
|
// storing pastes
|
||||||
$pasteData = Helper::getPaste();
|
$pasteData = Helper::getPastePost();
|
||||||
|
unset($pasteData['meta']['created'], $pasteData['meta']['salt']);
|
||||||
$this->_model->getPaste(Helper::getPasteId())->delete();
|
$this->_model->getPaste(Helper::getPasteId())->delete();
|
||||||
$paste = $this->_model->getPaste(Helper::getPasteId());
|
$paste = $this->_model->getPaste(Helper::getPasteId());
|
||||||
$this->assertFalse($paste->exists(), 'paste does not yet exist');
|
$this->assertFalse($paste->exists(), 'paste does not yet exist');
|
||||||
|
|
||||||
$paste = $this->_model->getPaste();
|
$paste = $this->_model->getPaste();
|
||||||
$paste->setData($pasteData['data']);
|
$paste->setData($pasteData);
|
||||||
$paste->setOpendiscussion();
|
|
||||||
$paste->setFormatter($pasteData['meta']['formatter']);
|
|
||||||
$paste->store();
|
$paste->store();
|
||||||
|
|
||||||
$paste = $this->_model->getPaste(Helper::getPasteId());
|
$paste = $this->_model->getPaste(Helper::getPasteId());
|
||||||
$this->assertTrue($paste->exists(), 'paste exists after storing it');
|
$this->assertTrue($paste->exists(), 'paste exists after storing it');
|
||||||
$paste = $paste->get();
|
$paste = $paste->get();
|
||||||
$this->assertEquals($pasteData['data'], $paste->data);
|
unset(
|
||||||
foreach (array('opendiscussion', 'formatter') as $key) {
|
$pasteData['meta'],
|
||||||
$this->assertEquals($pasteData['meta'][$key], $paste->meta->$key);
|
$paste['meta'],
|
||||||
}
|
$paste['comments'],
|
||||||
|
$paste['comment_count'],
|
||||||
|
$paste['comment_offset'],
|
||||||
|
$paste['@context']
|
||||||
|
);
|
||||||
|
$this->assertEquals($pasteData, $paste);
|
||||||
|
|
||||||
// storing comments
|
// storing comments
|
||||||
$commentData = Helper::getComment();
|
$commentData = Helper::getCommentPost();
|
||||||
$paste = $this->_model->getPaste(Helper::getPasteId());
|
$paste = $this->_model->getPaste(Helper::getPasteId());
|
||||||
$comment = $paste->getComment(Helper::getPasteId(), Helper::getCommentId());
|
$comment = $paste->getComment(Helper::getPasteId(), Helper::getCommentId());
|
||||||
$this->assertFalse($comment->exists(), 'comment does not yet exist');
|
$this->assertFalse($comment->exists(), 'comment does not yet exist');
|
||||||
|
|
||||||
$comment = $paste->getComment(Helper::getPasteId());
|
$comment = $paste->getComment(Helper::getPasteId());
|
||||||
$comment->setData($commentData['data']);
|
$comment->setData($commentData);
|
||||||
$comment->setNickname($commentData['meta']['nickname']);
|
|
||||||
$comment->getParentId();
|
|
||||||
$comment->store();
|
$comment->store();
|
||||||
|
|
||||||
$comment = $paste->getComment(Helper::getPasteId(), Helper::getCommentId());
|
$comments = $this->_model->getPaste(Helper::getPasteId())->get()['comments'];
|
||||||
$this->assertTrue($comment->exists(), 'comment exists after storing it');
|
$this->assertTrue(count($comments) === 1, 'comment exists after storing it');
|
||||||
$comment = $comment->get();
|
$commentData['id'] = Helper::getPasteId();
|
||||||
$this->assertEquals($commentData['data'], $comment->data);
|
$commentData['meta']['created'] = current($comments)['meta']['created'];
|
||||||
$this->assertEquals($commentData['meta']['nickname'], $comment->meta->nickname);
|
$commentData['meta']['icon'] = current($comments)['meta']['icon'];
|
||||||
|
$this->assertEquals($commentData, current($comments));
|
||||||
|
|
||||||
// deleting pastes
|
// deleting pastes
|
||||||
$this->_model->getPaste(Helper::getPasteId())->delete();
|
$this->_model->getPaste(Helper::getPasteId())->delete();
|
||||||
|
@ -98,25 +102,34 @@ class ModelTest extends PHPUnit_Framework_TestCase
|
||||||
$this->assertEquals(array(), $paste->getComments(), 'comment was deleted with paste');
|
$this->assertEquals(array(), $paste->getComments(), 'comment was deleted with paste');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testCommentDefaults()
|
||||||
|
{
|
||||||
|
$comment = new Comment(
|
||||||
|
$this->_conf,
|
||||||
|
forward_static_call(
|
||||||
|
'PrivateBin\\Data\\' . $this->_conf->getKey('class', 'model') . '::getInstance',
|
||||||
|
$this->_conf->getSection('model_options')
|
||||||
|
)
|
||||||
|
);
|
||||||
|
$comment->setPaste($this->_model->getPaste(Helper::getPasteId()));
|
||||||
|
$this->assertEquals(Helper::getPasteId(), $comment->getParentId(), 'comment parent ID gets initialized to paste ID');
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @expectedException Exception
|
* @expectedException Exception
|
||||||
* @expectedExceptionCode 75
|
* @expectedExceptionCode 75
|
||||||
*/
|
*/
|
||||||
public function testPasteDuplicate()
|
public function testPasteDuplicate()
|
||||||
{
|
{
|
||||||
$pasteData = Helper::getPaste();
|
$pasteData = Helper::getPastePost();
|
||||||
|
|
||||||
$this->_model->getPaste(Helper::getPasteId())->delete();
|
$this->_model->getPaste(Helper::getPasteId())->delete();
|
||||||
$paste = $this->_model->getPaste();
|
$paste = $this->_model->getPaste();
|
||||||
$paste->setData($pasteData['data']);
|
$paste->setData($pasteData);
|
||||||
$paste->setOpendiscussion();
|
|
||||||
$paste->setFormatter($pasteData['meta']['formatter']);
|
|
||||||
$paste->store();
|
$paste->store();
|
||||||
|
|
||||||
$paste = $this->_model->getPaste();
|
$paste = $this->_model->getPaste();
|
||||||
$paste->setData($pasteData['data']);
|
$paste->setData($pasteData);
|
||||||
$paste->setOpendiscussion();
|
|
||||||
$paste->setFormatter($pasteData['meta']['formatter']);
|
|
||||||
$paste->store();
|
$paste->store();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -126,61 +139,42 @@ class ModelTest extends PHPUnit_Framework_TestCase
|
||||||
*/
|
*/
|
||||||
public function testCommentDuplicate()
|
public function testCommentDuplicate()
|
||||||
{
|
{
|
||||||
$pasteData = Helper::getPaste();
|
$pasteData = Helper::getPastePost();
|
||||||
$commentData = Helper::getComment();
|
$commentData = Helper::getCommentPost();
|
||||||
$this->_model->getPaste(Helper::getPasteId())->delete();
|
$this->_model->getPaste(Helper::getPasteId())->delete();
|
||||||
|
|
||||||
$paste = $this->_model->getPaste();
|
$paste = $this->_model->getPaste();
|
||||||
$paste->setData($pasteData['data']);
|
$paste->setData($pasteData);
|
||||||
$paste->setOpendiscussion();
|
|
||||||
$paste->setFormatter($pasteData['meta']['formatter']);
|
|
||||||
$paste->store();
|
$paste->store();
|
||||||
|
|
||||||
$comment = $paste->getComment(Helper::getPasteId());
|
$comment = $paste->getComment(Helper::getPasteId());
|
||||||
$comment->setData($commentData['data']);
|
$comment->setData($commentData);
|
||||||
$comment->setNickname($commentData['meta']['nickname']);
|
|
||||||
$comment->store();
|
$comment->store();
|
||||||
|
|
||||||
$comment = $paste->getComment(Helper::getPasteId());
|
$comment = $paste->getComment(Helper::getPasteId());
|
||||||
$comment->setData($commentData['data']);
|
$comment->setData($commentData);
|
||||||
$comment->setNickname($commentData['meta']['nickname']);
|
|
||||||
$comment->store();
|
$comment->store();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testImplicitDefaults()
|
public function testImplicitDefaults()
|
||||||
{
|
{
|
||||||
$pasteData = Helper::getPaste();
|
$pasteData = Helper::getPastePost();
|
||||||
$commentData = Helper::getComment();
|
$commentData = Helper::getCommentPost();
|
||||||
$this->_model->getPaste(Helper::getPasteId())->delete();
|
$this->_model->getPaste(Helper::getPasteId())->delete();
|
||||||
|
|
||||||
$paste = $this->_model->getPaste();
|
$paste = $this->_model->getPaste();
|
||||||
$paste->setData($pasteData['data']);
|
$paste->setData($pasteData);
|
||||||
$paste->setBurnafterreading();
|
|
||||||
$paste->setOpendiscussion();
|
|
||||||
// not setting a formatter, should use default one
|
|
||||||
$paste->store();
|
|
||||||
|
|
||||||
$paste = $this->_model->getPaste(Helper::getPasteId())->get(); // ID was set based on data
|
|
||||||
$this->assertEquals(true, property_exists($paste->meta, 'burnafterreading') && $paste->meta->burnafterreading, 'burn after reading takes precendence');
|
|
||||||
$this->assertEquals(false, property_exists($paste->meta, 'opendiscussion') && $paste->meta->opendiscussion, 'opendiscussion is disabled');
|
|
||||||
$this->assertEquals($this->_conf->getKey('defaultformatter'), $paste->meta->formatter, 'default formatter is set');
|
|
||||||
|
|
||||||
$this->_model->getPaste(Helper::getPasteId())->delete();
|
|
||||||
$paste = $this->_model->getPaste();
|
|
||||||
$paste->setData($pasteData['data']);
|
|
||||||
$paste->setBurnafterreading('0');
|
|
||||||
$paste->setOpendiscussion();
|
|
||||||
$paste->store();
|
$paste->store();
|
||||||
|
|
||||||
$comment = $paste->getComment(Helper::getPasteId());
|
$comment = $paste->getComment(Helper::getPasteId());
|
||||||
$comment->setData($commentData['data']);
|
$comment->setData($commentData);
|
||||||
$comment->setNickname($commentData['meta']['nickname']);
|
$comment->get();
|
||||||
$comment->store();
|
$comment->store();
|
||||||
|
|
||||||
$identicon = new Identicon();
|
$identicon = new Identicon();
|
||||||
$pngdata = $identicon->getImageDataUri(TrafficLimiter::getHash(), 16);
|
$pngdata = $identicon->getImageDataUri(TrafficLimiter::getHash(), 16);
|
||||||
$comment = $paste->getComment(Helper::getPasteId(), Helper::getCommentId())->get();
|
$comment = current($this->_model->getPaste(Helper::getPasteId())->get()['comments']);
|
||||||
$this->assertEquals($pngdata, $comment->meta->vizhash, 'nickname triggers vizhash to be set');
|
$this->assertEquals($pngdata, $comment['meta']['icon'], 'icon gets set');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testPasteIdValidation()
|
public function testPasteIdValidation()
|
||||||
|
@ -203,12 +197,11 @@ class ModelTest extends PHPUnit_Framework_TestCase
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @expectedException Exception
|
* @expectedException Exception
|
||||||
* @expectedExceptionCode 61
|
* @expectedExceptionCode 60
|
||||||
*/
|
*/
|
||||||
public function testInvalidData()
|
public function testInvalidPasteId()
|
||||||
{
|
{
|
||||||
$paste = $this->_model->getPaste();
|
$this->_model->getPaste('');
|
||||||
$paste->setData('');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -227,9 +220,9 @@ class ModelTest extends PHPUnit_Framework_TestCase
|
||||||
*/
|
*/
|
||||||
public function testInvalidCommentDeletedPaste()
|
public function testInvalidCommentDeletedPaste()
|
||||||
{
|
{
|
||||||
$pasteData = Helper::getPaste();
|
$pasteData = Helper::getPastePost();
|
||||||
$paste = $this->_model->getPaste(Helper::getPasteId());
|
$paste = $this->_model->getPaste(Helper::getPasteId());
|
||||||
$paste->setData($pasteData['data']);
|
$paste->setData($pasteData);
|
||||||
$paste->store();
|
$paste->store();
|
||||||
|
|
||||||
$comment = $paste->getComment(Helper::getPasteId());
|
$comment = $paste->getComment(Helper::getPasteId());
|
||||||
|
@ -243,29 +236,40 @@ class ModelTest extends PHPUnit_Framework_TestCase
|
||||||
*/
|
*/
|
||||||
public function testInvalidCommentData()
|
public function testInvalidCommentData()
|
||||||
{
|
{
|
||||||
$pasteData = Helper::getPaste();
|
$pasteData = Helper::getPastePost();
|
||||||
$paste = $this->_model->getPaste(Helper::getPasteId());
|
$pasteData['adata'][2] = 0;
|
||||||
$paste->setData($pasteData['data']);
|
$paste = $this->_model->getPaste(Helper::getPasteId());
|
||||||
|
$paste->setData($pasteData);
|
||||||
$paste->store();
|
$paste->store();
|
||||||
|
|
||||||
$comment = $paste->getComment(Helper::getPasteId());
|
$comment = $paste->getComment(Helper::getPasteId());
|
||||||
$comment->store();
|
$comment->store();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @expectedException Exception
|
||||||
|
* @expectedExceptionCode 65
|
||||||
|
*/
|
||||||
|
public function testInvalidCommentParent()
|
||||||
|
{
|
||||||
|
$paste = $this->_model->getPaste(Helper::getPasteId());
|
||||||
|
$comment = $paste->getComment('');
|
||||||
|
$comment->store();
|
||||||
|
}
|
||||||
|
|
||||||
public function testExpiration()
|
public function testExpiration()
|
||||||
{
|
{
|
||||||
$pasteData = Helper::getPaste();
|
$pasteData = Helper::getPastePost();
|
||||||
$this->_model->getPaste(Helper::getPasteId())->delete();
|
$this->_model->getPaste(Helper::getPasteId())->delete();
|
||||||
$paste = $this->_model->getPaste(Helper::getPasteId());
|
$paste = $this->_model->getPaste(Helper::getPasteId());
|
||||||
$this->assertFalse($paste->exists(), 'paste does not yet exist');
|
$this->assertFalse($paste->exists(), 'paste does not yet exist');
|
||||||
|
|
||||||
$paste = $this->_model->getPaste();
|
$paste = $this->_model->getPaste();
|
||||||
$paste->setData($pasteData['data']);
|
$paste->setData($pasteData);
|
||||||
$paste->setExpiration('5min'); // = 300 seconds
|
|
||||||
$paste->store();
|
$paste->store();
|
||||||
|
|
||||||
$paste = $paste->get();
|
$paste = $paste->get();
|
||||||
$this->assertEquals(300, $paste->meta->remaining_time, 'remaining time is set correctly');
|
$this->assertEquals((float) 300, (float) $paste['meta']['time_to_live'], 'remaining time is set correctly', 1.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -274,11 +278,11 @@ class ModelTest extends PHPUnit_Framework_TestCase
|
||||||
*/
|
*/
|
||||||
public function testCommentDeletion()
|
public function testCommentDeletion()
|
||||||
{
|
{
|
||||||
$pasteData = Helper::getPaste();
|
$pasteData = Helper::getPastePost();
|
||||||
$this->_model->getPaste(Helper::getPasteId())->delete();
|
$this->_model->getPaste(Helper::getPasteId())->delete();
|
||||||
|
|
||||||
$paste = $this->_model->getPaste();
|
$paste = $this->_model->getPaste();
|
||||||
$paste->setData($pasteData['data']);
|
$paste->setData($pasteData);
|
||||||
$paste->store();
|
$paste->store();
|
||||||
$paste->getComment(Helper::getPasteId())->delete();
|
$paste->getComment(Helper::getPasteId())->delete();
|
||||||
}
|
}
|
||||||
|
@ -288,12 +292,12 @@ class ModelTest extends PHPUnit_Framework_TestCase
|
||||||
$conf = new Configuration;
|
$conf = new Configuration;
|
||||||
$store = Database::getInstance($conf->getSection('model_options'));
|
$store = Database::getInstance($conf->getSection('model_options'));
|
||||||
$store->delete(Helper::getPasteId());
|
$store->delete(Helper::getPasteId());
|
||||||
$expired = Helper::getPaste(array('expire_date' => 1344803344));
|
$expired = Helper::getPaste(2, array('expire_date' => 1344803344));
|
||||||
$paste = Helper::getPaste(array('expire_date' => time() + 3600));
|
$paste = Helper::getPaste(2, array('expire_date' => time() + 3600));
|
||||||
$keys = array('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'x', 'y', 'z');
|
$keys = array('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'x', 'y', 'z');
|
||||||
$ids = array();
|
$ids = array();
|
||||||
foreach ($keys as $key) {
|
foreach ($keys as $key) {
|
||||||
$ids[$key] = substr(md5($key), 0, 16);
|
$ids[$key] = hash('fnv164', $key);
|
||||||
$store->delete($ids[$key]);
|
$store->delete($ids[$key]);
|
||||||
$this->assertFalse($store->exists($ids[$key]), "paste $key does not yet exist");
|
$this->assertFalse($store->exists($ids[$key]), "paste $key does not yet exist");
|
||||||
if (in_array($key, array('x', 'y', 'z'))) {
|
if (in_array($key, array('x', 'y', 'z'))) {
|
||||||
|
@ -330,79 +334,34 @@ class ModelTest extends PHPUnit_Framework_TestCase
|
||||||
Helper::createIniFile(CONF, $options);
|
Helper::createIniFile(CONF, $options);
|
||||||
$model = new Model(new Configuration);
|
$model = new Model(new Configuration);
|
||||||
|
|
||||||
$pasteData = Helper::getPaste();
|
$pasteData = Helper::getPastePost();
|
||||||
$this->_model->getPaste(Helper::getPasteId())->delete();
|
$this->_model->getPaste(Helper::getPasteId())->delete();
|
||||||
$paste = $model->getPaste(Helper::getPasteId());
|
$paste = $model->getPaste(Helper::getPasteId());
|
||||||
$this->assertFalse($paste->exists(), 'paste does not yet exist');
|
$this->assertFalse($paste->exists(), 'paste does not yet exist');
|
||||||
|
|
||||||
$paste = $model->getPaste();
|
$paste = $model->getPaste();
|
||||||
$paste->setData($pasteData['data']);
|
$paste->setData($pasteData);
|
||||||
$paste->setOpendiscussion();
|
|
||||||
$paste->setFormatter($pasteData['meta']['formatter']);
|
|
||||||
$paste->store();
|
$paste->store();
|
||||||
|
|
||||||
$paste = $model->getPaste(Helper::getPasteId());
|
$paste = $model->getPaste(Helper::getPasteId());
|
||||||
$this->assertTrue($paste->exists(), 'paste exists after storing it');
|
$this->assertTrue($paste->exists(), 'paste exists after storing it');
|
||||||
$paste = $paste->get();
|
|
||||||
$this->assertEquals($pasteData['data'], $paste->data);
|
|
||||||
foreach (array('opendiscussion', 'formatter') as $key) {
|
|
||||||
$this->assertEquals($pasteData['meta'][$key], $paste->meta->$key);
|
|
||||||
}
|
|
||||||
|
|
||||||
// storing comments
|
// storing comments
|
||||||
$commentData = Helper::getComment();
|
$commentData = Helper::getCommentPost();
|
||||||
|
unset($commentData['meta']['icon']);
|
||||||
$paste = $model->getPaste(Helper::getPasteId());
|
$paste = $model->getPaste(Helper::getPasteId());
|
||||||
$comment = $paste->getComment(Helper::getPasteId(), Helper::getCommentId());
|
$comment = $paste->getComment(Helper::getPasteId(), Helper::getPasteId());
|
||||||
$this->assertFalse($comment->exists(), 'comment does not yet exist');
|
$this->assertFalse($comment->exists(), 'comment does not yet exist');
|
||||||
|
|
||||||
$comment = $paste->getComment(Helper::getPasteId());
|
$comment = $paste->getComment(Helper::getPasteId());
|
||||||
$comment->setData($commentData['data']);
|
$comment->setData($commentData);
|
||||||
$comment->setNickname($commentData['meta']['nickname']);
|
|
||||||
$comment->store();
|
$comment->store();
|
||||||
|
|
||||||
$comment = $paste->getComment(Helper::getPasteId(), Helper::getCommentId());
|
$comment = $paste->getComment(Helper::getPasteId(), Helper::getPasteId());
|
||||||
$this->assertTrue($comment->exists(), 'comment exists after storing it');
|
$this->assertTrue($comment->exists(), 'comment exists after storing it');
|
||||||
$comment = $comment->get();
|
|
||||||
$this->assertEquals($commentData['data'], $comment->data);
|
|
||||||
$this->assertEquals($commentData['meta']['nickname'], $comment->meta->nickname);
|
|
||||||
$this->assertFalse(property_exists($comment->meta, 'vizhash'), 'vizhash was not generated');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testCommentIdenticon()
|
$comment = current($this->_model->getPaste(Helper::getPasteId())->get()['comments']);
|
||||||
{
|
$this->assertFalse(array_key_exists('icon', $comment['meta']), 'icon was not generated');
|
||||||
$options = parse_ini_file(CONF, true);
|
|
||||||
$options['main']['icon'] = 'identicon';
|
|
||||||
$options['model'] = array(
|
|
||||||
'class' => 'Database',
|
|
||||||
);
|
|
||||||
$options['model_options'] = array(
|
|
||||||
'dsn' => 'sqlite::memory:',
|
|
||||||
'usr' => null,
|
|
||||||
'pwd' => null,
|
|
||||||
'opt' => array(PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION),
|
|
||||||
);
|
|
||||||
Helper::createIniFile(CONF, $options);
|
|
||||||
$model = new Model(new Configuration);
|
|
||||||
|
|
||||||
$pasteData = Helper::getPaste();
|
|
||||||
$commentData = Helper::getComment();
|
|
||||||
$model->getPaste(Helper::getPasteId())->delete();
|
|
||||||
|
|
||||||
$paste = $model->getPaste();
|
|
||||||
$paste->setData($pasteData['data']);
|
|
||||||
$paste->setOpendiscussion();
|
|
||||||
$paste->setFormatter($pasteData['meta']['formatter']);
|
|
||||||
$paste->store();
|
|
||||||
|
|
||||||
$comment = $paste->getComment(Helper::getPasteId());
|
|
||||||
$comment->setData($commentData['data']);
|
|
||||||
$comment->setNickname($commentData['meta']['nickname']);
|
|
||||||
$comment->store();
|
|
||||||
|
|
||||||
$identicon = new Identicon();
|
|
||||||
$pngdata = $identicon->getImageDataUri(TrafficLimiter::getHash(), 16);
|
|
||||||
$comment = $paste->getComment(Helper::getPasteId(), Helper::getCommentId())->get();
|
|
||||||
$this->assertEquals($pngdata, $comment->meta->vizhash, 'nickname triggers vizhash to be set');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testCommentVizhash()
|
public function testCommentVizhash()
|
||||||
|
@ -421,24 +380,21 @@ class ModelTest extends PHPUnit_Framework_TestCase
|
||||||
Helper::createIniFile(CONF, $options);
|
Helper::createIniFile(CONF, $options);
|
||||||
$model = new Model(new Configuration);
|
$model = new Model(new Configuration);
|
||||||
|
|
||||||
$pasteData = Helper::getPaste();
|
$pasteData = Helper::getPastePost();
|
||||||
$commentData = Helper::getComment();
|
$commentData = Helper::getCommentPost();
|
||||||
$model->getPaste(Helper::getPasteId())->delete();
|
$model->getPaste(Helper::getPasteId())->delete();
|
||||||
|
|
||||||
$paste = $model->getPaste();
|
$paste = $model->getPaste();
|
||||||
$paste->setData($pasteData['data']);
|
$paste->setData($pasteData);
|
||||||
$paste->setOpendiscussion();
|
|
||||||
$paste->setFormatter($pasteData['meta']['formatter']);
|
|
||||||
$paste->store();
|
$paste->store();
|
||||||
|
|
||||||
$comment = $paste->getComment(Helper::getPasteId());
|
$comment = $paste->getComment(Helper::getPasteId());
|
||||||
$comment->setData($commentData['data']);
|
$comment->setData($commentData);
|
||||||
$comment->setNickname($commentData['meta']['nickname']);
|
|
||||||
$comment->store();
|
$comment->store();
|
||||||
|
|
||||||
$vz = new Vizhash16x16();
|
$vz = new Vizhash16x16();
|
||||||
$pngdata = 'data:image/png;base64,' . base64_encode($vz->generate(TrafficLimiter::getHash()));
|
$pngdata = 'data:image/png;base64,' . base64_encode($vz->generate(TrafficLimiter::getHash()));
|
||||||
$comment = $paste->getComment(Helper::getPasteId(), Helper::getCommentId())->get();
|
$comment = current($this->_model->getPaste(Helper::getPasteId())->get()['comments']);
|
||||||
$this->assertEquals($pngdata, $comment->meta->vizhash, 'nickname triggers vizhash to be set');
|
$this->assertEquals($pngdata, $comment['meta']['icon'], 'nickname triggers vizhash to be set');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,39 @@
|
||||||
|
Running all unit tests
|
||||||
|
======================
|
||||||
|
|
||||||
|
Since it is non-trivial to setup all dependencies for our unit testing suite,
|
||||||
|
we provide a docker image that bundles all of them into one container, both
|
||||||
|
phpunit for PHP and mocha for JS.
|
||||||
|
|
||||||
|
You can fetch and run the image from the docker hub like this:
|
||||||
|
|
||||||
|
```console
|
||||||
|
docker run --rm --read-only -v ~/PrivateBin:/srv:ro privatebin/unit-testing
|
||||||
|
```
|
||||||
|
|
||||||
|
The parameters in detail:
|
||||||
|
|
||||||
|
- `-v ~/PrivateBin:/srv:ro` - Replace `~/PrivateBin` with the location of
|
||||||
|
the checked out PrivateBin repository on your machine. It is recommended to
|
||||||
|
mount it read-only, which guarantees that your repository isn't damaged by
|
||||||
|
an accidentally destructive test case in it.
|
||||||
|
- `--read-only` - This image supports running in read-only mode. Only /tmp
|
||||||
|
may be written into.
|
||||||
|
- `-rm` - Remove the container after the run. This saves you doing a cleanup
|
||||||
|
on your docker environment, if you run the image frequently.
|
||||||
|
|
||||||
|
You can also run just the php and javascript test suites instead of both:
|
||||||
|
|
||||||
|
```console
|
||||||
|
docker run --rm --read-only -v ~/PrivateBin:/srv:ro privatebin/unit-testing phpunit
|
||||||
|
docker run --rm --read-only -v ~/PrivateBin:/srv:ro privatebin/unit-testing mocha
|
||||||
|
```
|
||||||
|
|
||||||
|
We also provide a Janitor image that includes the Cloud9 and Theia WebIDEs as
|
||||||
|
well as the integrated unit testing utilities. See our [docker wiki
|
||||||
|
page](https://github.com/PrivateBin/PrivateBin/wiki/Docker#janitor-image-with-cloud9-and-theia-webide-janitortechnologyprivatebin)
|
||||||
|
for further details on this.
|
||||||
|
|
||||||
Running PHP unit tests
|
Running PHP unit tests
|
||||||
======================
|
======================
|
||||||
|
|
||||||
|
@ -9,11 +45,13 @@ and their dependencies:
|
||||||
* php-xdebug (for code coverage reports)
|
* php-xdebug (for code coverage reports)
|
||||||
|
|
||||||
Example for Debian and Ubuntu:
|
Example for Debian and Ubuntu:
|
||||||
|
|
||||||
```console
|
```console
|
||||||
$ sudo apt install phpunit php-gd php-sqlite3 php-xdebug
|
$ sudo apt install phpunit php-gd php-sqlite3 php-xdebug
|
||||||
```
|
```
|
||||||
|
|
||||||
To run the tests, change into the `tst` directory and run phpunit:
|
To run the tests, change into the `tst` directory and run phpunit:
|
||||||
|
|
||||||
```console
|
```console
|
||||||
$ cd PrivateBin/tst
|
$ cd PrivateBin/tst
|
||||||
$ phpunit
|
$ phpunit
|
||||||
|
@ -45,13 +83,13 @@ and its dependencies:
|
||||||
* npm
|
* npm
|
||||||
|
|
||||||
Then you can use the node package manager to install the latest stable release
|
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
|
of mocha and nyc (for code coverage reports) globally and jsVerify, jsdom
|
||||||
and jsdom-global locally:
|
and jsdom-global locally:
|
||||||
|
|
||||||
```console
|
```console
|
||||||
$ npm install -g mocha istanbul
|
$ npm install -g mocha nyc
|
||||||
$ cd PrivateBin/js
|
$ cd PrivateBin/js
|
||||||
$ npm install jsverify jsdom@9 jsdom-global@2 mime-types
|
$ npm install jsverify jsdom@9 jsdom-global@2 mime-types node-webcrypto-ossl
|
||||||
```
|
```
|
||||||
|
|
||||||
Example for Debian and Ubuntu, including steps to allow the current user to
|
Example for Debian and Ubuntu, including steps to allow the current user to
|
||||||
|
@ -61,18 +99,16 @@ $ sudo apt install npm
|
||||||
$ sudo mkdir /usr/local/lib/node_modules
|
$ sudo mkdir /usr/local/lib/node_modules
|
||||||
$ sudo chown -R $(whoami) $(npm config get prefix)/{lib/node_modules,bin,share}
|
$ sudo chown -R $(whoami) $(npm config get prefix)/{lib/node_modules,bin,share}
|
||||||
$ ln -s /usr/bin/nodejs /usr/local/bin/node
|
$ ln -s /usr/bin/nodejs /usr/local/bin/node
|
||||||
$ npm install -g mocha istanbul
|
$ npm install -g mocha nyc
|
||||||
$ cd PrivateBin/js
|
$ cd PrivateBin/js
|
||||||
$ npm install jsverify jsdom@9 jsdom-global@2 mime-types
|
$ npm install jsverify jsdom@9 jsdom-global@2 mime-types node-webcrypto-ossl
|
||||||
```
|
```
|
||||||
|
|
||||||
Note: If you use a distribution that provides nodeJS >= 6, then you can install
|
|
||||||
the latest jsdom and jsdom-global packages and don't need to use @9 and @2.
|
|
||||||
|
|
||||||
To run the tests, just change into the `js` directory and run istanbul:
|
To run the tests, just change into the `js` directory and run istanbul:
|
||||||
|
|
||||||
```console
|
```console
|
||||||
$ cd PrivateBin/js
|
$ cd PrivateBin/js
|
||||||
$ istanbul cover _mocha
|
$ nyc mocha
|
||||||
```
|
```
|
||||||
|
|
||||||
Property based unit testing
|
Property based unit testing
|
||||||
|
@ -115,6 +151,6 @@ After you adjusted the code of the library or the test you can rerun the test
|
||||||
with the same RNG state as follows:
|
with the same RNG state as follows:
|
||||||
|
|
||||||
```console
|
```console
|
||||||
$ istanbul cover _mocha -- test.js --jsverifyRngState 88caf85079d32e416b
|
$ nyc mocha test --jsverifyRngState 88caf85079d32e416b
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
@ -93,13 +93,13 @@ class RequestTest extends PHPUnit_Framework_TestCase
|
||||||
$_SERVER['REQUEST_METHOD'] = 'PUT';
|
$_SERVER['REQUEST_METHOD'] = 'PUT';
|
||||||
$_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
|
$_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
|
||||||
$file = tempnam(sys_get_temp_dir(), 'FOO');
|
$file = tempnam(sys_get_temp_dir(), 'FOO');
|
||||||
file_put_contents($file, 'data=foo');
|
file_put_contents($file, '{"ct":"foo"}');
|
||||||
Request::setInputStream($file);
|
Request::setInputStream($file);
|
||||||
$request = new Request;
|
$request = new Request;
|
||||||
unlink($file);
|
unlink($file);
|
||||||
$this->assertTrue($request->isJsonApiCall(), 'is JSON Api call');
|
$this->assertTrue($request->isJsonApiCall(), 'is JSON Api call');
|
||||||
$this->assertEquals('create', $request->getOperation());
|
$this->assertEquals('create', $request->getOperation());
|
||||||
$this->assertEquals('foo', $request->getParam('data'));
|
$this->assertEquals('foo', $request->getParam('ct'));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testApiCreateAlternative()
|
public function testApiCreateAlternative()
|
||||||
|
@ -107,11 +107,13 @@ class RequestTest extends PHPUnit_Framework_TestCase
|
||||||
$this->reset();
|
$this->reset();
|
||||||
$_SERVER['REQUEST_METHOD'] = 'POST';
|
$_SERVER['REQUEST_METHOD'] = 'POST';
|
||||||
$_SERVER['HTTP_ACCEPT'] = 'application/json, text/javascript, */*; q=0.01';
|
$_SERVER['HTTP_ACCEPT'] = 'application/json, text/javascript, */*; q=0.01';
|
||||||
$_POST['attachment'] = 'foo';
|
$file = tempnam(sys_get_temp_dir(), 'FOO');
|
||||||
$request = new Request;
|
file_put_contents($file, '{"ct":"foo"}');
|
||||||
|
Request::setInputStream($file);
|
||||||
|
$request = new Request;
|
||||||
$this->assertTrue($request->isJsonApiCall(), 'is JSON Api call');
|
$this->assertTrue($request->isJsonApiCall(), 'is JSON Api call');
|
||||||
$this->assertEquals('create', $request->getOperation());
|
$this->assertEquals('create', $request->getOperation());
|
||||||
$this->assertEquals('foo', $request->getParam('attachment'));
|
$this->assertEquals('foo', $request->getParam('ct'));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testApiRead()
|
public function testApiRead()
|
||||||
|
@ -136,8 +138,10 @@ class RequestTest extends PHPUnit_Framework_TestCase
|
||||||
$_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
|
$_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
|
||||||
$_SERVER['QUERY_STRING'] = $id;
|
$_SERVER['QUERY_STRING'] = $id;
|
||||||
$_GET = array($id => '');
|
$_GET = array($id => '');
|
||||||
$_POST['deletetoken'] = 'bar';
|
$file = tempnam(sys_get_temp_dir(), 'FOO');
|
||||||
$request = new Request;
|
file_put_contents($file, '{"deletetoken":"bar"}');
|
||||||
|
Request::setInputStream($file);
|
||||||
|
$request = new Request;
|
||||||
$this->assertTrue($request->isJsonApiCall(), 'is JSON Api call');
|
$this->assertTrue($request->isJsonApiCall(), 'is JSON Api call');
|
||||||
$this->assertEquals('delete', $request->getOperation());
|
$this->assertEquals('delete', $request->getOperation());
|
||||||
$this->assertEquals($id, $request->getParam('pasteid'));
|
$this->assertEquals($id, $request->getParam('pasteid'));
|
||||||
|
|
|
@ -1,33 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
use PrivateBin\Persistence\ServerSalt;
|
|
||||||
use PrivateBin\Sjcl;
|
|
||||||
|
|
||||||
class SjclTest extends PHPUnit_Framework_TestCase
|
|
||||||
{
|
|
||||||
public function testSjclValidatorValidatesCorrectly()
|
|
||||||
{
|
|
||||||
ServerSalt::setPath(sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'privatebin_data');
|
|
||||||
$paste = Helper::getPasteWithAttachment();
|
|
||||||
$this->assertTrue(Sjcl::isValid($paste['data']), 'valid sjcl');
|
|
||||||
$this->assertTrue(Sjcl::isValid($paste['attachment']), 'valid sjcl');
|
|
||||||
$this->assertTrue(Sjcl::isValid($paste['attachmentname']), 'valid sjcl');
|
|
||||||
$this->assertTrue(Sjcl::isValid(Helper::getComment()['data']), 'valid sjcl');
|
|
||||||
|
|
||||||
$this->assertTrue(Sjcl::isValid('{"iv":"83Ax/OdUav3SanDW9dcQPg","v":1,"iter":1000,"ks":128,"ts":64,"mode":"ccm","adata":"","cipher":"aes","salt":"Gx1vA2/gQ3U","ct":"j7ImByuE5xCqD2YXm6aSyA"}'), 'valid sjcl');
|
|
||||||
$this->assertFalse(Sjcl::isValid('{"iv":"$","v":1,"iter":1000,"ks":128,"ts":64,"mode":"ccm","adata":"","cipher":"aes","salt":"Gx1vA2/gQ3U","ct":"j7ImByuE5xCqD2YXm6aSyA"}'), 'invalid base64 encoding of iv');
|
|
||||||
$this->assertFalse(Sjcl::isValid('{"iv":"83Ax/OdUav3SanDW9dcQPg","v":1,"iter":1000,"ks":128,"ts":64,"mode":"ccm","adata":"","cipher":"aes","salt":"$","ct":"j7ImByuE5xCqD2YXm6aSyA"}'), 'invalid base64 encoding of salt');
|
|
||||||
$this->assertFalse(Sjcl::isValid('{"iv":"83Ax/OdUav3SanDW9dcQPg","v":1,"iter":1000,"ks":128,"ts":64,"mode":"ccm","adata":"","cipher":"aes","salt":"Gx1vA2/gQ3U","ct":"$"}'), 'invalid base64 encoding of ct');
|
|
||||||
$this->assertFalse(Sjcl::isValid('{"iv":"83Ax/OdUav3SanDW9dcQPg","v":1,"iter":1000,"ks":128,"ts":64,"mode":"ccm","adata":"","cipher":"aes","salt":"Gx1vA2/gQ3U","ct":"bm9kYXRhbm9kYXRhbm9kYXRhbm9kYXRhbm9kYXRhCg=="}'), 'low ct entropy');
|
|
||||||
$this->assertFalse(Sjcl::isValid('{"iv":"MTIzNDU2Nzg5MDEyMzQ1Njc4OTA=","v":1,"iter":1000,"ks":128,"ts":64,"mode":"ccm","adata":"","cipher":"aes","salt":"Gx1vA2/gQ3U","ct":"j7ImByuE5xCqD2YXm6aSyA"}'), 'iv to long');
|
|
||||||
$this->assertFalse(Sjcl::isValid('{"iv":"83Ax/OdUav3SanDW9dcQPg","v":1,"iter":1000,"ks":128,"ts":64,"mode":"ccm","adata":"","cipher":"aes","salt":"MTIzNDU2Nzg5MDEyMzQ1Njc4OTA=","ct":"j7ImByuE5xCqD2YXm6aSyA"}'), 'salt to long');
|
|
||||||
$this->assertFalse(Sjcl::isValid('{"iv":"83Ax/OdUav3SanDW9dcQPg","v":1,"iter":1000,"ks":128,"ts":64,"mode":"ccm","adata":"","cipher":"aes","salt":"Gx1vA2/gQ3U","ct":"j7ImByuE5xCqD2YXm6aSyA","foo":"MTIzNDU2Nzg5MDEyMzQ1Njc4OTA="}'), 'invalid additional key');
|
|
||||||
$this->assertFalse(Sjcl::isValid('{"iv":"83Ax/OdUav3SanDW9dcQPg","v":0.9,"iter":1000,"ks":128,"ts":64,"mode":"ccm","adata":"","cipher":"aes","salt":"Gx1vA2/gQ3U","ct":"j7ImByuE5xCqD2YXm6aSyA"}'), 'unsupported version');
|
|
||||||
$this->assertFalse(Sjcl::isValid('{"iv":"83Ax/OdUav3SanDW9dcQPg","v":1,"iter":100,"ks":128,"ts":64,"mode":"ccm","adata":"","cipher":"aes","salt":"Gx1vA2/gQ3U","ct":"j7ImByuE5xCqD2YXm6aSyA"}'), 'not enough iterations');
|
|
||||||
$this->assertFalse(Sjcl::isValid('{"iv":"83Ax/OdUav3SanDW9dcQPg","v":1,"iter":1000,"ks":127,"ts":64,"mode":"ccm","adata":"","cipher":"aes","salt":"Gx1vA2/gQ3U","ct":"j7ImByuE5xCqD2YXm6aSyA"}'), 'invalid key size');
|
|
||||||
$this->assertFalse(Sjcl::isValid('{"iv":"83Ax/OdUav3SanDW9dcQPg","v":1,"iter":1000,"ks":128,"ts":63,"mode":"ccm","adata":"","cipher":"aes","salt":"Gx1vA2/gQ3U","ct":"j7ImByuE5xCqD2YXm6aSyA"}'), 'invalid tag length');
|
|
||||||
$this->assertFalse(Sjcl::isValid('{"iv":"83Ax/OdUav3SanDW9dcQPg","v":1,"iter":1000,"ks":128,"ts":64,"mode":"!#@","adata":"","cipher":"aes","salt":"Gx1vA2/gQ3U","ct":"j7ImByuE5xCqD2YXm6aSyA"}'), 'invalid mode');
|
|
||||||
$this->assertFalse(Sjcl::isValid('{"iv":"83Ax/OdUav3SanDW9dcQPg","v":1,"iter":1000,"ks":128,"ts":64,"mode":"ccm","adata":"","cipher":"!#@","salt":"Gx1vA2/gQ3U","ct":"j7ImByuE5xCqD2YXm6aSyA"}'), 'invalid cipher');
|
|
||||||
// @note adata is not validated, except as part of the total message length
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -29,7 +29,7 @@ return array(
|
||||||
'PrivateBin\\Persistence\\ServerSalt' => $baseDir . '/lib/Persistence/ServerSalt.php',
|
'PrivateBin\\Persistence\\ServerSalt' => $baseDir . '/lib/Persistence/ServerSalt.php',
|
||||||
'PrivateBin\\Persistence\\TrafficLimiter' => $baseDir . '/lib/Persistence/TrafficLimiter.php',
|
'PrivateBin\\Persistence\\TrafficLimiter' => $baseDir . '/lib/Persistence/TrafficLimiter.php',
|
||||||
'PrivateBin\\Request' => $baseDir . '/lib/Request.php',
|
'PrivateBin\\Request' => $baseDir . '/lib/Request.php',
|
||||||
'PrivateBin\\Sjcl' => $baseDir . '/lib/Sjcl.php',
|
'PrivateBin\\FormatV2' => $baseDir . '/lib/FormatV2.php',
|
||||||
'PrivateBin\\View' => $baseDir . '/lib/View.php',
|
'PrivateBin\\View' => $baseDir . '/lib/View.php',
|
||||||
'PrivateBin\\Vizhash16x16' => $baseDir . '/lib/Vizhash16x16.php',
|
'PrivateBin\\Vizhash16x16' => $baseDir . '/lib/Vizhash16x16.php',
|
||||||
);
|
);
|
||||||
|
|
|
@ -58,7 +58,7 @@ class ComposerStaticInitDontChange
|
||||||
'PrivateBin\\Persistence\\ServerSalt' => __DIR__ . '/../..' . '/lib/Persistence/ServerSalt.php',
|
'PrivateBin\\Persistence\\ServerSalt' => __DIR__ . '/../..' . '/lib/Persistence/ServerSalt.php',
|
||||||
'PrivateBin\\Persistence\\TrafficLimiter' => __DIR__ . '/../..' . '/lib/Persistence/TrafficLimiter.php',
|
'PrivateBin\\Persistence\\TrafficLimiter' => __DIR__ . '/../..' . '/lib/Persistence/TrafficLimiter.php',
|
||||||
'PrivateBin\\Request' => __DIR__ . '/../..' . '/lib/Request.php',
|
'PrivateBin\\Request' => __DIR__ . '/../..' . '/lib/Request.php',
|
||||||
'PrivateBin\\Sjcl' => __DIR__ . '/../..' . '/lib/Sjcl.php',
|
'PrivateBin\\FormatV2' => __DIR__ . '/../..' . '/lib/FormatV2.php',
|
||||||
'PrivateBin\\View' => __DIR__ . '/../..' . '/lib/View.php',
|
'PrivateBin\\View' => __DIR__ . '/../..' . '/lib/View.php',
|
||||||
'PrivateBin\\Vizhash16x16' => __DIR__ . '/../..' . '/lib/Vizhash16x16.php',
|
'PrivateBin\\Vizhash16x16' => __DIR__ . '/../..' . '/lib/Vizhash16x16.php',
|
||||||
);
|
);
|
||||||
|
|
Loading…
Reference in New Issue