Merge branch 'master' into js-unit-testing

This commit is contained in:
El RIDO 2017-03-25 09:06:04 +01:00
commit 57ebc7338d
No known key found for this signature in database
GPG Key ID: 0F5C940A6BD81F92
58 changed files with 4361 additions and 2001 deletions

View File

@ -15,7 +15,9 @@ globals:
# http://eslint.org/docs/rules/
rules:
# Possible Errors
comma-dangle: [2, never]
comma-dangle:
- error
- never
no-cond-assign: 2
no-console: 0
no-constant-condition: 2
@ -31,7 +33,9 @@ rules:
no-extra-parens: 0
no-extra-semi: 2
no-func-assign: 2
no-inner-declarations: [2, functions]
no-inner-declarations:
- error
- functions
no-invalid-regexp: 2
no-irregular-whitespace: 2
no-negated-in-lhs: 2
@ -47,7 +51,9 @@ rules:
# Best Practices
accessor-pairs: 2
block-scoped-var: 0
complexity: [2, 6]
complexity:
- error
- 20
consistent-return: 0
curly: 0
default-case: 0
@ -99,7 +105,7 @@ rules:
no-with: 2
radix: 2
vars-on-top: 0
wrap-iife: 2
wrap-iife: 0
yoda: 0
# Strict
@ -152,7 +158,9 @@ rules:
max-len: 0
max-nested-callbacks: 0
max-params: 0
max-statements: [2, 30]
max-statements:
- error
- 60
new-cap: 0
new-parens: 0
newline-after-var: 0

2
.gitignore vendored
View File

@ -1,6 +1,7 @@
# Ignore server files for safety
.htaccess
.htpasswd
cfg/conf.ini
# Ignore data/
data/
@ -30,6 +31,7 @@ vendor/**/build_phar.php
# Ignore local node modules, unit testing logs, api docs and eclipse project files
js/node_modules/
tst/log/
tst/ConfigurationCombinationsTest.php
.settings
.buildpath
.project

View File

@ -7,6 +7,7 @@ php:
- '7.0'
- '7.1'
# as this is a php project, node.js v4 (for JS unit testing) isn't installed
install:
- rm -rf ~/.nvm && git clone https://github.com/creationix/nvm.git ~/.nvm && (cd ~/.nvm && git checkout `git describe --abbrev=0 --tags`) && source ~/.nvm/nvm.sh && nvm install 4
@ -18,7 +19,7 @@ before_script:
- cd ..
script:
- cd tst && phpunit
- cd tst && ../vendor/bin/phpunit
- cd ../js && mocha
after_script:

View File

@ -1,8 +1,10 @@
# PrivateBin version history
* **next (not yet released)**
* ADDED: Translations for Spanish, Occitan and Norwegian
* ADDED: Translations for Spanish, Occitan, Norwegian and Portuguese
* ADDED: Option in configuration to change the default "PrivateBin" title of the site
* CHANGED: Minimum required PHP version is 5.4 (#186)
* CHANGED: Shipped .htaccess files were updated for Apache 2.4 (#192)
* CHANGED: Cleanup of bootstrap template variants and moved icons to `img` directory
* **1.1 (2016-12-26)**
* ADDED: Translations for Italian and Russian

View File

@ -35,3 +35,4 @@ Sébastien Sauvage - original idea and main developer
* Alfredo Fabián Altamirano Tena - Spanish
* Quent-in - Occitan
* idarlund - Norwegian
* Tulio Leao - Portuguese

View File

@ -10,7 +10,7 @@ check the options and adjust them as you see fit.
### Requirements
- PHP version 5.3 or above
- PHP version 5.4 or above
- _one_ of the following sources of cryptographically safe randomness is required:
- PHP 7 or higher
- [Libsodium](https://download.libsodium.org/libsodium/content/installation/) and it's [PHP extension](https://paragonie.com/book/pecl-libsodium/read/00-intro.md#installing-libsodium)

View File

@ -16,10 +16,10 @@ Data is encrypted/decrypted in the browser using 256bit AES in [Galois Counter m
This is a fork of ZeroBin, originally developed by
[Sébastien Sauvage](https://github.com/sebsauvage/ZeroBin). It was refactored
to allow easier and cleaner extensions and has now much more features than the
to allow easier and cleaner extensions and has now many more features than the
original. It is however still fully compatible to the original ZeroBin 0.19
data storage scheme. Therefore such installations can be upgraded to this fork
without loosing any data.
without losing any data.
## What PrivateBin provides

1
cfg/.gitignore vendored
View File

@ -1 +0,0 @@
/conf.ini

View File

@ -1,2 +1 @@
Allow from none
Deny from all
Require all denied

View File

@ -18,13 +18,14 @@
}
],
"require": {
"php": "^5.3.0 || ^7.0",
"php": "^5.4.0 || ^7.0",
"paragonie/random_compat": "2.0.4",
"yzalis/identicon": "1.1.0"
},
"require-dev": {
"codacy/coverage": "dev-master",
"codeclimate/php-test-reporter": "dev-master"
"codeclimate/php-test-reporter": "dev-master",
"phpunit/phpunit": "^4.6 || ^5.0"
},
"autoload": {
"psr-4": {

View File

@ -17,6 +17,10 @@ body.navbar-spacing {
padding-top: 70px;
}
body.loading {
cursor: wait;
}
.buttondisabled {
opacity: 0.3;
}
@ -102,6 +106,12 @@ body.navbar-spacing {
border-left: 1px solid #ccc;
padding: 5px 0 5px 10px;
white-space: pre-wrap;
transition: background-color 0.75s ease-out;
}
.comment.highlight {
background-color: #ffdd86;
transition: background-color 0.2s ease-in;
}
footer h4 {

View File

@ -7,8 +7,8 @@
"en": "de",
"Paste does not exist, has expired or has been deleted.":
"Diesen Text gibt es nicht, er ist abgelaufen oder wurde gelöscht.",
"%s requires php 5.3.0 or above to work. Sorry.":
"%s benötigt PHP 5.3.0 oder höher, um zu funktionieren. Sorry.",
"%s requires php %s or above to work. Sorry.":
"%s benötigt PHP %s oder höher, um zu funktionieren. Sorry.",
"%s requires configuration section [%s] to be present in configuration file.":
"%s benötigt den Konfigurationsabschnitt [%s] in der Konfigurationsdatei um zu funktionieren.",
"Please wait %d seconds between each post.":
@ -83,25 +83,25 @@
"Could not decrypt data (Wrong key?)":
"Konnte Daten nicht entschlüsseln (Falscher Schlüssel?)",
"Could not delete the paste, it was not stored in burn after reading mode.":
"Konnte den Text nicht löschen, er wurde nicht im Einmal-Modus gespeichert.",
"Konnte das Paste nicht löschen, es wurde nicht im Einmal-Modus gespeichert.",
"FOR YOUR EYES ONLY. Don't close this window, this message can't be displayed again.":
"DIESER TEXT IST NUR FÜR DICH GEDACHT. Schliesse das Fenster nicht, diese Nachricht kann nur einmal geöffnet werden.",
"DIESER TEXT IST NUR FÜR DICH GEDACHT. Schließe das Fenster nicht, diese Nachricht kann nur einmal geöffnet werden.",
"Could not decrypt comment; Wrong key?":
"Konnte Kommentar nicht entschlüsseln; Falscher Schlüssel?",
"Reply":
"Antworten",
"Anonymous":
"Anonym",
"Anonymous avatar (Vizhash of the IP address)":
"Anonymer Avatar (Vizhash der IP-Addresse)",
"Avatar generated from IP address":
"Avatar (generiert aus der IP-Adresse)",
"Add comment":
"Kommentar hinzufügen",
"Optional nickname...":
"Optionales Pseudonym...",
"Optional nickname":
"Optionales Pseudonym",
"Post comment":
"Kommentar absenden",
"Sending comment...":
"Sende Kommentar...",
"Sending comment":
"Sende Kommentar",
"Comment posted.":
"Kommentar gesendet.",
"Could not refresh display: %s":
@ -112,24 +112,25 @@
"Fehler auf dem Server oder keine Antwort vom Server",
"Could not post comment: %s":
"Konnte Kommentar nicht senden: %s",
"Sending paste (Please move your mouse for more entropy)...":
"Sende Text (Bitte bewege Deine Maus um die Entropie zu erhöhen)...",
"Sending paste...":
"Sende Text...",
"Please move your mouse for more entropy…":
"Bitte bewege Deine Maus um die Entropie zu erhöhen",
"Sending paste":
"Sende Paste…",
"Your paste is <a id=\"pasteurl\" href=\"%s\">%s</a> <span id=\"copyhint\">(Hit [Ctrl]+[c] to copy)</span>":
"Dein Text ist unter <a id=\"pasteurl\" href=\"%s\">%s</a> zu finden <span id=\"copyhint\">(Drücke [Strg]+[c] um den Link zu kopieren)</span>",
"Dein Paste ist unter <a id=\"pasteurl\" href=\"%s\">%s</a> zu finden <span id=\"copyhint\">(Drücke [Strg]+[c] um den Link zu kopieren)</span>",
"Delete data":
"Lösche Daten",
"Could not create paste: %s":
"Konnte Text nicht erstellen: %s",
"Konnte Paste nicht erstellen: %s",
"Cannot decrypt paste: Decryption key missing in URL (Did you use a redirector or an URL shortener which strips part of the URL?)":
"Konnte Text nicht entschlüsseln: Der Schlüssel fehlt in der Adresse (Hast du eine Umleitung oder einen URL-Verkürzer benutzt, der Teile der Adresse entfernt?)",
"Konnte Paste nicht entschlüsseln: Der Schlüssel fehlt in der Adresse (Hast du eine Umleitung oder einen URL-Verkürzer benutzt, der Teile der Adresse entfernt?)",
"Format": "Format",
"Plain Text": "Nur Text",
"Source Code": "Quellcode",
"Markdown": "Markdown",
"Download attachment": "Anhang herunterladen",
"Cloned file attached.": "Kopierte Datei angehängt.",
"Cloned: '%s'": "Geklont: '%s'",
"The cloned file '%s' was attached to this paste.": "Die geklonte Datei '%s' wurde angehängt.",
"Attach a file": "Datei anhängen",
"Remove attachment": "Anhang entfernen",
"Your browser does not support uploading encrypted files. Please use a newer browser.":
@ -146,6 +147,10 @@
"Enter password":
"Passwort eingeben",
"Loading…": "Lädt…",
"Decrypting paste…": "Entschlüssle Paste…",
"Preparing new paste…": "Bereite neues Paste vor…",
"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>.":
"Wenn diese Nachricht nicht mehr verschwindet, schau bitte in <a href=\"https://github.com/PrivateBin/PrivateBin/wiki/FAQ#why-does-not-the-loading-message-go-away\">die FAQ</a> (englisch), um zu sehen, wie der Fehler behoben werden kann."
"Wenn diese Nachricht nicht mehr verschwindet, schau bitte in <a href=\"https://github.com/PrivateBin/PrivateBin/wiki/FAQ#why-does-not-the-loading-message-go-away\">die FAQ</a> (englisch), um zu sehen, wie der Fehler behoben werden kann.",
"+++ no paste text +++":
"+++ kein Paste-Text +++"
}

View File

@ -7,10 +7,10 @@
"en": "es",
"Paste does not exist, has expired or has been deleted.":
"El texto no existe, ha caducado o ha sido eliminado.",
"%s requires php 5.3.0 or above to work. Sorry.":
"%s requiere php 5.3.0 o superior para funcionar. Lo siento.",
"%s requires php %s or above to work. Sorry.":
"%s requiere php %s o superior para funcionar. Lo siento.",
"%s requires configuration section [%s] to be present in configuration file.":
"%s requiere que la sección de configuración [% s] esté presente en el archivo de configuración.",
"%s requiere que la sección de configuración [%s] esté presente en el archivo de configuración.",
"Please wait %d seconds between each post.":
"Por favor espere %d segundos entre cada publicación.",
"Paste is limited to %s of encrypted data.":
@ -96,12 +96,12 @@
"Avatar anónimo (Vizhash de la dirección IP)",
"Add comment":
"Añadir comentario",
"Optional nickname...":
"Seudónimo opcional...",
"Optional nickname":
"Seudónimo opcional",
"Post comment":
"Publicar comentario",
"Sending comment...":
"Enviando comentario...",
"Sending comment":
"Enviando comentario",
"Comment posted.":
"Comentario publicado.",
"Could not refresh display: %s":
@ -112,10 +112,10 @@
"Error del servidor o el servidor no responde",
"Could not post comment: %s":
"No fue posible publicar comentario: %s",
"Sending paste (Please move your mouse for more entropy)...":
"Enviando texto (Por favor, mueva el ratón para mayor entropía)...",
"Sending paste...":
"Enviando texto...",
"Sending paste (Please move your mouse for more entropy)":
"Enviando texto (Por favor, mueva el ratón para mayor entropía)",
"Sending paste":
"Enviando texto",
"Your paste is <a id=\"pasteurl\" href=\"%s\">%s</a> <span id=\"copyhint\">(Hit [Ctrl]+[c] to copy)</span>":
"Su texto está en <a id=\"pasteurl\" href=\"%s\">%s</a> <span id=\"copyhint\">(Presione [Ctrl]+[c] para copiar)</span>",
"Delete data":

View File

@ -7,8 +7,8 @@
"en": "fr",
"Paste does not exist, has expired or has been deleted.":
"Le paste n'existe pas, a expiré, ou a été supprimé.",
"%s requires php 5.3.0 or above to work. Sorry.":
"Désolé, %s nécessite php 5.3.0 ou supérieur pour fonctionner.",
"%s requires php %s or above to work. Sorry.":
"Désolé, %s nécessite php %s ou supérieur pour fonctionner.",
"%s requires configuration section [%s] to be present in configuration file.":
"%s a besoin de la section de configuration [%s] dans le fichier de configuration pour fonctionner.",
"Please wait %d seconds between each post.":
@ -96,12 +96,12 @@
"Avatar anonyme (Vizhash de l'adresse IP)",
"Add comment":
"Ajouter un commentaire",
"Optional nickname...":
"Pseudonyme optionnel...",
"Optional nickname":
"Pseudonyme optionnel",
"Post comment":
"Poster le commentaire",
"Sending comment...":
"Envoi du commentaire...",
"Sending comment":
"Envoi du commentaire",
"Comment posted.":
"Commentaire posté.",
"Could not refresh display: %s":
@ -112,10 +112,10 @@
"Le serveur ne répond pas ou a rencontré une erreur",
"Could not post comment: %s":
"Impossible de poster le commentaire : %s",
"Sending paste (Please move your mouse for more entropy)...":
"Envoi du paste (Merci de bouger votre souris pour plus d'entropie)...",
"Sending paste...":
"Envoi du paste...",
"Sending paste (Please move your mouse for more entropy)":
"Envoi du paste (Merci de bouger votre souris pour plus d'entropie)",
"Sending paste":
"Envoi du paste",
"Your paste is <a id=\"pasteurl\" href=\"%s\">%s</a> <span id=\"copyhint\">(Hit [Ctrl]+[c] to copy)</span>":
"Votre paste est disponible à l'adresse <a id=\"pasteurl\" href=\"%s\">%s</a> <span id=\"copyhint\">(Appuyez sur [Ctrl]+[c] pour copier)</span>",
"Delete data":
@ -149,12 +149,12 @@
"Editor": "Éditer",
"Preview": "Prévisualiser",
"%s requires the PATH to end in a \"%s\". Please update the PATH in your index.php.":
"%s requires the PATH to end in a \"%s\". Please update the PATH in your index.php.",
"%s requiert que le PATH se termine dans un \"%s\". Veuillez mettre à jour le PATH dans votre index.php.",
"Decrypt":
"Decrypt",
"Déchiffrer",
"Enter password":
"Entrez le mot de passe",
"Loading…": "Loading…",
"Loading…": "Chargement…",
"In case this message never disappears please have a look at <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> (in English)."
"Si ce message ne disparaîssait pas, jetez un oeil à <a href=\"https://github.com/PrivateBin/PrivateBin/wiki/FAQ#why-does-not-the-loading-message-go-away\">cette FAQ pour des idées de résolution</a> (en Anglais)."
}

View File

@ -7,8 +7,8 @@
"en": "it",
"Paste does not exist, has expired or has been deleted.":
"Questo messaggio non esiste, è scaduto o è stato cancellato.",
"%s requires php 5.3.0 or above to work. Sorry.":
"%s richiede PHP 5.3.0 o superiore.",
"%s requires php %s or above to work. Sorry.":
"%s richiede php %s o superiore per funzionare. Ci spiace.",
"%s requires configuration section [%s] to be present in configuration file.":
"%s richiede la presenza della sezione [%s] nei file di configurazione.",
"Please wait %d seconds between each post.":
@ -67,7 +67,7 @@
"Never":
"Mai",
"Note: This is a test service: Data may be deleted anytime. Kittens will die if you abuse this service.":
"Nota: questo è un servizio di prova, i dati possono essere cancellati in qualsiasi momento. Ti preghiamo di non abusare di questo servizio, grazie.",
"Nota: questo è un servizio di prova, i messaggi salvati possono essere cancellati in qualsiasi momento. Moriranno dei gattini se abuserai di questo servizio.",
"This document will expire in %d seconds.":
["Questo documento scadrà tra un secondo.", "Questo documento scadrà in %d secondi."],
"This document will expire in %d minutes.":
@ -87,21 +87,21 @@
"FOR YOUR EYES ONLY. Don't close this window, this message can't be displayed again.":
"FOR YOUR EYES ONLY. Non chiudere questa finestra, il messaggio non può essere visualizzato una seconda volta.",
"Could not decrypt comment; Wrong key?":
"Non riesco a decifrari il commento (Chiave errata?)",
"Non riesco a decifrare il commento (Chiave errata?)",
"Reply":
"Rispondi",
"Anonymous":
"Anonimo",
"Anonymous avatar (Vizhash of the IP address)":
"Avatar Anonino (Vizhash dell'indirizzo IP)",
"Avatar Anonimo (Vizhash dell'indirizzo IP)",
"Add comment":
"Aggiungi un commento",
"Optional nickname...":
"Nickname opzionale...",
"Optional nickname":
"Nickname opzionale",
"Post comment":
"Invia commento",
"Sending comment...":
"Commento in fase di invio...",
"Sending comment":
"Commento in fase di invio",
"Comment posted.":
"Commento inviato.",
"Could not refresh display: %s":
@ -112,10 +112,10 @@
"errore o mancata risposta dal server",
"Could not post comment: %s":
"Impossibile inviare il commento: %s",
"Sending paste (Please move your mouse for more entropy)...":
"Invio messaggio (Muovi il mouse in modo casuale, per generare maggior entropia)...",
"Sending paste...":
"Messaggio in fase di invio...",
"Sending paste (Please move your mouse for more entropy)":
"Invio messaggio (Muovi il mouse in modo casuale, per generare maggior entropia)",
"Sending paste":
"Messaggio in fase di invio",
"Your paste is <a id=\"pasteurl\" href=\"%s\">%s</a> <span id=\"copyhint\">(Hit [Ctrl]+[c] to copy)</span>":
"Il tuo messaggio è qui: <a id=\"pasteurl\" href=\"%s\">%s</a> <span id=\"copyhint\">([CTRL | CMD]+[C] per copiare il link)</span>",
"Delete data":
@ -147,5 +147,5 @@
"Inserisci la password",
"Loading…": "Loading…",
"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> (in English)."
"Nel caso questo messaggio non scompaia, controlla questa <a href=\"https://github.com/PrivateBin/PrivateBin/wiki/FAQ#why-does-not-the-loading-message-go-away\"> FAQ</a> per trovare informazioni su come risolvere il problema (in Inglese)."
}

View File

@ -7,8 +7,8 @@
"en": "no",
"Paste does not exist, has expired or has been deleted.":
"Innlegget eksisterer ikke, er utløpt eller har blitt slettet.",
"%s requires php 5.3.0 or above to work. Sorry.":
"Beklager, %s krever php 5.3.0 eller nyere for å kjøre.",
"%s requires php %s or above to work. Sorry.":
"Beklager, %s krever php %s eller nyere for å kjøre.",
"%s requires configuration section [%s] to be present in configuration file.":
"%s krever konfigurasjonsdel [%s] å være til stede i konfigurasjonsfilen .",
"Please wait %d seconds between each post.":
@ -96,12 +96,12 @@
"Anonym avatar (Vizhash av IP adressen)",
"Add comment":
"Legg til kommentar",
"Optional nickname...":
"Valgfritt kallenavn...",
"Optional nickname":
"Valgfritt kallenavn",
"Post comment":
"Send kommentar",
"Sending comment...":
"Sender Kommentar...",
"Sending comment":
"Sender Kommentar",
"Comment posted.":
"Kommentar sendt.",
"Could not refresh display: %s":
@ -112,10 +112,10 @@
"server feilet eller svarer ikke",
"Could not post comment: %s":
"Kunne ikke sende kommentar: %s",
"Sending paste (Please move your mouse for more entropy)...":
"Sender innlegg (Flytt musen for mere entropi)...",
"Sending paste...":
"Sender innlegg...",
"Sending paste (Please move your mouse for more entropy)":
"Sender innlegg (Flytt musen for mere entropi)",
"Sending paste":
"Sender innlegg",
"Your paste is <a id=\"pasteurl\" href=\"%s\">%s</a> <span id=\"copyhint\">(Hit [Ctrl]+[c] to copy)</span>":
"Ditt innlegg er <a id=\"pasteurl\" href=\"%s\">%s</a> <span id=\"copyhint\">(Trykk [Ctrl]+[c] for å kopiere)</span>",
"Delete data":

View File

@ -7,8 +7,8 @@
"en": "oc",
"Paste does not exist, has expired or has been deleted.":
"Lo tèxte existís pas, a expirat, o es estat suprimit.",
"%s requires php 5.3.0 or above to work. Sorry.":
"O planhèm, %s necessita php 5.3.0 o superior per foncionar.",
"%s requires php %s or above to work. Sorry.":
"O planhèm, %s necessita php %s o superior per foncionar.",
"%s requires configuration section [%s] to be present in configuration file.":
"%s fa besonh de la seccion de configuracion [%s] dins lo fichièr de configuracion per foncionar.",
"Please wait %d seconds between each post.":
@ -96,12 +96,12 @@
"Avatar anonime (Vizhash de l'adreça IP)",
"Add comment":
"Apondre un comentari",
"Optional nickname...":
"Escais opcional...",
"Optional nickname":
"Escais opcional",
"Post comment":
"Mandar lo comentari",
"Sending comment...":
"Mandadís del comentari...",
"Sending comment":
"Mandadís del comentari",
"Comment posted.":
"Comentari mandat.",
"Could not refresh display: %s":
@ -112,10 +112,10 @@
"Lo servidor respond pas o a rencontrat una error",
"Could not post comment: %s":
"Impossible de mandar lo comentari : %s",
"Sending paste (Please move your mouse for more entropy)...":
"Mandadís del tèxte (Mercés de bolegar vòstra mirga per mai entropia)...",
"Sending paste...":
"Mandadís del tèxte...",
"Sending paste (Please move your mouse for more entropy)":
"Mandadís del tèxte (Mercés de bolegar vòstra mirga per mai entropia)",
"Sending paste":
"Mandadís del tèxte",
"Your paste is <a id=\"pasteurl\" href=\"%s\">%s</a> <span id=\"copyhint\">(Hit [Ctrl]+[c] to copy)</span>":
"Vòstre tèxte es disponible a l'adreça <a id=\"pasteurl\" href=\"%s\">%s</a> <span id=\"copyhint\">(Picatz sus [Ctrl]+[c] per copiar)</span>",
"Delete data":

View File

@ -7,8 +7,8 @@
"en": "pl",
"Paste does not exist, has expired or has been deleted.":
"Wklejka nie istnieje, wygasła albo została usunięta.",
"%s requires php 5.3.0 or above to work. Sorry.":
"%s wymaga PHP w wersji 5.3.0 lub nowszej, sorry.",
"%s requires php %s or above to work. Sorry.":
"%s wymaga PHP w wersji %s lub nowszej, sorry.",
"%s requires configuration section [%s] to be present in configuration file.":
"%s wymaga obecności sekcji [%s] w pliku konfiguracyjnym.",
"Please wait %d seconds between each post.":
@ -96,12 +96,12 @@
"Anonimowy avatar (Vizhash z adresu IP)",
"Add comment":
"Dodaj komentarz",
"Optional nickname...":
"Opcjonalny nick...",
"Optional nickname":
"Opcjonalny nick",
"Post comment":
"Wyślij komentarz",
"Sending comment...":
"Wysyłanie komentarza...",
"Sending comment":
"Wysyłanie komentarza",
"Comment posted.":
"Wysłano komentarz.",
"Could not refresh display: %s":
@ -112,10 +112,10 @@
"bląd serwera lub brak odpowiedzi",
"Could not post comment: %s":
"Nie udało się wysłać komentarza: %s",
"Sending paste (Please move your mouse for more entropy)...":
"Wysyłanie wklejki (proszę poruszać myszą aby uzyskać większą entropię)...",
"Sending paste...":
"Wysyłanie wklejki...",
"Sending paste (Please move your mouse for more entropy)":
"Wysyłanie wklejki (proszę poruszać myszą aby uzyskać większą entropię)",
"Sending paste":
"Wysyłanie wklejki",
"Your paste is <a id=\"pasteurl\" href=\"%s\">%s</a> <span id=\"copyhint\">(Hit [Ctrl]+[c] to copy)</span>":
"Twoja wklejka to <a id=\"pasteurl\" href=\"%s\">%s</a> <span id=\"copyhint\">(wciśnij [Ctrl]+[c] aby skopiować)</span>",
"Delete data":

151
i18n/pt.json Normal file
View File

@ -0,0 +1,151 @@
{
"PrivateBin": "PrivateBin",
"%s is a minimalist, open source online pastebin where the server has zero knowledge of pasted data. Data is encrypted/decrypted <i>in the browser</i> using 256 bits AES. More information on the <a href=\"https://privatebin.info/\">project page</a>.":
"%s é um serviço minimalista e de código aberto do tipo \"pastebin\", em que o servidor tem zero conhecimento dos dados copiados. Os dados são cifrados e decifrados <i>no navegador</i> usando 256 bits AES. Mais informações na <a href=\"https://privatebin.info/\">página do projeto</a>.",
"Because ignorance is bliss":
"Porque a ignorância é uma benção",
"en": "pt",
"Paste does not exist, has expired or has been deleted.":
"A cópia não existe, expirou ou já foi excluída.",
"%s requires php %s or above to work. Sorry.":
"%s requer php %s ou superior para funcionar. Desculpa.",
"%s requires configuration section [%s] to be present in configuration file.":
"%s requer que a seção de configuração [% s] esteja no arquivo de configuração.",
"Please wait %d seconds between each post.":
"Por favor espere %d segundos entre cada publicação.",
"Paste is limited to %s of encrypted data.":
"A cópia está limitada a %s de dados cifrados.",
"Invalid data.":
"Dados inválidos.",
"You are unlucky. Try again.":
"Você é azarado. Tente novamente",
"Error saving comment. Sorry.":
"Erro ao salvar comentário. Desculpa.",
"Error saving paste. Sorry.":
"Erro ao salvar cópia. Desculpa.",
"Invalid paste ID.":
"ID de cópia inválido.",
"Paste is not of burn-after-reading type.":
"Cópia não é do tipo \"queime após ler\".",
"Wrong deletion token. Paste was not deleted.":
"Token de remoção inválido. A cópia não foi excluída.",
"Paste was properly deleted.":
"A cópia foi devidamente excluída.",
"JavaScript is required for %s to work.<br />Sorry for the inconvenience.":
"JavaScript é necessário para que %s funcione.<br />Pedimos desculpas pela inconveniência.",
"%s requires a modern browser to work.":
"%s requer um navegador moderno para funcionar.",
"Still using Internet Explorer? Do yourself a favor, switch to a modern browser:":
"Ainda usando Internet Explorer? Faça-se um favor, mude para um navegador moderno:",
"New":
"Novo",
"Send":
"Enviar",
"Clone":
"Clonar",
"Raw text":
"Texto sem formato",
"Expires":
"Expirar em",
"Burn after reading":
"Queime após ler",
"Open discussion":
"Discussão aberta",
"Password (recommended)":
"Senha (recomendada)",
"Discussion":
"Discussão",
"Toggle navigation":
"Mudar navegação",
"%d seconds": ["%d segundo", "%d segundos"],
"%d minutes": ["%d minuto", "%d minutos"],
"%d hours": ["%d hora", "%d horas"],
"%d days": ["%d dia", "%d dias"],
"%d weeks": ["%d semana", "%d semanas"],
"%d months": ["%d mês", "%d meses"],
"%d years": ["%d ano", "%d anos"],
"Never":
"Nunca",
"Note: This is a test service: Data may be deleted anytime. Kittens will die if you abuse this service.":
"Nota: Este é um serviço de teste. Dados podem ser perdidos a qualquer momento. Gatinhos morrerão se você abusar desse serviço.",
"This document will expire in %d seconds.":
["Este documento irá expirar em um segundo.", "Este documento irá expirar em %d segundos."],
"This document will expire in %d minutes.":
["Este documento irá expirar em um minuto.", "Este documento irá expirar em %d minutos."],
"This document will expire in %d hours.":
["Este documento irá expirar em uma hora.", "Este documento irá expirar em %d horas."],
"This document will expire in %d days.":
["Este documento irá expirar em um dia.", "Este documento irá expirar em %d dias."],
"This document will expire in %d months.":
["Este documento irá expirar em um mês.", "Este documento irá expirar em %d meses."],
"Please enter the password for this paste:":
"Por favor, digite a senha para essa cópia:",
"Could not decrypt data (Wrong key?)":
"Não foi possível decifrar os dados (Chave errada?)",
"Could not delete the paste, it was not stored in burn after reading mode.":
"Não foi possível excluir a cópia, ela não foi salva no modo de \"queime após ler\".",
"FOR YOUR EYES ONLY. Don't close this window, this message can't be displayed again.":
"APENAS PARA SEUS OLHOS. Não feche essa janela, essa mensagem não pode ser exibida novamente.",
"Could not decrypt comment; Wrong key?":
"Não foi possível decifrar o comentário; Chave errada?",
"Reply":
"Responder",
"Anonymous":
"Anônimo",
"Avatar generated from IP address":
"Avatar gerado à partir do endereço IP",
"Add comment":
"Adicionar comentário",
"Optional nickname…":
"Apelido opcional…",
"Post comment":
"Publicar comentário",
"Sending comment…":
"Enviando comentário…",
"Comment posted.":
"Comentário publicado.",
"Could not refresh display: %s":
"Não foi possível atualizar a tela: %s",
"unknown status":
"Estado desconhecido",
"server error or not responding":
"Servidor em erro ou não responsivo",
"Could not post comment: %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…":
"Enviando cópia…",
"Your paste is <a id=\"pasteurl\" href=\"%s\">%s</a> <span id=\"copyhint\">(Hit [Ctrl]+[c] to copy)</span>":
"Sua cópia é <a id=\"pasteurl\" href=\"%s\">%s</a> <span id=\"copyhint\">(Pressione [Ctrl]+[c] para copiar)</span>",
"Delete data":
"Excluir dados",
"Could not create paste: %s":
"Não foi possível criar cópia: %s",
"Cannot decrypt paste: Decryption key missing in URL (Did you use a redirector or an URL shortener which strips part of the URL?)":
"Não foi possível decifrar a cópia: chave de decriptografia ausente na URL (Você utilizou um redirecionador ou encurtador de URL que removeu parte dela?)",
"Format": "Formato",
"Plain Text": "Texto sem formato",
"Source Code": "Código fonte",
"Markdown": "Markdown",
"Download attachment": "Baixar anexo",
"Cloned: '%s'": "Clonado: '%s'",
"Attach a file": "Anexar um arquivo",
"Remove attachment": "Remover anexo",
"Your browser does not support uploading encrypted files. Please use a newer browser.":
"Seu navegador não permite subir arquivos cifrados. Por favor, utilize um navegador mais recente.",
"Invalid attachment.": "Anexo inválido.",
"Options": "Opções",
"Shorten URL": "Encurtar URL",
"Editor": "Editor",
"Preview": "Visualizar",
"%s requires the PATH to end in a \"%s\". Please update the PATH in your index.php.":
"%s requer que o PATH termine em \"%s\". Por favor, atualize o PATH em seu index.php.",
"Decrypt":
"Decifrar",
"Enter password":
"Digite a senha",
"Loading…": "Carregando…",
"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>.":
"Caso essa mensagem nunca desapareça, por favor veja <a href=\"https://github.com/PrivateBin/PrivateBin/wiki/FAQ#why-does-not-the-loading-message-go-away\">este FAQ para saber como resolver os problemas</a>."
}

View File

@ -7,8 +7,8 @@
"en": "ru",
"Paste does not exist, has expired or has been deleted.":
"Запись не существует, просрочена или была удалена.",
"%s requires php 5.3.0 or above to work. Sorry.":
"Для работы %s требуется PHP 5.3.0 или выше. Извините.",
"%s requires php %s or above to work. Sorry.":
"Для работы %s требуется php %s или выше. Извините.",
"%s requires configuration section [%s] to be present in configuration file.":
"%s необходимо наличие секции [%s] в конфигурационном файле.",
"Please wait %d seconds between each post.":
@ -32,7 +32,7 @@
"Paste was properly deleted.":
"Запись была успешно удалена.",
"JavaScript is required for %s to work.<br />Sorry for the inconvenience.":
"Для работы %s требуется включенный JavaScript.<br />Приносим извинения за неудобства..",
"Для работы %s требуется включенный JavaScript.<br />Приносим извинения за неудобства.",
"%s requires a modern browser to work.":
"Для работы %s требуется более современный браузер.",
"Still using Internet Explorer? Do yourself a favor, switch to a modern browser:":
@ -96,26 +96,26 @@
"Анонимный аватар (Vizhash IP адреса)",
"Add comment":
"Добавить комментарий",
"Optional nickname...":
"Опциональный никнейм...",
"Optional nickname":
"Опциональный никнейм",
"Post comment":
"Отправить комментарий",
"Sending comment...":
"Отправка комментария...",
"Sending comment":
"Отправка комментария",
"Comment posted.":
"Комментарий опубликован.",
"Could not refresh display: %s":
"Невозможно обновить данные: %s",
"unknown status":
"неизвестная причина",
"server error or not responding":
"ошибка сервера или нет ответа",
"unknown error":
"неизвестная ошибка",
"Could not post comment: %s":
"Не удалось опубликовать комментарий: %s",
"Sending paste (Please move your mouse for more entropy)...":
"Отправка записи (Пожалуйста двигайте мышкой для большей энтропии)...",
"Sending paste...":
"Отправка записи...",
"Please move your mouse for more entropy…":
"Пожалуйста двигайте мышкой для большей энтропии",
"Sending paste":
"Отправка записи",
"Your paste is <a id=\"pasteurl\" href=\"%s\">%s</a> <span id=\"copyhint\">(Hit [Ctrl]+[c] to copy)</span>":
"Ссылка на запись <a id=\"pasteurl\" href=\"%s\">%s</a> <span id=\"copyhint\">(Нажмите [Ctrl]+[c] чтобы скопировать ссылку)</span>",
"Delete data":
@ -138,7 +138,10 @@
"Source Code": "Исходный код",
"Markdown": "Язык разметки",
"Download attachment": "Скачать прикрепленный файл",
"Cloned file attached.": "Дубль файла прикреплен.",
"Cloned file attached.": "Дубликат файла прикреплен.",
"Cloned: '%s'": "Дублировано: '%s'",
"The cloned file '%s' was attached to this paste.":
"Дубликат файла '%s' был прикреплен к этой записи.",
"Attach a file": "Прикрепить файл",
"Remove attachment": "Удалить вложение",
"Your browser does not support uploading encrypted files. Please use a newer browser.":
@ -155,5 +158,12 @@
"Enter password":
"Введите пароль",
"Uploading paste… Please wait.":
"Отправка записи... Пожалуйста подождите."
"Отправка записи… Пожалуйста подождите.",
"Loading…": "Загрузка…",
"Decrypting paste…": "Расшифровка записи…",
"Preparing new paste…": "Подготовка новой записи…",
"In case this message never disappears please have a look at <a href=\"https://github.com/PrivateBin/PrivateBin/wiki/FAQ#why-does-not-the-loading-message-go-away\">this FAQ for information to troubleshoot</a>.":
"Если данное сообщение не исчезает длительное время, посмотрите <a href=\"https://github.com/PrivateBin/PrivateBin/wiki/FAQ#why-does-not-the-loading-message-go-away\">этот FAQ с информацией о возможном решении проблемы (на английском)</a>.",
"+++ no paste text +++":
"+++ в записи нет текста +++"
}

View File

@ -7,8 +7,8 @@
"en": "sl",
"Paste does not exist, has expired or has been deleted.":
"Prilepek ne obstaja, mu je potekla življenjska doba, ali pa je izbrisan.",
"%s requires php 5.3.0 or above to work. Sorry.":
"Oprosti, %s za delovanje potrebuje vsaj php 5.3.0.",
"%s requires php %s or above to work. Sorry.":
"Oprosti, %s za delovanje potrebuje vsaj php %s.",
"%s requires configuration section [%s] to be present in configuration file.":
"%s potrebuje sekcijo konfiguracij [%s] v konfiguracijski datoteki.",
"Please wait %d seconds between each post.":
@ -96,12 +96,12 @@
"Anonimen avatar (Vizhash IP naslova)",
"Add comment":
"Dodaj komentar",
"Optional nickname...":
"Optional nickname":
"Uporabniško ime (lahko izpustiš)",
"Post comment":
"Objavi komentar",
"Sending comment...":
"Pošiljam komentar ...",
"Sending comment":
"Pošiljam komentar ",
"Comment posted.":
"Komentar poslan.",
"Could not refresh display: %s":
@ -112,10 +112,10 @@
"napaka na strežniku, ali pa se strežnik ne odziva",
"Could not post comment: %s":
"Komentarja ni bilo mogoče objaviti : %s",
"Sending paste (Please move your mouse for more entropy)...":
"Pošiljam prilepek (prosim premakni svojo miško za več entropije) ...",
"Sending paste...":
"Pošiljam prilepek...",
"Sending paste (Please move your mouse for more entropy)":
"Pošiljam prilepek (prosim premakni svojo miško za več entropije) ",
"Sending paste":
"Pošiljam prilepek",
"Your paste is <a id=\"pasteurl\" href=\"%s\">%s</a> <span id=\"copyhint\">(Hit [Ctrl]+[c] to copy)</span>":
"Tvoj prilepek je dostopen na naslovu: <a id=\"pasteurl\" href=\"%s\">%s</a> <span id=\"copyhint\">(Pritisni [Ctrl]+[c] ali [Cmd] + [c] in skopiraj)</span>",
"Delete data":

View File

@ -7,8 +7,8 @@
"en": "zh",
"Paste does not exist, has expired or has been deleted.":
"粘贴不存在,已过期或者已被删除。",
"%s requires php 5.3.0 or above to work. Sorry.":
"%s需要工作于PHP 5.3.0及以上版本,抱歉。",
"%s requires php %s or above to work. Sorry.":
"%s需要工作于PHP %s及以上版本,抱歉。",
"%s requires configuration section [%s] to be present in configuration file.":
"%s需要设置配置文件中 [%s] 的部分。",
"Please wait %d seconds between each post.":
@ -92,16 +92,16 @@
"回复",
"Anonymous":
"匿名",
"Anonymous avatar (Vizhash of the IP address)":
"匿名头像 (由IP地址生成Vizhash)",
"Avatar generated from IP address":
"由IP生成的头像",
"Add comment":
"添加评论",
"Optional nickname...":
"可选昵称...",
"Optional nickname":
"可选昵称",
"Post comment":
"评论",
"Sending comment...":
"评论发送中...",
"Sending comment":
"评论发送中",
"Comment posted.":
"评论已发送。",
"Could not refresh display: %s":
@ -112,10 +112,10 @@
"服务器错误或无回应",
"Could not post comment: %s":
"无法发送评论: %s",
"Sending paste (Please move your mouse for more entropy)...":
"粘贴提交中 (请移动鼠标以产生更多熵)...",
"Sending paste...":
"粘贴提交中...",
"Please move your mouse for more entropy…":
"请移动鼠标增加随机性…",
"Sending paste":
"粘贴提交中",
"Your paste is <a id=\"pasteurl\" href=\"%s\">%s</a> <span id=\"copyhint\">(Hit [Ctrl]+[c] to copy)</span>":
"您的粘贴的链接是<a id=\"pasteurl\" href=\"%s\">%s</a> <span id=\"copyhint\">(按下 [Ctrl]+[c] 以复制)</span>",
"Delete data":
@ -129,7 +129,8 @@
"Source Code": "源代码",
"Markdown": "Markdown",
"Download attachment": "下载附件",
"Cloned file attached.": "已附加克隆的文件",
"Cloned: '%s'": "克隆: '%s'",
"The cloned file '%s' was attached to this paste.": "克隆文件 '%s' 已附加到此粘贴。",
"Attach a file": "添加一个附件",
"Remove attachment": "移除附件",
"Your browser does not support uploading encrypted files. Please use a newer browser.":
@ -137,15 +138,18 @@
"Invalid attachment.": "无效的附件",
"Options": "选项",
"Shorten URL": "缩短链接",
"Editor": "編輯",
"Preview": "預習",
"Editor": "编辑",
"Preview": "预览",
"%s requires the PATH to end in a \"%s\". Please update the PATH in your index.php.":
"%s requires the PATH to end in a \"%s\". Please update the PATH in your index.php.",
"%s 的 PATH 变量必须结束于 \"%s\"。 请修改你的 index.php 中的 PATH 变量。",
"Decrypt":
"Decrypt",
"解密",
"Enter password":
"Enter password",
"Loading…": "Loading…",
"输入密码",
"Loading…": "载入中…",
"Decrypting paste…": "正在解密",
"Preparing new paste…": "正在准备新的粘贴",
"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> (in English)."
"如果这个消息一直不消失,请参考 <a href=\"https://github.com/PrivateBin/PrivateBin/wiki/FAQ#why-does-not-the-loading-message-go-away\">这里的 FAQ 进行故障排除</a> (英文版)。",
"+++ no paste text +++": "+++ 没有粘贴内容 +++"
}

File diff suppressed because it is too large Load Diff

View File

@ -11,7 +11,9 @@ var jsc = require('jsverify'),
a2zString.map(function(c) {
return c.toUpperCase();
})
);
),
// schemas supported by the whatwg-url library
schemas = ['ftp','gopher','http','https','ws','wss'];
global.$ = global.jQuery = require('./jquery-3.1.1');
global.sjcl = require('./sjcl-1.0.6');
@ -20,49 +22,49 @@ global.RawDeflate = require('./rawdeflate-0.5');
require('./rawinflate-0.3');
require('./privatebin');
describe('helper', function () {
describe('Helper', function () {
describe('secondsToHuman', function () {
after(function () {
cleanup();
});
jsc.property('returns an array with a number and a word', 'integer', function (number) {
var result = $.PrivateBin.helper.secondsToHuman(number);
var result = $.PrivateBin.Helper.secondsToHuman(number);
return Array.isArray(result) &&
result.length === 2 &&
result[0] === parseInt(result[0], 10) &&
typeof result[1] === 'string';
});
jsc.property('returns seconds on the first array position', 'integer 59', function (number) {
return $.PrivateBin.helper.secondsToHuman(number)[0] === number;
return $.PrivateBin.Helper.secondsToHuman(number)[0] === number;
});
jsc.property('returns seconds on the second array position', 'integer 59', function (number) {
return $.PrivateBin.helper.secondsToHuman(number)[1] === 'second';
return $.PrivateBin.Helper.secondsToHuman(number)[1] === 'second';
});
jsc.property('returns minutes on the first array position', 'integer 60 3599', function (number) {
return $.PrivateBin.helper.secondsToHuman(number)[0] === Math.floor(number / 60);
return $.PrivateBin.Helper.secondsToHuman(number)[0] === Math.floor(number / 60);
});
jsc.property('returns minutes on the second array position', 'integer 60 3599', function (number) {
return $.PrivateBin.helper.secondsToHuman(number)[1] === 'minute';
return $.PrivateBin.Helper.secondsToHuman(number)[1] === 'minute';
});
jsc.property('returns hours on the first array position', 'integer 3600 86399', function (number) {
return $.PrivateBin.helper.secondsToHuman(number)[0] === Math.floor(number / (60 * 60));
return $.PrivateBin.Helper.secondsToHuman(number)[0] === Math.floor(number / (60 * 60));
});
jsc.property('returns hours on the second array position', 'integer 3600 86399', function (number) {
return $.PrivateBin.helper.secondsToHuman(number)[1] === 'hour';
return $.PrivateBin.Helper.secondsToHuman(number)[1] === 'hour';
});
jsc.property('returns days on the first array position', 'integer 86400 5184000', function (number) {
return $.PrivateBin.helper.secondsToHuman(number)[0] === Math.floor(number / (60 * 60 * 24));
return $.PrivateBin.Helper.secondsToHuman(number)[0] === Math.floor(number / (60 * 60 * 24));
});
jsc.property('returns days on the second array position', 'integer 86400 5184000', function (number) {
return $.PrivateBin.helper.secondsToHuman(number)[1] === 'day';
return $.PrivateBin.Helper.secondsToHuman(number)[1] === 'day';
});
// max safe integer as per http://ecma262-5.com/ELS5_HTML.htm#Section_8.5
jsc.property('returns months on the first array position', 'integer 5184000 9007199254740991', function (number) {
return $.PrivateBin.helper.secondsToHuman(number)[0] === Math.floor(number / (60 * 60 * 24 * 30));
return $.PrivateBin.Helper.secondsToHuman(number)[0] === Math.floor(number / (60 * 60 * 24 * 30));
});
jsc.property('returns months on the second array position', 'integer 5184000 9007199254740991', function (number) {
return $.PrivateBin.helper.secondsToHuman(number)[1] === 'month';
return $.PrivateBin.Helper.secondsToHuman(number)[1] === 'month';
});
});
@ -77,11 +79,11 @@ describe('helper', function () {
var html = '',
result = true;
ids.forEach(function(item, i) {
html += '<div id="' + item.join('') + '">' + $.PrivateBin.helper.htmlEntities(contents[i] || contents[0]) + '</div>';
html += '<div id="' + item.join('') + '">' + $.PrivateBin.Helper.htmlEntities(contents[i] || contents[0]) + '</div>';
});
var clean = jsdom(html);
ids.forEach(function(item, i) {
$.PrivateBin.helper.selectText(item.join(''));
$.PrivateBin.Helper.selectText(item.join(''));
// TODO: As per https://github.com/tmpvar/jsdom/issues/321 there is no getSelection in jsdom, yet.
// Once there is one, uncomment the line below to actually check the result.
//result *= (contents[i] || contents[0]) === window.getSelection().toString();
@ -106,13 +108,13 @@ describe('helper', function () {
var html = '',
result = true;
ids.forEach(function(item, i) {
html += '<div id="' + item.join('') + '">' + $.PrivateBin.helper.htmlEntities(contents[i] || contents[0]) + '</div>';
html += '<div id="' + item.join('') + '">' + $.PrivateBin.Helper.htmlEntities(contents[i] || contents[0]) + '</div>';
});
var elements = $('<body />').html(html);
ids.forEach(function(item, i) {
var id = item.join(''),
element = elements.find('#' + id).first();
$.PrivateBin.helper.setElementText(element, replacingContent);
$.PrivateBin.Helper.setElementText(element, replacingContent);
result *= replacingContent === element.text();
});
return Boolean(result);
@ -120,56 +122,6 @@ describe('helper', function () {
);
});
describe('setMessage', function () {
after(function () {
cleanup();
});
jsc.property(
'replaces the content of an empty element, analog to setElementText',
jsc.nearray(jsc.nearray(jsc.elements(alnumString))),
'nearray string',
'string',
function (ids, contents, replacingContent) {
var html = '',
result = true;
ids.forEach(function(item, i) {
html += '<div id="' + item.join('') + '">' + $.PrivateBin.helper.htmlEntities(contents[i] || contents[0]) + '</div>';
});
var elements = $('<body />').html(html);
ids.forEach(function(item, i) {
var id = item.join(''),
element = elements.find('#' + id).first();
$.PrivateBin.helper.setMessage(element, replacingContent);
result *= replacingContent === element.text();
});
return Boolean(result);
}
);
jsc.property(
'replaces the last child of a non-empty element',
jsc.nearray(jsc.nearray(jsc.elements(alnumString))),
jsc.nearray(jsc.nearray(jsc.elements(alnumString))),
'nearray string',
'string',
function (ids, classes, contents, replacingContent) {
var html = '',
result = true;
ids.forEach(function(item, i) {
html += '<div id="' + item.join('') + '"><span class="' + (classes[i] || classes[0]) + '"></span> ' + $.PrivateBin.helper.htmlEntities(contents[i] || contents[0]) + '</div>';
});
var elements = $('<body />').html(html);
ids.forEach(function(item, i) {
var id = item.join(''),
element = elements.find('#' + id).first();
$.PrivateBin.helper.setMessage(element, replacingContent);
result *= ' ' + replacingContent === element.contents()[1].nodeValue;
});
return Boolean(result);
}
);
});
describe('urls2links', function () {
after(function () {
cleanup();
@ -181,7 +133,7 @@ describe('helper', function () {
function (content) {
var element = $('<div>' + content + '</div>'),
before = element.html();
$.PrivateBin.helper.urls2links(element);
$.PrivateBin.Helper.urls2links(element);
return before === element.html();
}
);
@ -197,8 +149,8 @@ describe('helper', function () {
var query = query.join(''),
fragment = fragment.join(''),
url = schema + '://' + address.join('') + '/?' + query + '#' + fragment,
prefix = $.PrivateBin.helper.htmlEntities(prefix),
postfix = ' ' + $.PrivateBin.helper.htmlEntities(postfix),
prefix = $.PrivateBin.Helper.htmlEntities(prefix),
postfix = ' ' + $.PrivateBin.Helper.htmlEntities(postfix),
element = $('<div>' + prefix + url + postfix + '</div>');
// special cases: When the query string and fragment imply the beginning of an HTML entity, eg. &#0 or &#x
@ -212,7 +164,7 @@ describe('helper', function () {
element = $('<div>' + prefix + url + '</div>');
}
$.PrivateBin.helper.urls2links(element);
$.PrivateBin.Helper.urls2links(element);
return element.html() === $('<div>' + prefix + '<a href="' + url + '" rel="nofollow">' + url + '</a>' + postfix + '</div>').html();
}
);
@ -223,10 +175,10 @@ describe('helper', function () {
'string',
function (prefix, query, postfix) {
var url = 'magnet:?' + query.join(''),
prefix = $.PrivateBin.helper.htmlEntities(prefix),
postfix = $.PrivateBin.helper.htmlEntities(postfix),
prefix = $.PrivateBin.Helper.htmlEntities(prefix),
postfix = $.PrivateBin.Helper.htmlEntities(postfix),
element = $('<div>' + prefix + url + ' ' + postfix + '</div>');
$.PrivateBin.helper.urls2links(element);
$.PrivateBin.Helper.urls2links(element);
return element.html() === $('<div>' + prefix + '<a href="' + url + '" rel="nofollow">' + url + '</a> ' + postfix + '</div>').html();
}
);
@ -247,8 +199,7 @@ describe('helper', function () {
postfix = postfix.replace(/%(s|d)/g, '%%'),
result = prefix + params[0] + postfix;
params.unshift(prefix + '%s' + postfix);
return result === $.PrivateBin.helper.sprintf.apply(this, params) &&
result === $.PrivateBin.helper.sprintf(params);
return result === $.PrivateBin.Helper.sprintf.apply(this, params);
}
);
jsc.property(
@ -261,8 +212,7 @@ describe('helper', function () {
postfix = postfix.replace(/%(s|d)/g, '%%'),
result = prefix + params[0] + postfix;
params.unshift(prefix + '%d' + postfix);
return result === $.PrivateBin.helper.sprintf.apply(this, params) &&
result === $.PrivateBin.helper.sprintf(params);
return result === $.PrivateBin.Helper.sprintf.apply(this, params);
}
);
jsc.property(
@ -275,8 +225,7 @@ describe('helper', function () {
postfix = postfix.replace(/%(s|d)/g, '%%'),
result = prefix + '0' + postfix;
params.unshift(prefix + '%d' + postfix);
return result === $.PrivateBin.helper.sprintf.apply(this, params) &&
result === $.PrivateBin.helper.sprintf(params);
return result === $.PrivateBin.Helper.sprintf.apply(this, params)
}
);
jsc.property(
@ -291,8 +240,7 @@ describe('helper', function () {
postfix = postfix.replace(/%(s|d)/g, '%%'),
params = [prefix + '%d' + middle + '%s' + postfix, uint, string],
result = prefix + uint + middle + string + postfix;
return result === $.PrivateBin.helper.sprintf.apply(this, params) &&
result === $.PrivateBin.helper.sprintf(params);
return result === $.PrivateBin.Helper.sprintf.apply(this, params);
}
);
jsc.property(
@ -307,8 +255,7 @@ describe('helper', function () {
postfix = postfix.replace(/%(s|d)/g, '%%'),
params = [prefix + '%s' + middle + '%d' + postfix, string, uint],
result = prefix + string + middle + uint + postfix;
return result === $.PrivateBin.helper.sprintf.apply(this, params) &&
result === $.PrivateBin.helper.sprintf(params);
return result === $.PrivateBin.Helper.sprintf.apply(this, params);
}
);
});
@ -333,88 +280,35 @@ describe('helper', function () {
}
});
var clean = jsdom('', {cookie: cookieArray}),
result = $.PrivateBin.helper.getCookie(selectedKey);
result = $.PrivateBin.Helper.getCookie(selectedKey);
clean();
return result === selectedValue;
}
);
});
describe('scriptLocation', function () {
describe('baseUri', function () {
before(function () {
$.PrivateBin.Helper.reset();
});
jsc.property(
'returns the URL without query & fragment',
jsc.nearray(jsc.elements(a2zString)),
jsc.elements(schemas),
jsc.nearray(jsc.elements(a2zString)),
jsc.array(jsc.elements(queryString)),
'string',
function (schema, address, query, fragment) {
var expected = schema.join('') + '://' + address.join('') + '/',
var expected = schema + '://' + address.join('') + '/',
clean = jsdom('', {url: expected + '?' + query.join('') + '#' + fragment}),
result = $.PrivateBin.helper.scriptLocation();
result = $.PrivateBin.Helper.baseUri();
$.PrivateBin.Helper.reset();
clean();
return expected === result;
}
);
});
describe('pasteId', function () {
jsc.property(
'returns the query string without separator, if any',
jsc.nearray(jsc.elements(a2zString)),
jsc.nearray(jsc.elements(a2zString)),
jsc.array(jsc.elements(queryString)),
'string',
function (schema, address, query, fragment) {
var queryString = query.join(''),
clean = jsdom('', {
url: schema.join('') + '://' + address.join('') +
'/?' + queryString + '#' + fragment
}),
result = $.PrivateBin.helper.pasteId();
clean();
return queryString === result;
}
);
});
describe('pageKey', function () {
jsc.property(
'returns the fragment of the URL',
jsc.nearray(jsc.elements(a2zString)),
jsc.nearray(jsc.elements(a2zString)),
jsc.array(jsc.elements(queryString)),
jsc.array(jsc.elements(base64String)),
function (schema, address, query, fragment) {
var fragmentString = fragment.join(''),
clean = jsdom('', {
url: schema.join('') + '://' + address.join('') +
'/?' + query.join('') + '#' + fragmentString
}),
result = $.PrivateBin.helper.pageKey();
clean();
return fragmentString === result;
}
);
jsc.property(
'returns the fragment stripped of trailing query parts',
jsc.nearray(jsc.elements(a2zString)),
jsc.nearray(jsc.elements(a2zString)),
jsc.array(jsc.elements(queryString)),
jsc.array(jsc.elements(base64String)),
jsc.array(jsc.elements(queryString)),
function (schema, address, query, fragment, trail) {
var fragmentString = fragment.join(''),
clean = jsdom('', {
url: schema.join('') + '://' + address.join('') + '/?' +
query.join('') + '#' + fragmentString + '&' + trail.join('')
}),
result = $.PrivateBin.helper.pageKey();
clean();
return fragmentString === result;
}
);
});
describe('htmlEntities', function () {
after(function () {
cleanup();
@ -424,9 +318,76 @@ describe('helper', function () {
'removes all HTML entities from any given string',
'string',
function (string) {
var result = $.PrivateBin.helper.htmlEntities(string);
var result = $.PrivateBin.Helper.htmlEntities(string);
return !(/[<>"'`=\/]/.test(result)) && !(string.indexOf('&') > -1 && !(/&amp;/.test(result)));
}
);
});
});
describe('Model', function () {
describe('getPasteId', function () {
before(function () {
$.PrivateBin.Model.reset();
});
jsc.property(
'returns the query string without separator, if any',
jsc.nearray(jsc.elements(a2zString)),
jsc.nearray(jsc.elements(a2zString)),
jsc.nearray(jsc.elements(queryString)),
'string',
function (schema, address, query, fragment) {
var queryString = query.join(''),
clean = jsdom('', {
url: schema.join('') + '://' + address.join('') +
'/?' + queryString + '#' + fragment
}),
result = $.PrivateBin.Model.getPasteId();
$.PrivateBin.Model.reset();
clean();
return queryString === result;
}
);
});
describe('getPasteKey', function () {
jsc.property(
'returns the fragment of the URL',
jsc.nearray(jsc.elements(a2zString)),
jsc.nearray(jsc.elements(a2zString)),
jsc.array(jsc.elements(queryString)),
jsc.nearray(jsc.elements(base64String)),
function (schema, address, query, fragment) {
var fragmentString = fragment.join(''),
clean = jsdom('', {
url: schema.join('') + '://' + address.join('') +
'/?' + query.join('') + '#' + fragmentString
}),
result = $.PrivateBin.Model.getPasteKey();
$.PrivateBin.Model.reset();
clean();
return fragmentString === result;
}
);
jsc.property(
'returns the fragment stripped of trailing query parts',
jsc.nearray(jsc.elements(a2zString)),
jsc.nearray(jsc.elements(a2zString)),
jsc.array(jsc.elements(queryString)),
jsc.nearray(jsc.elements(base64String)),
jsc.array(jsc.elements(queryString)),
function (schema, address, query, fragment, trail) {
var fragmentString = fragment.join(''),
clean = jsdom('', {
url: schema.join('') + '://' + address.join('') + '/?' +
query.join('') + '#' + fragmentString + '&' + trail.join('')
}),
result = $.PrivateBin.Model.getPasteKey();
$.PrivateBin.Model.reset();
clean();
return fragmentString === result;
}
);
});
});

View File

@ -1,2 +1 @@
Allow from none
Deny from all
Require all denied

View File

@ -58,7 +58,7 @@ abstract class AbstractData
* @access public
* @static
* @param array $options
* @return privatebin_abstract
* @return AbstractData
*/
public static function getInstance($options)
{
@ -88,7 +88,6 @@ abstract class AbstractData
*
* @access public
* @param string $pasteid
* @return void
*/
abstract public function delete($pasteid);
@ -147,7 +146,6 @@ abstract class AbstractData
*
* @access public
* @param int $batchsize
* @return void
*/
public function purge($batchsize)
{

View File

@ -282,7 +282,6 @@ class Database extends AbstractData
*
* @access public
* @param string $pasteid
* @return void
*/
public function delete($pasteid)
{
@ -375,11 +374,10 @@ class Database extends AbstractData
$comments[$i]->data = $row['data'];
$comments[$i]->meta = new stdClass;
$comments[$i]->meta->postdate = (int) $row['postdate'];
if (array_key_exists('nickname', $row) && !empty($row['nickname'])) {
$comments[$i]->meta->nickname = $row['nickname'];
}
if (array_key_exists('vizhash', $row) && !empty($row['vizhash'])) {
$comments[$i]->meta->vizhash = $row['vizhash'];
foreach (array('nickname', 'vizhash') as $key) {
if (array_key_exists($key, $row) && !empty($row[$key])) {
$comments[$i]->meta->$key = $row[$key];
}
}
}
ksort($comments);
@ -564,7 +562,6 @@ class Database extends AbstractData
*
* @access private
* @static
* @return void
*/
private static function _createPasteTable()
{
@ -589,7 +586,6 @@ class Database extends AbstractData
*
* @access private
* @static
* @return void
*/
private static function _createCommentTable()
{
@ -616,7 +612,6 @@ class Database extends AbstractData
*
* @access private
* @static
* @return void
*/
private static function _createConfigTable()
{
@ -651,7 +646,6 @@ class Database extends AbstractData
* @access private
* @static
* @param string $oldversion
* @return void
*/
private static function _upgradeDatabase($oldversion)
{

View File

@ -12,8 +12,8 @@
namespace PrivateBin\Data;
use PrivateBin\Json;
use PrivateBin\Model\Paste;
use PrivateBin\Persistence\DataStore;
/**
* Filesystem
@ -22,15 +22,6 @@ use PrivateBin\Model\Paste;
*/
class Filesystem extends AbstractData
{
/**
* directory where data is stored
*
* @access private
* @static
* @var string
*/
private static $_dir = 'data/';
/**
* get instance of singleton
*
@ -41,17 +32,16 @@ class Filesystem extends AbstractData
*/
public static function getInstance($options = null)
{
// if needed initialize the singleton
if (!(self::$_instance instanceof self)) {
self::$_instance = new self;
}
// if given update the data directory
if (
is_array($options) &&
array_key_exists('dir', $options)
) {
self::$_dir = $options['dir'] . DIRECTORY_SEPARATOR;
}
// if needed initialize the singleton
if (!(self::$_instance instanceof self)) {
self::$_instance = new self;
self::_init();
DataStore::setPath($options['dir']);
}
return self::$_instance;
}
@ -62,19 +52,19 @@ class Filesystem extends AbstractData
* @access public
* @param string $pasteid
* @param array $paste
* @throws Exception
* @return bool
*/
public function create($pasteid, $paste)
{
$storagedir = self::_dataid2path($pasteid);
if (is_file($storagedir . $pasteid)) {
$file = $storagedir . $pasteid;
if (is_file($file)) {
return false;
}
if (!is_dir($storagedir)) {
mkdir($storagedir, 0700, true);
}
return (bool) file_put_contents($storagedir . $pasteid, Json::encode($paste));
return DataStore::store($file, $paste);
}
/**
@ -108,7 +98,6 @@ class Filesystem extends AbstractData
*
* @access public
* @param string $pasteid
* @return void
*/
public function delete($pasteid)
{
@ -155,20 +144,19 @@ class Filesystem extends AbstractData
* @param string $parentid
* @param string $commentid
* @param array $comment
* @throws Exception
* @return bool
*/
public function createComment($pasteid, $parentid, $commentid, $comment)
{
$storagedir = self::_dataid2discussionpath($pasteid);
$filename = $pasteid . '.' . $commentid . '.' . $parentid;
if (is_file($storagedir . $filename)) {
$file = $storagedir . $pasteid . '.' . $commentid . '.' . $parentid;
if (is_file($file)) {
return false;
}
if (!is_dir($storagedir)) {
mkdir($storagedir, 0700, true);
}
return (bool) file_put_contents($storagedir . $filename, Json::encode($comment));
return DataStore::store($file, $comment);
}
/**
@ -237,8 +225,9 @@ class Filesystem extends AbstractData
protected function _getExpiredPastes($batchsize)
{
$pastes = array();
$mainpath = DataStore::getPath();
$firstLevel = array_filter(
scandir(self::$_dir),
scandir($mainpath),
'self::_isFirstLevelDir'
);
if (count($firstLevel) > 0) {
@ -246,7 +235,7 @@ class Filesystem extends AbstractData
for ($i = 0, $max = $batchsize * 10; $i < $max; ++$i) {
$firstKey = array_rand($firstLevel);
$secondLevel = array_filter(
scandir(self::$_dir . $firstLevel[$firstKey]),
scandir($mainpath . DIRECTORY_SEPARATOR . $firstLevel[$firstKey]),
'self::_isSecondLevelDir'
);
@ -257,8 +246,9 @@ class Filesystem extends AbstractData
}
$secondKey = array_rand($secondLevel);
$path = self::$_dir . $firstLevel[$firstKey] .
DIRECTORY_SEPARATOR . $secondLevel[$secondKey];
$path = $mainpath . DIRECTORY_SEPARATOR .
$firstLevel[$firstKey] . DIRECTORY_SEPARATOR .
$secondLevel[$secondKey];
if (!is_dir($path)) {
continue;
}
@ -292,29 +282,6 @@ class Filesystem extends AbstractData
return $pastes;
}
/**
* initialize privatebin
*
* @access private
* @static
* @return void
*/
private static function _init()
{
// Create storage directory if it does not exist.
if (!is_dir(self::$_dir)) {
mkdir(self::$_dir, 0700);
}
// Create .htaccess file if it does not exist.
if (!is_file(self::$_dir . '.htaccess')) {
file_put_contents(
self::$_dir . '.htaccess',
'Allow from none' . PHP_EOL .
'Deny from all' . PHP_EOL
);
}
}
/**
* Convert paste id to storage path.
*
@ -332,8 +299,10 @@ class Filesystem extends AbstractData
*/
private static function _dataid2path($dataid)
{
return self::$_dir . substr($dataid, 0, 2) . DIRECTORY_SEPARATOR .
substr($dataid, 2, 2) . DIRECTORY_SEPARATOR;
return DataStore::getPath(
substr($dataid, 0, 2) . DIRECTORY_SEPARATOR .
substr($dataid, 2, 2) . DIRECTORY_SEPARATOR
);
}
/**
@ -363,7 +332,7 @@ class Filesystem extends AbstractData
private static function _isFirstLevelDir($element)
{
return self::_isSecondLevelDir($element) &&
is_dir(self::$_dir . DIRECTORY_SEPARATOR . $element);
is_dir(DataStore::getPath($element));
}
/**

View File

@ -21,21 +21,6 @@ use Exception;
*/
class Filter
{
/**
* strips slashes deeply
*
* @access public
* @static
* @param mixed $value
* @return mixed
*/
public static function stripslashesDeep($value)
{
return is_array($value) ?
array_map('self::stripslashesDeep', $value) :
stripslashes($value);
}
/**
* format a given time string into a human readable label (localized)
*

View File

@ -135,15 +135,17 @@ class I18n
*
* @access public
* @static
* @return void
*/
public static function loadTranslations()
{
$availableLanguages = self::getAvailableLanguages();
// check if the lang cookie was set and that language exists
if (array_key_exists('lang', $_COOKIE) && in_array($_COOKIE['lang'], $availableLanguages)) {
$match = $_COOKIE['lang'];
if (
array_key_exists('lang', $_COOKIE) &&
($key = array_search($_COOKIE['lang'], $availableLanguages)) !== false
) {
$match = $availableLanguages[$key];
}
// find a translation file matching the browsers language preferences
else {
@ -256,7 +258,6 @@ class I18n
* @access public
* @static
* @param string $lang
* @return void
*/
public static function setLanguageFallback($lang)
{
@ -304,7 +305,7 @@ class I18n
return $n % 10 == 1 && $n % 100 != 11 ? 0 : ($n % 10 >= 2 && $n % 10 <= 4 && ($n % 100 < 10 || $n % 100 >= 20) ? 1 : 2);
case 'sl':
return $n % 100 == 1 ? 1 : ($n % 100 == 2 ? 2 : ($n % 100 == 3 || $n % 100 == 4 ? 3 : 0));
// de, en, es, it, no
// de, en, es, it, no, pt
default:
return $n != 1 ? 1 : 0;
}

View File

@ -40,7 +40,6 @@ class Model
* Factory constructor.
*
* @param configuration $conf
* @return void
*/
public function __construct(Configuration $conf)
{
@ -64,8 +63,6 @@ class Model
/**
* Checks if a purge is necessary and triggers it if yes.
*
* @return void
*/
public function purge()
{

View File

@ -63,7 +63,6 @@ abstract class AbstractModel
* @access public
* @param Configuration $configuration
* @param AbstractData $storage
* @return void
*/
public function __construct(Configuration $configuration, AbstractData $storage)
{
@ -90,7 +89,6 @@ abstract class AbstractModel
* @access public
* @param string $id
* @throws Exception
* @return void
*/
public function setId($id)
{
@ -106,7 +104,6 @@ abstract class AbstractModel
* @access public
* @param string $data
* @throws Exception
* @return void
*/
public function setData($data)
{
@ -133,7 +130,6 @@ abstract class AbstractModel
*
* @access public
* @throws Exception
* @return void
*/
abstract public function store();
@ -142,7 +138,6 @@ abstract class AbstractModel
*
* @access public
* @throws Exception
* @return void
*/
abstract public function delete();

View File

@ -61,7 +61,6 @@ class Comment extends AbstractModel
*
* @access public
* @throws Exception
* @return void
*/
public function store()
{
@ -101,7 +100,6 @@ class Comment extends AbstractModel
*
* @access public
* @throws Exception
* @return void
*/
public function delete()
{
@ -129,7 +127,6 @@ class Comment extends AbstractModel
* @access public
* @param Paste $paste
* @throws Exception
* @return void
*/
public function setPaste(Paste $paste)
{
@ -154,7 +151,6 @@ class Comment extends AbstractModel
* @access public
* @param string $id
* @throws Exception
* @return void
*/
public function setParentId($id)
{
@ -184,7 +180,6 @@ class Comment extends AbstractModel
* @access public
* @param string $nickname
* @throws Exception
* @return void
*/
public function setNickname($nickname)
{

View File

@ -75,7 +75,6 @@ class Paste extends AbstractModel
*
* @access public
* @throws Exception
* @return void
*/
public function store()
{
@ -103,7 +102,6 @@ class Paste extends AbstractModel
*
* @access public
* @throws Exception
* @return void
*/
public function delete()
{
@ -183,7 +181,6 @@ class Paste extends AbstractModel
* @access public
* @param string $attachment
* @throws Exception
* @return void
*/
public function setAttachment($attachment)
{
@ -199,7 +196,6 @@ class Paste extends AbstractModel
* @access public
* @param string $attachmentname
* @throws Exception
* @return void
*/
public function setAttachmentName($attachmentname)
{
@ -214,7 +210,6 @@ class Paste extends AbstractModel
*
* @access public
* @param string $expiration
* @return void
*/
public function setExpiration($expiration)
{
@ -236,7 +231,6 @@ class Paste extends AbstractModel
* @access public
* @param string $burnafterreading
* @throws Exception
* @return void
*/
public function setBurnafterreading($burnafterreading = '1')
{
@ -257,7 +251,6 @@ class Paste extends AbstractModel
* @access public
* @param string $opendiscussion
* @throws Exception
* @return void
*/
public function setOpendiscussion($opendiscussion = '1')
{
@ -281,7 +274,6 @@ class Paste extends AbstractModel
* @access public
* @param string $format
* @throws Exception
* @return void
*/
public function setFormatter($format)
{

View File

@ -36,7 +36,6 @@ abstract class AbstractPersistence
* @access public
* @static
* @param string $path
* @return void
*/
public static function setPath($path)
{
@ -80,27 +79,23 @@ abstract class AbstractPersistence
* @access protected
* @static
* @throws Exception
* @return void
*/
protected static function _initialize()
{
// Create storage directory if it does not exist.
if (!is_dir(self::$_path)) {
if (!@mkdir(self::$_path)) {
if (!@mkdir(self::$_path, 0700)) {
throw new Exception('unable to create directory ' . self::$_path, 10);
}
}
// Create .htaccess file if it does not exist.
$file = self::$_path . DIRECTORY_SEPARATOR . '.htaccess';
if (!is_file($file)) {
$writtenBytes = @file_put_contents(
$file,
'Allow from none' . PHP_EOL .
'Deny from all' . PHP_EOL,
'Require all denied' . PHP_EOL,
LOCK_EX
);
if ($writtenBytes === false || $writtenBytes < 30) {
if ($writtenBytes === false || $writtenBytes < 19) {
throw new Exception('unable to write to file ' . $file, 11);
}
}

View File

@ -0,0 +1,47 @@
<?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.1
*/
namespace PrivateBin\Persistence;
use Exception;
use PrivateBin\Json;
/**
* DataStore
*
* Handles data storage for Data\Filesystem.
*/
class DataStore extends AbstractPersistence
{
/**
* store the data
*
* @access public
* @static
* @param string $filename
* @param array $data
* @return bool
*/
public static function store($filename, $data)
{
$path = self::getPath();
if (strpos($filename, $path) === 0) {
$filename = substr($filename, strlen($path));
}
try {
self::_store($filename, Json::encode($data));
return true;
} catch (Exception $e) {
return false;
}
}
}

View File

@ -36,7 +36,6 @@ class PurgeLimiter extends AbstractPersistence
* @access public
* @static
* @param int $limit
* @return void
*/
public static function setLimit($limit)
{
@ -49,7 +48,6 @@ class PurgeLimiter extends AbstractPersistence
* @access public
* @static
* @param Configuration $conf
* @return void
*/
public static function setConfiguration(Configuration $conf)
{

View File

@ -95,7 +95,6 @@ class ServerSalt extends AbstractPersistence
* @access public
* @static
* @param string $path
* @return void
*/
public static function setPath($path)
{

View File

@ -45,7 +45,6 @@ class TrafficLimiter extends AbstractPersistence
* @access public
* @static
* @param int $limit
* @return void
*/
public static function setLimit($limit)
{
@ -58,7 +57,6 @@ class TrafficLimiter extends AbstractPersistence
* @access public
* @static
* @param Configuration $conf
* @return void
*/
public static function setConfiguration(Configuration $conf)
{

View File

@ -30,6 +30,13 @@ class PrivateBin
*/
const VERSION = '1.1';
/**
* minimal required PHP version
*
* @const string
*/
const MIN_PHP_VERSION = '5.4.0';
/**
* show the same error message if the paste expired or does not exist
*
@ -116,12 +123,11 @@ class PrivateBin
*
* @access public
* @throws Exception
* @return void
*/
public function __construct()
{
if (version_compare(PHP_VERSION, '5.3.0') < 0) {
throw new Exception(I18n::_('%s requires php 5.3.0 or above to work. Sorry.', I18n::_('PrivateBin')), 1);
if (version_compare(PHP_VERSION, self::MIN_PHP_VERSION) < 0) {
throw new Exception(I18n::_('%s requires php %s or above to work. Sorry.', I18n::_('PrivateBin'), self::MIN_PHP_VERSION), 1);
}
if (strlen(PATH) < 0 && substr(PATH, -1) !== DIRECTORY_SEPARATOR) {
throw new Exception(I18n::_('%s requires the PATH to end in a "%s". Please update the PATH in your index.php.', I18n::_('PrivateBin'), DIRECTORY_SEPARATOR), 5);
@ -164,21 +170,9 @@ class PrivateBin
* initialize privatebin
*
* @access private
* @return void
*/
private function _init()
{
foreach (array('cfg', 'lib') as $dir) {
if (!is_file(PATH . $dir . DIRECTORY_SEPARATOR . '.htaccess')) {
file_put_contents(
PATH . $dir . DIRECTORY_SEPARATOR . '.htaccess',
'Allow from none' . PHP_EOL .
'Deny from all' . PHP_EOL,
LOCK_EX
);
}
}
$this->_conf = new Configuration;
$this->_model = new Model($this->_conf);
$this->_request = new Request;
@ -324,7 +318,6 @@ class PrivateBin
* @access private
* @param string $dataid
* @param string $deletetoken
* @return void
*/
private function _delete($dataid, $deletetoken)
{
@ -334,19 +327,16 @@ class PrivateBin
// accessing this property ensures that the paste would be
// deleted if it has already expired
$burnafterreading = $paste->isBurnafterreading();
if ($deletetoken == 'burnafterreading') {
if ($burnafterreading) {
$paste->delete();
$this->_return_message(0, $dataid);
} else {
$this->_return_message(1, 'Paste is not of burn-after-reading type.');
}
if (
($burnafterreading && $deletetoken == 'burnafterreading') ||
Filter::slowEquals($deletetoken, $paste->getDeleteToken())
) {
// Paste exists and deletion token is valid: Delete the paste.
$paste->delete();
$this->_status = 'Paste was properly deleted.';
} else {
// Make sure the token is valid.
if (Filter::slowEquals($deletetoken, $paste->getDeleteToken())) {
// Paste exists and deletion token is valid: Delete the paste.
$paste->delete();
$this->_status = 'Paste was properly deleted.';
if (!$burnafterreading && $deletetoken == 'burnafterreading') {
$this->_error = 'Paste is not of burn-after-reading type.';
} else {
$this->_error = 'Wrong deletion token. Paste was not deleted.';
}
@ -357,6 +347,13 @@ class PrivateBin
} catch (Exception $e) {
$this->_error = $e->getMessage();
}
if ($this->_request->isJsonApiCall()) {
if (strlen($this->_error)) {
$this->_return_message(1, $this->_error);
} else {
$this->_return_message(0, $dataid);
}
}
}
/**
@ -364,7 +361,6 @@ class PrivateBin
*
* @access private
* @param string $dataid
* @return void
*/
private function _read($dataid)
{
@ -397,7 +393,6 @@ class PrivateBin
* Display PrivateBin frontend.
*
* @access private
* @return void
*/
private function _view()
{
@ -461,7 +456,6 @@ class PrivateBin
*
* @access private
* @param string $type
* @return void
*/
private function _jsonld($type)
{
@ -494,7 +488,6 @@ class PrivateBin
* @param int $status
* @param string $message
* @param array $other
* @return void
*/
private function _return_message($status, $message, $other = array())
{

View File

@ -41,7 +41,7 @@ class Request
const MIME_XHTML = 'application/xhtml+xml';
/**
* Input stream to use for PUT parameter parsing.
* Input stream to use for PUT parameter parsing
*
* @access private
* @var string
@ -49,7 +49,7 @@ class Request
private static $_inputStream = 'php://input';
/**
* Operation to perform.
* Operation to perform
*
* @access private
* @var string
@ -57,7 +57,7 @@ class Request
private $_operation = 'view';
/**
* Request parameters.
* Request parameters
*
* @access private
* @var array
@ -65,7 +65,7 @@ class Request
private $_params = array();
/**
* If we are in a JSON API context.
* If we are in a JSON API context
*
* @access private
* @var bool
@ -73,20 +73,12 @@ class Request
private $_isJsonApi = false;
/**
* Constructor.
* Constructor
*
* @access public
* @return void
*/
public function __construct()
{
// in case stupid admin has left magic_quotes enabled in php.ini (for PHP < 5.4)
if (version_compare(PHP_VERSION, '5.4.0') < 0 && get_magic_quotes_gpc()) {
$_POST = array_map('PrivateBin\\Filter::stripslashesDeep', $_POST);
$_GET = array_map('PrivateBin\\Filter::stripslashesDeep', $_GET);
$_COOKIE = array_map('PrivateBin\\Filter::stripslashesDeep', $_COOKIE);
}
// decide if we are in JSON API or HTML context
$this->_isJsonApi = $this->_detectJsonRequest();
@ -129,7 +121,7 @@ class Request
}
/**
* Get current operation.
* Get current operation
*
* @access public
* @return string
@ -140,7 +132,7 @@ class Request
}
/**
* Get a request parameter.
* Get a request parameter
*
* @access public
* @param string $param
@ -153,7 +145,7 @@ class Request
}
/**
* If we are in a JSON API context.
* If we are in a JSON API context
*
* @access public
* @return bool
@ -164,7 +156,7 @@ class Request
}
/**
* Override the default input stream source, used for unit testing.
* Override the default input stream source, used for unit testing
*
* @param string $input
*/
@ -174,7 +166,7 @@ class Request
}
/**
* detect the clients supported media type and decide if its a JSON API call or not
* Detect the clients supported media type and decide if its a JSON API call or not
*
* Adapted from: https://stackoverflow.com/questions/3770513/detect-browser-language-in-php#3771447
*

View File

@ -35,7 +35,6 @@ class View
* @access public
* @param string $name
* @param mixed $value
* @return void
*/
public function assign($name, $value)
{
@ -48,7 +47,6 @@ class View
* @access public
* @param string $template
* @throws Exception
* @return void
*/
public function draw($template)
{

View File

@ -61,7 +61,6 @@ class Vizhash16x16
* constructor
*
* @access public
* @return void
*/
public function __construct()
{
@ -210,7 +209,6 @@ class Vizhash16x16
* @param resource $image
* @param int $action
* @param int $color
* @return void
*/
private function drawshape($image, $action, $color)
{

1
tpl/.htaccess Normal file
View File

@ -0,0 +1 @@
Require all denied

View File

@ -4,7 +4,7 @@ $isCpct = substr($template, 9, 8) === '-compact';
$isDark = substr($template, 9, 5) === '-dark';
$isPage = substr($template, -5) === '-page';
?><!DOCTYPE html>
<html lang="en">
<html>
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge">
@ -69,7 +69,7 @@ if ($MARKDOWN):
<?php
endif;
?>
<script type="text/javascript" src="js/privatebin.js?<?php echo rawurlencode($VERSION); ?>" integrity="sha512-Ap3kgrXsOa+7xANzug/xGKb3rEOzCYuJoxUG5vmwYRWn/8wI9Cc/RYZehKnFwHuFCFnQzfqJ9GT2D1HSOdnd5g==" crossorigin="anonymous"></script>
<script type="text/javascript" src="js/privatebin.js?<?php echo rawurlencode($VERSION); ?>" integrity="sha512-xfoNRo43UxDcd7m1P2IsvSPcOeGJo1oUjbA+CNCBoBilKRU3Iuqbf7awv3knP50bX7PVGEjtS6yJ2KtOtqnhTA==" crossorigin="anonymous"></script>
<!--[if lt IE 10]>
<style type="text/css">body {padding-left:60px;padding-right:60px;} #ienotice {display:block;} #oldienotice {display:block;}</style>
<![endif]-->
@ -94,7 +94,7 @@ endif;
<form id="passwordform" role="form">
<div class="form-group">
<label for="passworddecrypt"><span class="glyphicon glyphicon-eye-open"></span> <?php echo I18n::_('Please enter the password for this paste:') ?></label>
<input id="passworddecrypt" type="password" class="form-control" placeholder="<?php echo I18n::_('Enter password') ?>" autofocus>
<input id="passworddecrypt" type="password" class="form-control" placeholder="<?php echo I18n::_('Enter password') ?>">
</div>
<button type="submit" class="btn btn-success btn-block"><span class="glyphicon glyphicon-off"></span> <?php echo I18n::_('Decrypt') ?></button>
</form>
@ -121,8 +121,8 @@ endif;
<div id="navbar" class="navbar-collapse collapse">
<ul class="nav navbar-nav">
<li id="loadingindicator" class="navbar-text hidden">
<span class="glyphicon glyphicon-upload" aria-hidden="true"></span>
<?php echo I18n::_('Uploading paste… Please wait.'), PHP_EOL; ?>
<span class="glyphicon glyphicon-time" aria-hidden="true"></span>
<?php echo I18n::_('Loading…'), PHP_EOL; ?>
</li>
<li>
<?php
@ -132,7 +132,7 @@ if ($isPage):
<span class="glyphicon glyphicon-upload" aria-hidden="true"></span> <?php echo I18n::_('Send'), PHP_EOL;
else:
?>
<button id="newbutton" type="button" class="reloadlink hidden btn btn-<?php echo $isDark ? 'warning' : 'default'; ?> navbar-btn">
<button id="newbutton" type="button" class="hidden btn btn-<?php echo $isDark ? 'warning' : 'default'; ?> navbar-btn">
<span class="glyphicon glyphicon-file" aria-hidden="true"></span> <?php echo I18n::_('New'), PHP_EOL;
endif;
?>
@ -198,7 +198,7 @@ if ($isCpct):
<?php
if ($DISCUSSION):
?>
<li id="opendisc" class="checkbox hidden">
<li id="opendiscussionoption" class="checkbox hidden">
<label>
<input type="checkbox" id="opendiscussion" name="opendiscussion"<?php
if ($OPENDISCUSSION):
@ -230,17 +230,6 @@ if ($isCpct):
?>
</ul>
<select id="pasteFormatter" name="pasteFormatter" class="hidden">
<?php
foreach ($FORMATTER as $key => $value):
?>
<option value="<?php echo $key; ?>"<?php
if ($key == $FORMATTERDEFAULT):
?> selected="selected"<?php
endif;
?>><?php echo $value; ?></option>
<?php
endforeach;
?>
</select>
</li>
<?php
@ -262,7 +251,7 @@ else:
if ($DISCUSSION):
?>
<li>
<div id="opendisc" class="navbar-text checkbox hidden">
<div id="opendiscussionoption" class="navbar-text checkbox hidden">
<label>
<input type="checkbox" id="opendiscussion" name="opendiscussion"<?php
if ($OPENDISCUSSION):
@ -295,6 +284,7 @@ if ($FILEUPLOAD):
<input type="file" id="file" name="file" />
</div>
</li>
<li id="customattachment" class="hidden"></li>
<li>
<a id="fileremovebutton" href="#">
<?php echo I18n::_('Remove attachment'), PHP_EOL; ?>
@ -383,99 +373,124 @@ if ($isCpct):
?></div><?php
endif;
?></nav>
<header class="container">
<main>
<section class="container">
<?php
if (strlen($NOTICE)):
?>
<div role="alert" class="alert alert-info">
<span class="glyphicon glyphicon-info-sign" aria-hidden="true"></span> <?php echo htmlspecialchars($NOTICE), PHP_EOL; ?>
</div>
<div role="alert" class="alert alert-info">
<span class="glyphicon glyphicon-info-sign" aria-hidden="true"></span>
<?php echo htmlspecialchars($NOTICE), PHP_EOL; ?>
</div>
<?php
endif;
?>
<div id="remainingtime" role="alert" class="hidden alert alert-info">
<span class="glyphicon glyphicon-info-sign" aria-hidden="true"></span>
</div>
<div id="remainingtime" role="alert" class="hidden alert alert-info">
<span class="glyphicon glyphicon-fire" aria-hidden="true"></span>
</div>
<?php
if ($FILEUPLOAD):
?>
<div id="attachment" role="alert" class="hidden alert alert-info">
<span class="glyphicon glyphicon-info-sign" aria-hidden="true"></span> <a><?php echo I18n::_('Download attachment'); ?></a> <span id="clonedfile" class="hidden"><?php echo I18n::_('Cloned file attached.'); ?></span>
</div>
<?php
endif;
if (strlen($STATUS)):
?>
<div id="status" role="alert" class="alert alert-success">
<span class="glyphicon glyphicon-ok" aria-hidden="true"></span> <?php echo htmlspecialchars($STATUS), PHP_EOL; ?>
</div>
<div id="attachment" role="alert" class="hidden alert alert-info">
<span class="glyphicon glyphicon-download-alt" aria-hidden="true"></span>
<a class="alert-link"><?php echo I18n::_('Download attachment'), PHP_EOL; ?></a>
</div>
<?php
endif;
?>
<div id="errormessage" role="alert" class="<?php
if (!strlen($ERROR)):
?>hidden <?php
endif;
?>alert alert-danger"><span class="glyphicon glyphicon-alert" aria-hidden="true"></span> <?php echo htmlspecialchars($ERROR); ?></div>
<noscript><div id="noscript" role="alert" class="nonworking alert alert-<?php echo $isDark ? 'error' : 'warning'; ?>"><span class="glyphicon glyphicon-exclamation-sign" aria-hidden="true"></span> <?php echo I18n::_('JavaScript is required for %s to work.<br />Sorry for the inconvenience.', I18n::_($NAME)); ?></div></noscript>
<div id="oldienotice" role="alert" class="hidden nonworking alert alert-danger"><span class="glyphicon glyphicon-alert" aria-hidden="true"></span> <?php echo I18n::_('%s requires a modern browser to work.', I18n::_($NAME)); ?></div>
<div id="ienotice" role="alert" class="hidden alert alert-<?php echo $isDark ? 'error' : 'warning'; ?>"><span class="glyphicon glyphicon-question-sign" aria-hidden="true"></span> <?php echo I18n::_('Still using Internet Explorer? Do yourself a favor, switch to a modern browser:'), PHP_EOL; ?>
<a href="https://www.mozilla.org/firefox/">Firefox</a>,
<a href="https://www.opera.com/">Opera</a>,
<a href="https://www.google.com/chrome">Chrome</a>,
<a href="https://www.apple.com/safari">Safari</a>...
</div>
<div id="pasteresult" role="alert" class="hidden alert alert-success">
<span class="glyphicon glyphicon-ok" aria-hidden="true"></span>
<div id="deletelink"></div>
<div id="pastelink">
<div id="status" role="alert" class="statusmessage alert alert-info<?php echo empty($STATUS) ? ' hidden' : '' ?>">
<span class="glyphicon glyphicon-info-sign" aria-hidden="true"></span>
<?php echo htmlspecialchars($STATUS), PHP_EOL; ?>
</div>
<div id="errormessage" role="alert" class="statusmessage<?php echo empty($ERROR) ? ' hidden' : '' ?> alert alert-danger">
<span class="glyphicon glyphicon-alert" aria-hidden="true"></span>
<?php echo htmlspecialchars($ERROR), PHP_EOL; ?>
</div>
<noscript>
<div id="noscript" role="alert" class="nonworking alert alert-<?php echo $isDark ? 'error' : 'warning'; ?>">
<span class="glyphicon glyphicon-exclamation-sign" aria-hidden="true"></span>
<?php echo I18n::_('JavaScript is required for %s to work.<br />Sorry for the inconvenience.', I18n::_($NAME)), PHP_EOL; ?>
</div>
</noscript>
<div id="oldienotice" role="alert" class="hidden nonworking alert alert-danger">
<span class="glyphicon glyphicon-alert" aria-hidden="true"></span>
<?php echo I18n::_('%s requires a modern browser to work.', I18n::_($NAME)), PHP_EOL; ?>
</div>
<div id="ienotice" role="alert" class="hidden alert alert-<?php echo $isDark ? 'error' : 'warning'; ?>">
<span class="glyphicon glyphicon-question-sign" aria-hidden="true"></span>
<?php echo I18n::_('Still using Internet Explorer? Do yourself a favor, switch to a modern browser:'), PHP_EOL; ?>
<a href="https://www.mozilla.org/firefox/">Firefox</a>,
<a href="https://www.opera.com/">Opera</a>,
<a href="https://www.google.com/chrome">Chrome</a>
</div>
<div id="pasteSuccess" role="alert" class="hidden alert alert-success">
<span class="glyphicon glyphicon-ok" aria-hidden="true"></span>
<div id="deletelink"></div>
<div id="pastelink">
<?php
if (strlen($URLSHORTENER)):
?>
<button id="shortenbutton" data-shortener="<?php echo htmlspecialchars($URLSHORTENER); ?>" type="button" class="btn btn-<?php echo $isDark ? 'warning' : 'primary'; ?>">
<span class="glyphicon glyphicon-send" aria-hidden="true"></span> <?php echo I18n::_('Shorten URL'), PHP_EOL; ?>
</button>
<button id="shortenbutton" data-shortener="<?php echo htmlspecialchars($URLSHORTENER); ?>" type="button" class="btn btn-<?php echo $isDark ? 'warning' : 'primary'; ?>">
<span class="glyphicon glyphicon-send" aria-hidden="true"></span> <?php echo I18n::_('Shorten URL'), PHP_EOL; ?>
</button>
<?php
endif;
?>
</div>
</div>
</div>
<ul id="preview" class="nav nav-tabs hidden">
<li role="presentation" class="active"><a id="messageedit" href="#"><?php echo I18n::_('Editor'); ?></a></li>
<li role="presentation"><a id="messagepreview" href="#"><?php echo I18n::_('Preview'); ?></a></li>
</ul>
</header>
<section class="container">
<article class="row">
<div id="image" class="col-md-12 text-center hidden"></div>
<div id="prettymessage" class="col-md-12 hidden">
<pre id="prettyprint" class="col-md-12 prettyprint linenums:1"></pre>
<ul id="editorTabs" class="nav nav-tabs hidden">
<li role="presentation" class="active"><a id="messageedit" href="#"><?php echo I18n::_('Editor'); ?></a></li>
<li role="presentation"><a id="messagepreview" href="#"><?php echo I18n::_('Preview'); ?></a></li>
</ul>
</section>
<section class="container">
<article class="row">
<div id="placeholder" class="col-md-12 hidden"><?php echo I18n::_('+++ no paste text +++'); ?></div>
<div id="attachmentPreview" class="col-md-12 text-center hidden"></div>
<div id="prettymessage" class="col-md-12 hidden">
<pre id="prettyprint" class="col-md-12 prettyprint linenums:1"></pre>
</div>
<div id="plaintext" class="col-md-12 hidden"></div>
<p class="col-md-12"><textarea id="message" name="message" cols="80" rows="25" class="form-control hidden"></textarea></p>
</article>
</section>
<section class="container">
<div id="discussion" class="hidden">
<h4><?php echo I18n::_('Discussion'); ?></h4>
<div id="commentcontainer"></div>
</div>
<div id="cleartext" class="col-md-12 hidden"></div>
<p class="col-md-12"><textarea id="message" name="message" cols="80" rows="25" class="form-control hidden"></textarea></p>
</article>
</section>
<section class="container">
<div id="discussion" class="hidden">
<h4><?php echo I18n::_('Discussion'); ?></h4>
<div id="comments"></div>
</section>
<section class="container">
<div id="noscript" role="alert" class="nonworking alert alert-info noscript-hide">
<span class="glyphicon glyphicon-exclamation-sign" aria-hidden="true"></span>
<?php echo I18n::_('Loading…'); ?><br />
<span class="small"><?php echo I18n::_('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>.'); ?></span>
</div>
</section>
<footer class="container">
<div class="row">
<h4 class="col-md-5 col-xs-8"><?php echo I18n::_($NAME); ?> <small>- <?php echo I18n::_('Because ignorance is bliss'); ?></small></h4>
<p class="col-md-1 col-xs-4 text-center"><?php echo $VERSION; ?></p>
<p id="aboutbox" class="col-md-6 col-xs-12">
<?php echo I18n::_('%s is a minimalist, open source online pastebin where the server has zero knowledge of pasted data. Data is encrypted/decrypted <i>in the browser</i> using 256 bits AES. More information on the <a href="https://privatebin.info/">project page</a>.', I18n::_($NAME)), PHP_EOL; ?>
</p>
</div>
</footer>
</main>
<div id="serverdata" class="hidden" aria-hidden="true">
<div id="cipherdata"><?php echo htmlspecialchars($CIPHERDATA, ENT_NOQUOTES); ?></div>
<?php
if ($DISCUSSION):
?>
<div id="templates">
<!-- @TODO: when I intend/structure this corrrectly Firefox adds whitespaces everywhere which completly destroy the layout. (same possible when you remove the template data below and show this area in the browser) -->
<article id="commenttemplate" class="comment"><div class="commentmeta"><span class="nickname">name</span><span class="commentdate">0000-00-00</span></div><div class="commentdata">c</div><button class="btn btn-default btn-sm"><?php echo I18n::_('Reply'); ?></button></article>
<p id="commenttailtemplate" class="comment"><button class="btn btn-default btn-sm"><?php echo I18n::_('Add comment'); ?></button></p>
<div id="replytemplate" class="reply hidden"><input type="text" id="nickname" class="form-control" title="<?php echo I18n::_('Optional nickname…'); ?>" placeholder="<?php echo I18n::_('Optional nickname…'); ?>" /><textarea id="replymessage" class="replymessage form-control" cols="80" rows="7"></textarea><br /><div id="replystatus" role="alert" class="statusmessage hidden alert"><span class="glyphicon" aria-hidden="true"></span> </div><button id="replybutton" class="btn btn-default btn-sm"><?php echo I18n::_('Post comment'); ?></button></div>
</div>
</section>
<section class="container">
<div id="noscript" role="alert" class="nonworking alert alert-info noscript-hide"><span class="glyphicon glyphicon-exclamation-sign" aria-hidden="true">
<span> <?php echo I18n::_('Loading…'); ?></span><br>
<span class="small"><?php echo I18n::_('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>.'); ?></span>
</div>
</section>
<footer class="container">
<div class="row">
<h4 class="col-md-5 col-xs-8"><?php echo I18n::_($NAME); ?> <small>- <?php echo I18n::_('Because ignorance is bliss'); ?></small></h4>
<p class="col-md-1 col-xs-4 text-center"><?php echo $VERSION; ?></p>
<p id="aboutbox" class="col-md-6 col-xs-12">
<?php echo I18n::_('%s is a minimalist, open source online pastebin where the server has zero knowledge of pasted data. Data is encrypted/decrypted <i>in the browser</i> using 256 bits AES. More information on the <a href="https://privatebin.info/">project page</a>.', I18n::_($NAME)), PHP_EOL; ?>
</p>
</div>
</footer>
<div id="cipherdata" class="hidden"><?php echo htmlspecialchars($CIPHERDATA, ENT_NOQUOTES); ?></div>
<?php
endif;
?>
</div>
</body>
</html>

View File

@ -47,7 +47,7 @@ if ($MARKDOWN):
<?php
endif;
?>
<script type="text/javascript" src="js/privatebin.js?<?php echo rawurlencode($VERSION); ?>" integrity="sha512-Ap3kgrXsOa+7xANzug/xGKb3rEOzCYuJoxUG5vmwYRWn/8wI9Cc/RYZehKnFwHuFCFnQzfqJ9GT2D1HSOdnd5g==" crossorigin="anonymous"></script>
<script type="text/javascript" src="js/privatebin.js?<?php echo rawurlencode($VERSION); ?>" integrity="sha512-xfoNRo43UxDcd7m1P2IsvSPcOeGJo1oUjbA+CNCBoBilKRU3Iuqbf7awv3knP50bX7PVGEjtS6yJ2KtOtqnhTA==" crossorigin="anonymous"></script>
<!--[if lt IE 10]>
<style type="text/css">body {padding-left:60px;padding-right:60px;} #ienotice {display:block;} #oldienotice {display:block;}</style>
<![endif]-->
@ -79,12 +79,12 @@ endif;
<div id="ienotice"><?php echo I18n::_('Still using Internet Explorer? Do yourself a favor, switch to a modern browser:'), PHP_EOL; ?>
<a href="https://www.mozilla.org/firefox/">Firefox</a>,
<a href="https://www.opera.com/">Opera</a>,
<a href="https://www.google.com/chrome">Chrome</a>,
<a href="https://www.apple.com/safari">Safari</a>...
<a href="https://www.google.com/chrome">Chrome</a>
</div>
</header>
<section>
<article>
<div id="loadingindicator" class="hidden"><?php echo I18n::_('Loading…'); ?></div>
<div id="status"><?php echo htmlspecialchars($STATUS); ?></div>
<div id="errormessage" class="hidden"><?php echo htmlspecialchars($ERROR); ?></div>
<div id="toolbar">
@ -125,7 +125,7 @@ endif;
<?php
if ($DISCUSSION):
?>
<div id="opendisc" class="button hidden">
<div id="opendiscussionoption" class="button hidden">
<input type="checkbox" id="opendiscussion" name="opendiscussion"<?php
if ($OPENDISCUSSION):
?> checked="checked"<?php
@ -216,17 +216,31 @@ endif;
<div id="prettymessage" class="hidden">
<pre id="prettyprint" class="prettyprint linenums:1"></pre>
</div>
<div id="cleartext" class="hidden"></div>
<div id="plaintext" class="hidden"></div>
<textarea id="message" name="message" cols="80" rows="25" class="hidden"></textarea>
</article>
</section>
<section>
<div id="discussion" class="hidden">
<h4 class="title"><?php echo I18n::_('Discussion'); ?></h4>
<div id="comments"></div>
<div id="commentcontainer"></div>
</div>
</section>
<div id="cipherdata" class="hidden"><?php echo htmlspecialchars($CIPHERDATA, ENT_NOQUOTES); ?></div>
<div id="serverdata" class="hidden" aria-hidden="true">
<div id="cipherdata" class="hidden"><?php echo htmlspecialchars($CIPHERDATA, ENT_NOQUOTES); ?></div>
<?php
if ($DISCUSSION):
?>
<div id="templates">
<!-- @TODO: when I intend/structure this corrrectly Firefox adds whitespaces everywhere which completly destroy the layout. (same possible when you remove the template data below and show this area in the browser) -->
<article id="commenttemplate" class="comment"><div class="commentmeta"><span class="nickname">name</span><span class="commentdate">0000-00-00</span></div><div class="commentdata">c</div><button class="btn btn-default btn-sm"><?php echo I18n::_('Reply'); ?></button></article>
<div id="commenttailtemplate" class="comment"><button class="btn btn-default btn-sm"><?php echo I18n::_('Add comment'); ?></button></div>
<div id="replytemplate" class="reply hidden"><input type="text" id="nickname" class="form-control" title="<?php echo I18n::_('Optional nickname…'); ?>" placeholder="<?php echo I18n::_('Optional nickname…'); ?>" /><textarea id="replymessage" class="replymessage form-control" cols="80" rows="7"></textarea><br /><div id="replystatus" role="alert" class="statusmessage hidden alert"><span class="glyphicon" aria-hidden="true"></span> </div><button id="replybutton" class="btn btn-default btn-sm"><?php echo I18n::_('Post comment'); ?></button></div>
</div>
<?php
endif;
?>
</div>
<section class="container">
<div id="noscript" role="alert" class="nonworking alert alert-info noscript-hide"><span class="glyphicon glyphicon-exclamation-sign" aria-hidden="true">
<span> <?php echo I18n::_('Loading…'); ?></span><br>

1
tst/.gitignore vendored
View File

@ -1 +0,0 @@
/ConfigurationCombinationsTest.php

View File

@ -1,2 +0,0 @@
Allow from none
Deny from all

View File

@ -172,22 +172,24 @@ class Helper
*/
public static function rmDir($path)
{
$path .= DIRECTORY_SEPARATOR;
$dir = dir($path);
while (false !== ($file = $dir->read())) {
if ($file != '.' && $file != '..') {
if (is_dir($path . $file)) {
self::rmDir($path . $file);
} elseif (is_file($path . $file)) {
if (!unlink($path . $file)) {
throw new Exception('Error deleting file "' . $path . $file . '".');
if (is_dir($path)) {
$path .= DIRECTORY_SEPARATOR;
$dir = dir($path);
while (false !== ($file = $dir->read())) {
if ($file != '.' && $file != '..') {
if (is_dir($path . $file)) {
self::rmDir($path . $file);
} elseif (is_file($path . $file)) {
if (!unlink($path . $file)) {
throw new Exception('Error deleting file "' . $path . $file . '".');
}
}
}
}
}
$dir->close();
if (!rmdir($path)) {
throw new Exception('Error deleting directory "' . $path . '".');
$dir->close();
if (!rmdir($path)) {
throw new Exception('Error deleting directory "' . $path . '".');
}
}
}

View File

@ -8,16 +8,26 @@ class FilesystemTest extends PHPUnit_Framework_TestCase
private $_path;
private $_invalidPath;
public function setUp()
{
/* Setup Routine */
$this->_path = sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'privatebin_data';
$this->_model = Filesystem::getInstance(array('dir' => $this->_path));
$this->_path = sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'privatebin_data';
$this->_invalidPath = $this->_path . DIRECTORY_SEPARATOR . 'bar';
$this->_model = Filesystem::getInstance(array('dir' => $this->_path));
if (!is_dir($this->_path)) {
mkdir($this->_path);
}
if (!is_dir($this->_invalidPath)) {
mkdir($this->_invalidPath);
}
}
public function tearDown()
{
/* Tear Down Routine */
chmod($this->_invalidPath, 0700);
Helper::rmDir($this->_path);
}
@ -37,6 +47,7 @@ class FilesystemTest extends PHPUnit_Framework_TestCase
$this->assertFalse($this->_model->existsComment(Helper::getPasteId(), Helper::getPasteId(), Helper::getCommentId()), 'comment does not yet exist');
$this->assertTrue($this->_model->createComment(Helper::getPasteId(), Helper::getPasteId(), Helper::getCommentId(), Helper::getComment()), 'store comment');
$this->assertTrue($this->_model->existsComment(Helper::getPasteId(), Helper::getPasteId(), Helper::getCommentId()), 'comment exists after storing it');
$this->assertFalse($this->_model->createComment(Helper::getPasteId(), Helper::getPasteId(), Helper::getCommentId(), Helper::getComment()), 'unable to store the same comment twice');
$comment = json_decode(json_encode(Helper::getComment()));
$comment->id = Helper::getCommentId();
$comment->parentid = Helper::getPasteId();
@ -99,10 +110,6 @@ class FilesystemTest extends PHPUnit_Framework_TestCase
}
}
/**
* @expectedException Exception
* @expectedExceptionCode 90
*/
public function testErrorDetection()
{
$this->_model->delete(Helper::getPasteId());
@ -112,10 +119,6 @@ class FilesystemTest extends PHPUnit_Framework_TestCase
$this->assertFalse($this->_model->exists(Helper::getPasteId()), 'paste does still not exist');
}
/**
* @expectedException Exception
* @expectedExceptionCode 90
*/
public function testCommentErrorDetection()
{
$this->_model->delete(Helper::getPasteId());

View File

@ -4,14 +4,6 @@ use PrivateBin\Filter;
class FilterTest extends PHPUnit_Framework_TestCase
{
public function testFilterStripsSlashesDeeply()
{
$this->assertEquals(
array("f'oo", "b'ar", array("fo'o", "b'ar")),
Filter::stripslashesDeep(array("f\\'oo", "b\\'ar", array("fo\\'o", "b\\'ar")))
);
}
public function testFilterMakesTimesHumanlyReadable()
{
$this->assertEquals('5 minutes', Filter::formatHumanReadableTime('5min'));

View File

@ -98,6 +98,7 @@ class JsonApiTest extends PHPUnit_Framework_TestCase
new PrivateBin;
$content = ob_get_contents();
ob_end_clean();
unlink($file);
$response = json_decode($content, true);
$this->assertEquals(0, $response['status'], 'outputs status');
$this->assertEquals(Helper::getPasteId(), $response['id'], 'outputted paste ID matches input');
@ -132,6 +133,7 @@ class JsonApiTest extends PHPUnit_Framework_TestCase
new PrivateBin;
$content = ob_get_contents();
ob_end_clean();
unlink($file);
$response = json_decode($content, true);
$this->assertEquals(0, $response['status'], 'outputs status');
$this->assertFalse($this->_model->exists(Helper::getPasteId()), 'paste successfully deleted');
@ -147,10 +149,9 @@ class JsonApiTest extends PHPUnit_Framework_TestCase
$this->assertTrue($this->_model->exists(Helper::getPasteId()), 'paste exists before deleting data');
$paste = $this->_model->read(Helper::getPasteId());
$_POST = array(
'action' => 'delete',
'pasteid' => Helper::getPasteId(),
'deletetoken' => hash_hmac('sha256', Helper::getPasteId(), $paste->meta->salt),
);
$_SERVER['QUERY_STRING'] = Helper::getPasteId();
$_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
$_SERVER['REQUEST_METHOD'] = 'POST';
ob_start();

View File

@ -82,6 +82,7 @@ class ModelTest extends PHPUnit_Framework_TestCase
$comment = $paste->getComment(Helper::getPasteId());
$comment->setData($commentData['data']);
$comment->setNickname($commentData['meta']['nickname']);
$comment->getParentId();
$comment->store();
$comment = $paste->getComment(Helper::getPasteId(), Helper::getCommentId());
@ -189,6 +190,27 @@ class ModelTest extends PHPUnit_Framework_TestCase
$this->assertFalse(Paste::isValidId('../bar/baz'), 'path attack');
}
/**
* @expectedException Exception
* @expectedExceptionCode 64
*/
public function testInvalidPaste()
{
$this->_model->getPaste(Helper::getPasteId())->delete();
$paste = $this->_model->getPaste(Helper::getPasteId());
$paste->get();
}
/**
* @expectedException Exception
* @expectedExceptionCode 61
*/
public function testInvalidData()
{
$paste = $this->_model->getPaste();
$paste->setData('');
}
/**
* @expectedException Exception
* @expectedExceptionCode 62
@ -199,6 +221,37 @@ class ModelTest extends PHPUnit_Framework_TestCase
$paste->getComment(Helper::getPasteId());
}
/**
* @expectedException Exception
* @expectedExceptionCode 67
*/
public function testInvalidCommentDeletedPaste()
{
$pasteData = Helper::getPaste();
$paste = $this->_model->getPaste(Helper::getPasteId());
$paste->setData($pasteData['data']);
$paste->store();
$comment = $paste->getComment(Helper::getPasteId());
$paste->delete();
$comment->store();
}
/**
* @expectedException Exception
* @expectedExceptionCode 68
*/
public function testInvalidCommentData()
{
$pasteData = Helper::getPaste();
$paste = $this->_model->getPaste(Helper::getPasteId());
$paste->setData($pasteData['data']);
$paste->store();
$comment = $paste->getComment(Helper::getPasteId());
$comment->store();
}
public function testExpiration()
{
$pasteData = Helper::getPaste();

View File

@ -140,21 +140,18 @@ class PrivateBinTest extends PHPUnit_Framework_TestCase
public function testHtaccess()
{
$this->reset();
$dirs = array('cfg', 'lib');
foreach ($dirs as $dir) {
$file = PATH . $dir . DIRECTORY_SEPARATOR . '.htaccess';
@unlink($file);
}
$file = $this->_path . DIRECTORY_SEPARATOR . '.htaccess';
@unlink($file);
$_POST = Helper::getPaste();
$_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
$_SERVER['REQUEST_METHOD'] = 'POST';
$_SERVER['REMOTE_ADDR'] = '::1';
ob_start();
new PrivateBin;
ob_end_clean();
foreach ($dirs as $dir) {
$file = PATH . $dir . DIRECTORY_SEPARATOR . '.htaccess';
$this->assertFileExists(
$file,
"$dir htaccess recreated"
);
}
$this->assertFileExists($file, 'htaccess recreated');
}
/**
@ -739,10 +736,10 @@ class PrivateBinTest extends PHPUnit_Framework_TestCase
new PrivateBin;
$content = ob_get_contents();
ob_end_clean();
$this->assertContains(
'<div id="cipherdata" class="hidden">' .
htmlspecialchars(Helper::getPasteAsJson(), ENT_NOQUOTES) .
'</div>',
$this->assertRegExp(
'#<div id="cipherdata"[^>]*>' .
preg_quote(htmlspecialchars(Helper::getPasteAsJson(), ENT_NOQUOTES)) .
'</div>#',
$content,
'outputs data correctly'
);
@ -760,7 +757,7 @@ class PrivateBinTest extends PHPUnit_Framework_TestCase
$content = ob_get_contents();
ob_end_clean();
$this->assertRegExp(
'#<div[^>]*id="errormessage"[^>]*>.*Invalid paste ID\.</div>#',
'#<div[^>]*id="errormessage"[^>]*>.*Invalid paste ID\.#s',
$content,
'outputs error correctly'
);
@ -778,7 +775,7 @@ class PrivateBinTest extends PHPUnit_Framework_TestCase
$content = ob_get_contents();
ob_end_clean();
$this->assertRegExp(
'#<div[^>]*id="errormessage"[^>]*>.*Paste does not exist[^<]*</div>#',
'#<div[^>]*id="errormessage"[^>]*>.*Paste does not exist, has expired or has been deleted\.#s',
$content,
'outputs error correctly'
);
@ -798,7 +795,7 @@ class PrivateBinTest extends PHPUnit_Framework_TestCase
$content = ob_get_contents();
ob_end_clean();
$this->assertRegExp(
'#<div[^>]*id="errormessage"[^>]*>.*Paste does not exist[^<]*</div>#',
'#<div[^>]*id="errormessage"[^>]*>.*Paste does not exist, has expired or has been deleted\.#s',
$content,
'outputs error correctly'
);
@ -818,10 +815,10 @@ class PrivateBinTest extends PHPUnit_Framework_TestCase
$content = ob_get_contents();
ob_end_clean();
unset($burnPaste['meta']['salt']);
$this->assertContains(
'<div id="cipherdata" class="hidden">' .
htmlspecialchars(Helper::getPasteAsJson($burnPaste['meta']), ENT_NOQUOTES) .
'</div>',
$this->assertRegExp(
'#<div id="cipherdata"[^>]*>' .
preg_quote(htmlspecialchars(Helper::getPasteAsJson($burnPaste['meta']), ENT_NOQUOTES)) .
'</div>#',
$content,
'outputs data correctly'
);
@ -889,10 +886,10 @@ class PrivateBinTest extends PHPUnit_Framework_TestCase
$content = ob_get_contents();
ob_end_clean();
$meta['formatter'] = 'syntaxhighlighting';
$this->assertContains(
'<div id="cipherdata" class="hidden">' .
htmlspecialchars(Helper::getPasteAsJson($meta), ENT_NOQUOTES) .
'</div>',
$this->assertRegExp(
'#<div id="cipherdata"[^>]*>' .
preg_quote(htmlspecialchars(Helper::getPasteAsJson($meta), ENT_NOQUOTES)) .
'</div>#',
$content,
'outputs data correctly'
);
@ -914,10 +911,10 @@ class PrivateBinTest extends PHPUnit_Framework_TestCase
ob_end_clean();
$oldPaste['meta']['formatter'] = 'plaintext';
unset($oldPaste['meta']['salt']);
$this->assertContains(
'<div id="cipherdata" class="hidden">' .
htmlspecialchars(Helper::getPasteAsJson($oldPaste['meta']), ENT_NOQUOTES) .
'</div>',
$this->assertRegExp(
'#<div id="cipherdata"[^>]*>' .
preg_quote(htmlspecialchars(Helper::getPasteAsJson($oldPaste['meta']), ENT_NOQUOTES)) .
'</div>#',
$content,
'outputs data correctly'
);
@ -939,7 +936,7 @@ class PrivateBinTest extends PHPUnit_Framework_TestCase
$content = ob_get_contents();
ob_end_clean();
$this->assertRegExp(
'#<div[^>]*id="status"[^>]*>.*Paste was properly deleted[^<]*</div>#s',
'#<div[^>]*id="status"[^>]*>.*Paste was properly deleted\.#s',
$content,
'outputs deleted status correctly'
);
@ -960,7 +957,7 @@ class PrivateBinTest extends PHPUnit_Framework_TestCase
$content = ob_get_contents();
ob_end_clean();
$this->assertRegExp(
'#<div[^>]*id="errormessage"[^>]*>.*Invalid paste ID\.</div>#',
'#<div[^>]*id="errormessage"[^>]*>.*Invalid paste ID\.#s',
$content,
'outputs delete error correctly'
);
@ -980,7 +977,7 @@ class PrivateBinTest extends PHPUnit_Framework_TestCase
$content = ob_get_contents();
ob_end_clean();
$this->assertRegExp(
'#<div[^>]*id="errormessage"[^>]*>.*Paste does not exist[^<]*</div>#',
'#<div[^>]*id="errormessage"[^>]*>.*Paste does not exist, has expired or has been deleted\.#s',
$content,
'outputs delete error correctly'
);
@ -1000,7 +997,7 @@ class PrivateBinTest extends PHPUnit_Framework_TestCase
$content = ob_get_contents();
ob_end_clean();
$this->assertRegExp(
'#<div[^>]*id="errormessage"[^>]*>.*Wrong deletion token[^<]*</div>#',
'#<div[^>]*id="errormessage"[^>]*>.*Wrong deletion token\. Paste was not deleted\.#s',
$content,
'outputs delete error correctly'
);
@ -1047,7 +1044,7 @@ class PrivateBinTest extends PHPUnit_Framework_TestCase
ob_end_clean();
$response = json_decode($content, true);
$this->assertEquals(1, $response['status'], 'outputs status');
$this->assertTrue($this->_model->exists(Helper::getPasteId()), 'paste successfully deleted');
$this->assertTrue($this->_model->exists(Helper::getPasteId()), 'paste exists after failing to delete data');
}
/**
@ -1067,7 +1064,7 @@ class PrivateBinTest extends PHPUnit_Framework_TestCase
$content = ob_get_contents();
ob_end_clean();
$this->assertRegExp(
'#<div[^>]*id="errormessage"[^>]*>.*Paste does not exist[^<]*</div>#',
'#<div[^>]*id="errormessage"[^>]*>.*Paste does not exist, has expired or has been deleted\.#s',
$content,
'outputs error correctly'
);
@ -1091,7 +1088,7 @@ class PrivateBinTest extends PHPUnit_Framework_TestCase
$content = ob_get_contents();
ob_end_clean();
$this->assertRegExp(
'#<div[^>]*id="status"[^>]*>.*Paste was properly deleted[^<]*</div>#s',
'#<div[^>]*id="status"[^>]*>.*Paste was properly deleted\.#s',
$content,
'outputs deleted status correctly'
);

View File

@ -63,6 +63,7 @@ class RequestTest extends PHPUnit_Framework_TestCase
file_put_contents($file, 'data=foo');
Request::setInputStream($file);
$request = new Request;
unlink($file);
$this->assertTrue($request->isJsonApiCall(), 'is JSON Api call');
$this->assertEquals('create', $request->getOperation());
$this->assertEquals('foo', $request->getParam('data'));

View File

@ -1,11 +1,13 @@
<?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');

View File

@ -96,15 +96,15 @@ class ViewTest extends PHPUnit_Framework_TestCase
public function testTemplateRendersCorrectly()
{
foreach ($this->_content as $template => $content) {
$this->assertContains(
'<div id="cipherdata" class="hidden">' .
htmlspecialchars(Helper::getPaste()['data'], ENT_NOQUOTES) .
'</div>',
$this->assertRegExp(
'#<div[^>]+id="cipherdata"[^>]*>' .
preg_quote(htmlspecialchars(Helper::getPaste()['data'], ENT_NOQUOTES)) .
'</div>#',
$content,
$template . ': outputs data correctly'
);
$this->assertRegExp(
'#<div[^>]+id="errormessage"[^>]*>.*' . self::$error . '</div>#',
'#<div[^>]+id="errormessage"[^>]*>.*' . self::$error . '#s',
$content,
$template . ': outputs error correctly'
);
@ -119,7 +119,7 @@ class ViewTest extends PHPUnit_Framework_TestCase
$template . ': checked discussion if configured'
);
$this->assertRegExp(
'#<[^>]+id="opendisc"[^>]*>#',
'#<[^>]+id="opendiscussionoption"[^>]*>#',
$content,
$template . ': discussions available if configured'
);

1
vendor/.htaccess vendored Normal file
View File

@ -0,0 +1 @@
Require all denied