mirror of
https://github.com/PrivateBin/PrivateBin.git
synced 2026-04-19 21:58:08 -04:00
Compare commits
361 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 7ce853001d | |||
| 456ced37c2 | |||
| 3a7350c5c4 | |||
| f0d0daffcc | |||
| 21400d4928 | |||
| 91d51e075d | |||
| bd308a0d3b | |||
| f2e0c1a701 | |||
| ffab73a314 | |||
| 1dd53a93f4 | |||
| 835fbe0e2f | |||
| 11b16fc6fd | |||
| 2b509d0475 | |||
| 40d35ab3c2 | |||
| 82be7c6354 | |||
| 75dc346f0f | |||
| 960faf4417 | |||
| 7ad677921b | |||
| b6db556b34 | |||
| 36cb37c029 | |||
| 5617612eb3 | |||
| 2a4d572c1e | |||
| 6c1f0dde0c | |||
| 1807580226 | |||
| cc60ab701b | |||
| 389b07bd2d | |||
| e9d6996db4 | |||
| a58bba0958 | |||
| abaa9eca35 | |||
| 3e02818335 | |||
| 6b001b5e4a | |||
| ccdb26df51 | |||
| bef5c647cf | |||
| 2b46fdd626 | |||
| f83f80b5f6 | |||
| f39934a104 | |||
| 4c8d23d3a5 | |||
| fe89161848 | |||
| d544d5e763 | |||
| 094c96afc6 | |||
| 247992fbca | |||
| 77153a9b49 | |||
| 110962bc8e | |||
| d73cfb093c | |||
| 288cf3f005 | |||
| a62f29f052 | |||
| 9b9be50678 | |||
| 30c0d22468 | |||
| 004e2dd75c | |||
| d5d06caf40 | |||
| 7a6f36a789 | |||
| a0f8a667ae | |||
| fbf0eae513 | |||
| 0e3a7196f9 | |||
| f987e96d4b | |||
| 1054319313 | |||
| 6b59d4f380 | |||
| 1034d4038e | |||
| dbe8debe30 | |||
| 190a35a53b | |||
| 91041d8c59 | |||
| d764c03759 | |||
| 7277d2bb43 | |||
| c8c6a67530 | |||
| 9443900f66 | |||
| 76bc8590a6 | |||
| 6cc47e6073 | |||
| 2d7f5e9a9f | |||
| 6307c01cc6 | |||
| a200f8875c | |||
| 75eede9870 | |||
| e822673e90 | |||
| 9df6754dfa | |||
| f5f07b9288 | |||
| 75a86e2594 | |||
| 4758fd683a | |||
| 186dd82653 | |||
| 8faf0501f4 | |||
| 832f000576 | |||
| 6da1fa04c3 | |||
| 0c0be618e0 | |||
| 8b0ab8ee49 | |||
| 49d0b35dc8 | |||
| 86da334e8f | |||
| b21d5afa2d | |||
| 5b5f1e3aa5 | |||
| 34923addd7 | |||
| 15374f99ae | |||
| 1d6bcb1f57 | |||
| 77dd0b027f | |||
| 902e7cf480 | |||
| 08d8922aad | |||
| 79abba5124 | |||
| cadfb09193 | |||
| 401cd32d07 | |||
| f3b14225ba | |||
| 3b472fdd41 | |||
| bb3af09c6c | |||
| 57b03d438e | |||
| 1a7ac272eb | |||
| 1b2b183d7f | |||
| 1f872167c1 | |||
| 29ffd25c18 | |||
| d43dfdefdf | |||
| 1d20eee169 | |||
| 53c0e4976b | |||
| 0333777a37 | |||
| f4438a0103 | |||
| 55db9426b9 | |||
| 535f038daa | |||
| 0c4852c099 | |||
| b8e8755fb1 | |||
| 0b6af67b99 | |||
| 56c54dd880 | |||
| a8e1c33b54 | |||
| 0cc2b67753 | |||
| 4f051fe5a5 | |||
| 8d63921924 | |||
| 0be55e05bf | |||
| b133c2e233 | |||
| b54308a77e | |||
| 47deaeb7ca | |||
| 83336b2949 | |||
| 35ef64ff79 | |||
| c725b4f0fe | |||
| 585d5db983 | |||
| 2182cdd44f | |||
| 041ef7f7a5 | |||
| 6a489d35ab | |||
| ee99952d90 | |||
| 4f09873f94 | |||
| ca999b15ed | |||
| eef4f8be3f | |||
| ff19d052b7 | |||
| e53090a937 | |||
| 74e1f18ae0 | |||
| 02fcc0ba53 | |||
| e36a94cfdd | |||
| b80b318e38 | |||
| 1fff4bf4d7 | |||
| 20d39347c6 | |||
| aa6e2f7631 | |||
| 3666db3b9a | |||
| 5ba8266eb8 | |||
| 86c5dc9db9 | |||
| af852927a9 | |||
| 0fb104b102 | |||
| f6421c9c7c | |||
| 2d1552a345 | |||
| 8085604385 | |||
| 31cdfc7be6 | |||
| 307443dac3 | |||
| c5fd6028b5 | |||
| c7cd450f9b | |||
| a988be7431 | |||
| 981304848f | |||
| 51a590c3c7 | |||
| f4e68fcc04 | |||
| f43a41c117 | |||
| ab11fbeb47 | |||
| 5f4fe52eab | |||
| b80732f8e2 | |||
| a372ee92e9 | |||
| e2ae0da4e1 | |||
| 3f7bceb862 | |||
| 507a10adc5 | |||
| a8f7840d25 | |||
| 3ba6483bf3 | |||
| fba8384ac3 | |||
| def58480b3 | |||
| cb5ba022ba | |||
| c5c3a0e743 | |||
| 3c068cd6c3 | |||
| 82f1431440 | |||
| df2f5931cd | |||
| ff3b668958 | |||
| eb10d4d35e | |||
| 18972ae0fa | |||
| e7fb9ac54c | |||
| 08816a1d71 | |||
| ea663f7491 | |||
| 5f28acf629 | |||
| 5f2daa5cd6 | |||
| 1fd998f325 | |||
| 9c09018e6e | |||
| be164bb6a9 | |||
| fd08d991fe | |||
| 3d9ba10fcb | |||
| ae1e4e3edb | |||
| af54e70359 | |||
| 3327645fd4 | |||
| b72994f2e0 | |||
| b4c75b541b | |||
| 9357f122b7 | |||
| d0248d55d3 | |||
| 078c5785dd | |||
| 68b097087d | |||
| fa4fe2852d | |||
| bbcf57de0e | |||
| f04043a399 | |||
| 1f2dddd9d8 | |||
| 93135e0abf | |||
| e294145a2b | |||
| cc6fb1c0c3 | |||
| 1b88eef356 | |||
| 5af069b4f0 | |||
| 1232717334 | |||
| 7b2f0ff302 | |||
| a203e6322b | |||
| 7901ec74a7 | |||
| b5a6ce323e | |||
| 3429d293d3 | |||
| ae486d651b | |||
| f46221e7c3 | |||
| 55efc858b5 | |||
| 7bdcc2ae15 | |||
| 1a7d0799c0 | |||
| de8f40ac1a | |||
| c758eca0a4 | |||
| 9beb176874 | |||
| 9b19a88bb9 | |||
| d699c41e26 | |||
| 2bc54caa07 | |||
| a2ffbafa13 | |||
| 197c4a34e8 | |||
| 7a3a306ddc | |||
| cbdcaf4c30 | |||
| f635881bd4 | |||
| 371dca1986 | |||
| 7311e9ce12 | |||
| abb2b90e9b | |||
| edb8e5e078 | |||
| ffe48092fe | |||
| 8bc97517fb | |||
| 3df6b62d22 | |||
| 93138cbbae | |||
| fc5e380ccc | |||
| 33587d54e4 | |||
| d355bb87e3 | |||
| 52b9b257c3 | |||
| b939b64778 | |||
| de4bada695 | |||
| 5c5ae967a8 | |||
| 342270d6dd | |||
| dae093bbfa | |||
| 7de12d64d5 | |||
| b6460616ba | |||
| 7a1de52e05 | |||
| 91c8f9f23c | |||
| 84771d7167 | |||
| 3dd01b1f70 | |||
| 89f6f0051d | |||
| 194b27e685 | |||
| e32e3979a8 | |||
| af5a14afc3 | |||
| 5812a6bb68 | |||
| f339c0b1c0 | |||
| 502bb5fa15 | |||
| 89bdc92451 | |||
| 63d6816c7c | |||
| e572e9c79c | |||
| 2b0ebdb6c7 | |||
| 3a06d5a745 | |||
| 12aa325494 | |||
| a806a6455e | |||
| 4296b43832 | |||
| c3ad4a4b4d | |||
| 805eb288d9 | |||
| b21efd8336 | |||
| 7d82c82fd9 | |||
| 3f92d4c038 | |||
| 377d7d565b | |||
| e6def62581 | |||
| 17c1284ccf | |||
| 0a9cd05453 | |||
| 2b8534d49e | |||
| 472bf520d8 | |||
| 4d3a2ae946 | |||
| 4c329be95f | |||
| b47b8cf050 | |||
| 1b8351fef9 | |||
| d0c6ab224f | |||
| 53e23b7422 | |||
| 010f9db274 | |||
| c2c0980c57 | |||
| fcb6422663 | |||
| 993abd746e | |||
| 30228cc33c | |||
| 14ff704b28 | |||
| cd1b0e0a50 | |||
| 4a73afa057 | |||
| 63d20330b4 | |||
| 982a4f957c | |||
| 67fd327df4 | |||
| db0db4ebff | |||
| 4514f1f3a4 | |||
| 926fab30e9 | |||
| 492cdc9926 | |||
| 6b5e7c1b49 | |||
| 2bc7e8e38f | |||
| 48916d5df7 | |||
| 0887f567ab | |||
| 3e4def2069 | |||
| 39867d8151 | |||
| c7a86ebd5c | |||
| 56d993ca82 | |||
| 45b3ec4ac6 | |||
| 9bd04c55c9 | |||
| dd4633ff8f | |||
| c0207d00a2 | |||
| bd83415c82 | |||
| 478f806e9c | |||
| db402baa14 | |||
| dac5bd1d93 | |||
| 4b2f2920a2 | |||
| 83620d7eb5 | |||
| de4abad748 | |||
| 3ca01024fd | |||
| 5809a7cfa7 | |||
| 0e78534e48 | |||
| b68ae363ec | |||
| 3181cfe58a | |||
| bc11452259 | |||
| 853a4f386f | |||
| 9683c591bb | |||
| 47029fb04e | |||
| 735a77b783 | |||
| 5f4200c721 | |||
| 9b893f09d7 | |||
| 3b9b6c948f | |||
| 7b7a32c0a7 | |||
| fd7d05e862 | |||
| 8232dce395 | |||
| 6f3bb25b09 | |||
| 1dc8b24665 | |||
| ed66351337 | |||
| 9e6eb50ced | |||
| d727837324 | |||
| 175d14224e | |||
| 51f1f67fe8 | |||
| ab250d8686 | |||
| 1ff8637c23 | |||
| 727166e945 | |||
| e50f3eb311 | |||
| f5fa37b5f2 | |||
| 587822838a | |||
| 553417194c | |||
| 8a08a2167b | |||
| 3f75c81a2f | |||
| effe6ad3e5 | |||
| 8fbdb69d8a | |||
| b8594c174a | |||
| d847e2fcf2 | |||
| c152f85b50 | |||
| 819d25a74c | |||
| cea96ee12a | |||
| ef9780707a | |||
| 9ca041fa06 | |||
| 9327c9b58b | |||
| 5644001c53 | |||
| 91f78ecd0f |
@@ -16,8 +16,11 @@ js/test/ export-ignore
|
||||
.jshintrc export-ignore
|
||||
.nsprc export-ignore
|
||||
.php_cs export-ignore
|
||||
.scrutinizer.yml export-ignore
|
||||
.styleci.yml export-ignore
|
||||
.travis.yml export-ignore
|
||||
codacy-analysis.yml export-ignore
|
||||
crowdin.yml export-ignore
|
||||
composer.json export-ignore
|
||||
composer.lock export-ignore
|
||||
BADGES.md export-ignore
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
name: Refresh PHP 8 branch
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
schedule:
|
||||
- cron: '42 2 * * *'
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout php8 branch
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
# directly checkout the php8 branch
|
||||
ref: php8
|
||||
# Number of commits to fetch. 0 indicates all history for all branches and tags.
|
||||
# Default: 1
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Merge master changes into php8
|
||||
run: |
|
||||
git config user.name "github-actions[bot]"
|
||||
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
||||
git merge origin/master
|
||||
|
||||
- name: Push new changes
|
||||
uses: github-actions-x/commit@v2.8
|
||||
with:
|
||||
name: github-actions[bot]
|
||||
email: 41898282+github-actions[bot]@users.noreply.github.com
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
push-branch: 'php8'
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
# This is a basic workflow to help you get started with Actions
|
||||
|
||||
name: Snyk scan
|
||||
|
||||
on:
|
||||
# Triggers the workflow on push or pull request events but only for the master branch
|
||||
push:
|
||||
branches: [ master ]
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
jobs:
|
||||
# https://github.com/snyk/actions/tree/master/php
|
||||
snyk-php:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@master
|
||||
- name: Install Google Cloud Storage
|
||||
run: composer require --no-update google/cloud-storage && composer update --no-dev
|
||||
- name: Run Snyk to check for vulnerabilities
|
||||
uses: snyk/actions/php@master
|
||||
continue-on-error: true # To make sure that SARIF upload gets called
|
||||
env:
|
||||
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
|
||||
with:
|
||||
args: --sarif-file-output=snyk.sarif
|
||||
- name: Upload result to GitHub Code Scanning
|
||||
uses: github/codeql-action/upload-sarif@v1
|
||||
with:
|
||||
sarif_file: snyk.sarif
|
||||
@@ -2,6 +2,7 @@ name: Tests
|
||||
on: [push]
|
||||
|
||||
jobs:
|
||||
|
||||
Composer:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
@@ -10,42 +11,110 @@ jobs:
|
||||
- name: Validate composer.json and composer.lock
|
||||
run: composer validate
|
||||
- name: Install dependencies
|
||||
run: /usr/bin/php7.4 $(which composer) install --prefer-dist --no-suggest
|
||||
run: composer install --prefer-dist --no-dev
|
||||
|
||||
PHPunit:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
php-versions: ['5.6', '7.0', '7.1', '7.2', '7.3', '7.4']
|
||||
name: PHP ${{ matrix.php-versions }} unit tests on ${{ matrix.operating-system }}
|
||||
env:
|
||||
extensions: gd, sqlite3
|
||||
extensions-cache-key-name: phpextensions
|
||||
|
||||
steps:
|
||||
|
||||
# let's get started!
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
|
||||
# cache PHP extensions
|
||||
- name: Setup cache environment
|
||||
id: extcache
|
||||
uses: shivammathur/cache-extensions@v1
|
||||
with:
|
||||
php-version: ${{ matrix.php-versions }}
|
||||
extensions: ${{ env.extensions }}
|
||||
key: ${{ runner.os }}-${{ env.extensions-cache-key }}
|
||||
|
||||
- name: Cache extensions
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ${{ steps.extcache.outputs.dir }}
|
||||
key: ${{ steps.extcache.outputs.key }}
|
||||
restore-keys: ${{ runner.os }}-${{ env.extensions-cache-key }}
|
||||
|
||||
- name: Setup PHP
|
||||
uses: shivammathur/setup-php@v2
|
||||
with:
|
||||
php-version: ${{ matrix.php-versions }}
|
||||
extensions: gd, sqlite3
|
||||
extensions: ${{ env.extensions }}
|
||||
|
||||
# Setup GitHub CI PHP problem matchers
|
||||
# https://github.com/shivammathur/setup-php#problem-matchers
|
||||
- name: Setup problem matchers for PHP
|
||||
run: echo "::add-matcher::${{ runner.tool_cache }}/php.json"
|
||||
|
||||
- name: Setup problem matchers for PHPUnit
|
||||
run: echo "::add-matcher::${{ runner.tool_cache }}/phpunit.json"
|
||||
|
||||
# composer cache
|
||||
- name: Remove composer lock
|
||||
run: rm composer.lock
|
||||
|
||||
- name: Get composer cache directory
|
||||
id: composer-cache
|
||||
run: echo "::set-output name=dir::$(composer config cache-files-dir)"
|
||||
|
||||
# http://man7.org/linux/man-pages/man1/date.1.html
|
||||
# https://github.com/actions/cache#creating-a-cache-key
|
||||
- name: Get Date
|
||||
id: get-date
|
||||
run: |
|
||||
echo "::set-output name=date::$(/bin/date -u "+%Y%m%d")"
|
||||
shell: bash
|
||||
|
||||
- name: Cache dependencies
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ${{ steps.composer-cache.outputs.dir }}
|
||||
key: ${{ runner.os }}-composer-${{ steps.get-date.outputs.date }}-${{ hashFiles('**/composer.json') }}
|
||||
restore-keys: ${{ runner.os }}-composer-${{ steps.get-date.outputs.date }}-
|
||||
|
||||
# composer installation
|
||||
- name: Setup PHPunit
|
||||
run: composer install -n
|
||||
|
||||
- name: Install Google Cloud Storage
|
||||
run: composer require google/cloud-storage
|
||||
|
||||
# testing
|
||||
- name: Run unit tests
|
||||
run: ../vendor/bin/phpunit --no-coverage
|
||||
working-directory: tst
|
||||
|
||||
Mocha:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v1
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: '12'
|
||||
cache: 'npm'
|
||||
cache-dependency-path: 'js/package.json'
|
||||
|
||||
- name: Setup Mocha
|
||||
run: npm install -g mocha
|
||||
|
||||
- name: Setup Node modules
|
||||
run: npm install
|
||||
working-directory: js
|
||||
|
||||
- name: Run unit tests
|
||||
run: mocha
|
||||
working-directory: js
|
||||
|
||||
+3
-1
@@ -6,7 +6,7 @@ cfg/*
|
||||
!cfg/.htaccess
|
||||
|
||||
# Ignore data/
|
||||
data/
|
||||
/data/
|
||||
|
||||
# Ignore PhpDoc
|
||||
doc/*
|
||||
@@ -36,3 +36,5 @@ tst/ConfigurationCombinationsTest.php
|
||||
.project
|
||||
.externalToolBuilders
|
||||
.c9
|
||||
/.idea/
|
||||
*.iml
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
checks:
|
||||
php: true
|
||||
javascript: true
|
||||
filter:
|
||||
paths:
|
||||
- "css/privatebin.css"
|
||||
- "css/bootstrap/privatebin.css"
|
||||
- "js/privatebin.js"
|
||||
- "lib/*.php"
|
||||
- "index.php"
|
||||
coding_style:
|
||||
php:
|
||||
spaces:
|
||||
around_operators:
|
||||
additive: false
|
||||
concatenation: true
|
||||
build:
|
||||
environment:
|
||||
php:
|
||||
version: '7.2'
|
||||
tests:
|
||||
override:
|
||||
-
|
||||
command: 'composer require google/cloud-storage && cd tst && ../vendor/bin/phpunit'
|
||||
coverage:
|
||||
file: 'tst/log/coverage-clover.xml'
|
||||
format: 'clover'
|
||||
nodes:
|
||||
tests: true
|
||||
analysis:
|
||||
tests:
|
||||
override:
|
||||
-
|
||||
command: phpcs-run
|
||||
use_website_config: true
|
||||
- php-scrutinizer-run
|
||||
@@ -29,3 +29,9 @@ disabled:
|
||||
- short_array_syntax
|
||||
- single_line_after_imports
|
||||
- unalign_equals
|
||||
|
||||
finder:
|
||||
path:
|
||||
- "lib/"
|
||||
- "tpl/"
|
||||
- "tst/"
|
||||
|
||||
+25
-8
@@ -1,8 +1,25 @@
|
||||
# PrivateBin version history
|
||||
|
||||
* **1.4 (not yet released)**
|
||||
* **1.4 (2022-04-09)**
|
||||
* ADDED: Translations for Corsican, Estonian, Finnish and Lojban
|
||||
* ADDED: new HTTP headers improving security (#765)
|
||||
* ADDED: Download button for paste text (#774)
|
||||
* ADDED: Opt-out of federated learning of cohorts (FLoC) (#776)
|
||||
* ADDED: Configuration option to exempt IPs from the rate-limiter (#787)
|
||||
* ADDED: Google Cloud Storage backend support (#795)
|
||||
* ADDED: Oracle database support (#868)
|
||||
* ADDED: Configuration option to limit paste creation and commenting to certain IPs (#883)
|
||||
* ADDED: Set CSP also as meta tag, to deal with misconfigured webservers mangling the HTTP header
|
||||
* ADDED: Sanitize SVG preview, preventing script execution in instance context
|
||||
* CHANGED: Language selection cookie only transmitted over HTTPS (#472)
|
||||
* CHANGED: Upgrading libraries to: base-x 4.0.0, bootstrap 3.4.1 (JS), DOMpurify 2.3.6, ip-lib 1.18.0, jQuery 3.6.0, random_compat 2.0.21, Showdown 2.0.3 & zlib 1.2.12
|
||||
* CHANGED: Removed automatic `.ini` configuration file migration (#808)
|
||||
* CHANGED: Removed configurable `dir` for `traffic` & `purge` limiters (#419)
|
||||
* CHANGED: Server salt, traffic and purge limiter now stored in the storage backend (#419)
|
||||
* CHANGED: Drop support for attachment download in IE
|
||||
* FIXED: Error when attachments are disabled, but paste with attachment gets displayed
|
||||
* **1.3.5 (2021-04-05)**
|
||||
* ADDED: Translation for Hebrew, Lithuanian, Indonesian and Catalan
|
||||
* ADDED: Translations for Hebrew, Lithuanian, Indonesian and Catalan
|
||||
* ADDED: Make the project info configurable (#681)
|
||||
* CHANGED: Upgrading libraries to: DOMpurify 2.2.7, kjua 0.9.0 & random_compat 2.0.18
|
||||
* CHANGED: Open all links in new window (#630)
|
||||
@@ -43,7 +60,7 @@
|
||||
* FIXED: HTML injection via unescaped attachment filename (#554)
|
||||
* FIXED: Password disabling option (#527)
|
||||
* **1.2.2 (2020-01-11)**
|
||||
* CHANGED: Upgrading libraries to: bootstrap 3.4.1, DOMpurify 2.0.7, jQuery 3.4.1, kjua 0.6.0, Showdown 1.9.1 & SJCL 1.0.8
|
||||
* CHANGED: Upgrading libraries to: bootstrap 3.4.1 (CSS), DOMpurify 2.0.7, jQuery 3.4.1, kjua 0.6.0, Showdown 1.9.1 & SJCL 1.0.8
|
||||
* FIXED: HTML injection via unescaped attachment filename (#554)
|
||||
* **1.3.1 (2019-09-22)**
|
||||
* ADDED: Translation for Bulgarian (#455)
|
||||
@@ -54,7 +71,7 @@
|
||||
* CHANGED: Upgrading libraries to: DOMpurify 2.0.1
|
||||
* FIXED: Enabling browsers without WASM to create pastes and read uncompressed ones (#454)
|
||||
* FIXED: Cloning related issues (#489, #491, #493, #494)
|
||||
* FIXED: Enable file operation only when editing (#497)
|
||||
* FIXED: Enable file operation only when editing (#497)
|
||||
* FIXED: Clicking 'New' on a previously submitted paste does not blank address bar (#354)
|
||||
* FIXED: Clear address bar when create new paste from existing paste (#479)
|
||||
* FIXED: Discussion section not hiding when new/clone paste is clicked on (#484)
|
||||
@@ -217,7 +234,7 @@ encryption), i18n (translation, counterpart of i18n.php) and helper (stateless u
|
||||
* FIXED: 2 minor corrections to avoid notices in php log.
|
||||
* FIXED: Sources converted to UTF-8.
|
||||
* **Alpha 0.14 (2012-04-20):**
|
||||
* ADDED: GD presence is checked.
|
||||
* ADDED: GD presence is checked.
|
||||
* CHANGED: Traffic limiter data files moved to data/ (→easier rights management)
|
||||
* ADDED: "Burn after reading" implemented. Opening the URL will display the paste and immediately destroy it on server.
|
||||
* **Alpha 0.13 (2012-04-18):**
|
||||
@@ -225,16 +242,16 @@ encryption), i18n (translation, counterpart of i18n.php) and helper (stateless u
|
||||
* FIXED: $error not properly initialized in index.php
|
||||
* **Alpha 0.12 (2012-04-18):**
|
||||
* **DISCUSSIONS !** Now you can enable discussions on your pastes. Of course, posted comments and nickname are also encrypted and the server cannot see them.
|
||||
* This feature implies a change in storage format. You will have to delete all previous pastes in your ZeroBin.
|
||||
* This feature implies a change in storage format. You will have to delete all previous pastes in your ZeroBin.
|
||||
* Added [[php:vizhash_gd|Vizhash]] as avatars, so you can match posters IP addresses without revealing them. (Same image = same IP). Of course the IP address cannot be deduced from the Vizhash.
|
||||
* Remaining time before expiration is now displayed.
|
||||
* Explicit tags were added to CSS and jQuery selectors (eg. div#aaa instead of #aaa) to speed up browser.
|
||||
* Explicit tags were added to CSS and jQuery selectors (eg. div#aaa instead of #aaa) to speed up browser.
|
||||
* Better cleaning of the URL (to make sure the key is not broken by some stupid redirection service)
|
||||
* **Alpha 0.11 (2012-04-12):**
|
||||
* Automatically ignore parameters (such as &utm_source=...) added //after// the anchor by some stupid Web 2.0 services.
|
||||
* First public release.
|
||||
* **Alpha 0.10 (2012-04-12):**
|
||||
* IE9 does not seem to correctly support ''pre-wrap'' either. Special handling mode activated for all version of IE<10. (Note: **ALL other browsers** correctly support this feature.)
|
||||
* IE9 does not seem to correctly support ''pre-wrap'' either. Special handling mode activated for all version of IE<10. (Note: **ALL other browsers** correctly support this feature.)
|
||||
* **Alpha 0.9 (2012-04-11):**
|
||||
* Oh bummer... IE 8 is as shitty as IE6/7: Its does not seem to support ''white-space:pre-wrap'' correctly. I had to activate the special handling mode. I still have to test IE 9.
|
||||
* **Alpha 0.8 (2012-04-11):**
|
||||
|
||||
+12
-6
@@ -2,18 +2,17 @@
|
||||
|
||||
## Active contributors
|
||||
|
||||
Simon Rupf - current developer and maintainer
|
||||
rugk - security review, doc improvment, JS refactoring & various other stuff
|
||||
R4SAS - python client, compression, blob URI to support larger attachments
|
||||
* Simon Rupf - current developer and maintainer
|
||||
* rugk - security review, doc improvment, JS refactoring & various other stuff
|
||||
* R4SAS - python client, compression, blob URI to support larger attachments
|
||||
|
||||
## Past contributions
|
||||
|
||||
Sébastien Sauvage - original idea and main developer
|
||||
|
||||
* Sébastien Sauvage - original idea and main developer
|
||||
* Alexey Gladkov - syntax highlighting
|
||||
* Greg Knaddison - robots.txt
|
||||
* MrKooky - HTML5 markup, CSS cleanup
|
||||
* Simon Rupf - WebCrypto, unit tests, current docker containers, MVC, configuration, i18n
|
||||
* Simon Rupf - WebCrypto, unit tests, container images, database backend, MVC, configuration, i18n
|
||||
* Hexalyse - Password protection
|
||||
* Viktor Stanchev - File upload support
|
||||
* azlux - Tab character input support
|
||||
@@ -27,6 +26,9 @@ Sébastien Sauvage - original idea and main developer
|
||||
* Harald Leithner - base58 encoding of key
|
||||
* Haocen - lots of bugfixes and UI improvements
|
||||
* Lucas Savva - configurable config file location, NixOS packaging
|
||||
* rodehoed - option to exempt ips from the rate-limiter
|
||||
* Mark van Holsteijn - Google Cloud Storage backend
|
||||
* Austin Huang - Oracle database support
|
||||
|
||||
## Translations
|
||||
* Hexalyse - French
|
||||
@@ -50,3 +52,7 @@ Sébastien Sauvage - original idea and main developer
|
||||
* Moo - Lithuanian
|
||||
* whenwesober - Indonesian
|
||||
* retiolus - Catalan
|
||||
* sarnane - Estonian
|
||||
* foxsouns - Lojban
|
||||
* Patriccollu di Santa Maria è Sichè - Corsican
|
||||
* Markus Mikkonen - Finnish
|
||||
|
||||
+94
-53
@@ -2,38 +2,46 @@
|
||||
|
||||
**TL;DR:** Download the
|
||||
[latest release archive](https://github.com/PrivateBin/PrivateBin/releases/latest)
|
||||
and extract it in your web hosts folder where you want to install your PrivateBin
|
||||
instance. We try to provide a mostly safe default configuration, but we urge you to
|
||||
check the [security section](#hardening-and-security) below and the [configuration
|
||||
options](#configuration) to adjust as you see fit.
|
||||
(with the link labelled as "Source code (…)") and extract it in your web hosts
|
||||
folder where you want to install your PrivateBin instance. We try to provide a
|
||||
mostly safe default configuration, but we urge you to check the
|
||||
[security section](#hardening-and-security) below and the
|
||||
[configuration options](#configuration) to adjust as you see fit.
|
||||
|
||||
**NOTE:** See [our FAQ](https://github.com/PrivateBin/PrivateBin/wiki/FAQ#how-can-i-securely-clonedownload-your-project) for information how to securely download the PrivateBin release files.
|
||||
**NOTE:** See our [FAQ entry on securely downloading release files](https://github.com/PrivateBin/PrivateBin/wiki/FAQ#how-can-i-securely-clonedownload-your-project)
|
||||
for more information.
|
||||
|
||||
**NOTE:** There is a [ansible](https://ansible.com) role by @e1mo available to install and configure PrivateBin on your server. It's available on [ansible galaxy](https://galaxy.ansible.com/e1mo/privatebin) ([source code](https://git.sr.ht/~e1mo/ansible-role-privatebin)).
|
||||
**NOTE:** There is a [ansible](https://ansible.com) role by @e1mo available to
|
||||
install and configure PrivateBin on your server. It's available on
|
||||
[ansible galaxy](https://galaxy.ansible.com/e1mo/privatebin)
|
||||
([source code](https://git.sr.ht/~e1mo/ansible-role-privatebin)).
|
||||
|
||||
### Minimal requirements
|
||||
### Minimal Requirements
|
||||
|
||||
- PHP version 7.0 or above
|
||||
- Or PHP version 5.6 AND _one_ of the following sources of cryptographically safe randomness:
|
||||
- [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)
|
||||
- open_basedir access to `/dev/urandom`
|
||||
- mcrypt extension (mcrypt needs to be able to access `/dev/urandom`. This means if `open_basedir` is set, it must include this file.)
|
||||
- Or PHP version 5.6 AND _one_ of the following sources of cryptographically
|
||||
safe randomness:
|
||||
- [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)
|
||||
- `open_basedir` access to `/dev/urandom`
|
||||
- mcrypt extension AND `open_basedir` access to `/dev/urandom`
|
||||
- com_dotnet extension
|
||||
- GD extension
|
||||
- zlib extension
|
||||
- some disk space or (optionally) a database supported by [PDO](https://php.net/manual/book.pdo.php)
|
||||
- ability to create files and folders in the installation directory and the PATH defined in index.php
|
||||
- A web browser with JavaScript support
|
||||
- some disk space or a database supported by [PDO](https://php.net/manual/book.pdo.php)
|
||||
- ability to create files and folders in the installation directory and the PATH
|
||||
defined in index.php
|
||||
- A web browser with JavaScript and (optional) WebAssembly support
|
||||
|
||||
## Hardening and security
|
||||
## Hardening and Security
|
||||
|
||||
### Changing the path
|
||||
### Changing the Path
|
||||
|
||||
In the index.php you can define a different `PATH`. This is useful to secure your
|
||||
installation. You can move the configuration, data files, templates and PHP
|
||||
In the index.php you can define a different `PATH`. This is useful to secure
|
||||
your installation. You can move the configuration, data files, templates and PHP
|
||||
libraries (directories cfg, doc, data, lib, tpl, tst and vendor) outside of your
|
||||
document root. This new location must still be accessible to your webserver / PHP
|
||||
process (see also
|
||||
document root. This new location must still be accessible to your webserver and
|
||||
PHP process (see also
|
||||
[open_basedir setting](https://secure.php.net/manual/en/ini.core.php#ini.open-basedir)).
|
||||
|
||||
> #### PATH Example
|
||||
@@ -42,24 +50,25 @@ process (see also
|
||||
> http://example.com/paste/
|
||||
>
|
||||
> The full path of PrivateBin on your webserver is:
|
||||
> /home/example.com/htdocs/paste
|
||||
> /srv/example.com/htdocs/paste
|
||||
>
|
||||
> When setting the path like this:
|
||||
> define('PATH', '../../secret/privatebin/');
|
||||
>
|
||||
> PrivateBin will look for your includes / data here:
|
||||
> /home/example.com/secret/privatebin
|
||||
> PrivateBin will look for your includes and data here:
|
||||
> /srv/example.com/secret/privatebin
|
||||
|
||||
### Changing the config path only
|
||||
|
||||
In situations where you want to keep the PrivateBin static files separate from the
|
||||
rest of your data, or you want to reuse the installation files on multiple vhosts,
|
||||
you may only want to change the `conf.php`. In this instance, you can set the
|
||||
you may only want to change the `conf.php`. In this case, you can set the
|
||||
`CONFIG_PATH` environment variable to the absolute path to the `conf.php` file.
|
||||
This can be done in your web server's virtual host config, the PHP config, or in
|
||||
the index.php if you choose to customize it.
|
||||
the index.php, if you choose to customize it.
|
||||
|
||||
Note that your PHP process will need read access to the config wherever it may be.
|
||||
Note that your PHP process will need read access to the configuration file,
|
||||
wherever it may be.
|
||||
|
||||
> #### CONFIG_PATH example
|
||||
> Setting the value in an Apache Vhost:
|
||||
@@ -73,23 +82,27 @@ Note that your PHP process will need read access to the config wherever it may b
|
||||
|
||||
### Transport security
|
||||
|
||||
When setting up PrivateBin, also set up HTTPS, if you haven't already. Without HTTPS
|
||||
PrivateBin is not secure, as the JavaScript files could be manipulated during transmission.
|
||||
For more information on this, see our [FAQ entry on HTTPS setup](https://github.com/PrivateBin/PrivateBin/wiki/FAQ#how-should-i-setup-https).
|
||||
When setting up PrivateBin, also set up HTTPS, if you haven't already. Without
|
||||
HTTPS PrivateBin is not secure, as the JavaScript or WebAssembly files could be
|
||||
manipulated during transmission. For more information on this, see our
|
||||
[FAQ entry on HTTPS setup recommendations](https://github.com/PrivateBin/PrivateBin/wiki/FAQ#how-should-i-setup-https).
|
||||
|
||||
### File-level permissions
|
||||
|
||||
After completing the installation, you should make sure, other users on the system cannot read the config file or the `data/` directory, as – depending on your configuration – potential secret information are saved there.
|
||||
After completing the installation, you should make sure, that other users on the
|
||||
system cannot read the config file or the `data/` directory, as – depending on
|
||||
your configuration – potentially sensitive information may be stored in there.
|
||||
|
||||
See [this FAQ item](https://github.com/PrivateBin/PrivateBin/wiki/FAQ#what-are-the-recommended-file-and-folder-permissions-for-privatebin) for a detailed guide on how to "harden" the permissions of files and folders.
|
||||
See our [FAQ entry on permissions](https://github.com/PrivateBin/PrivateBin/wiki/FAQ#what-are-the-recommended-file-and-folder-permissions-for-privatebin)
|
||||
for a detailed guide on how to "harden" access to files and folders.
|
||||
|
||||
## Configuration
|
||||
|
||||
In the file `cfg/conf.php` you can configure PrivateBin. A `cfg/conf.sample.php`
|
||||
is provided containing all options and default values. You can copy it to
|
||||
`cfg/conf.php` and adapt it as needed. Alternatively you can copy it anywhere and
|
||||
set the `CONFIG_PATH` environment variable (see above notes). The config file is
|
||||
divided into multiple sections, which are enclosed in square brackets.
|
||||
is provided containing all options and their default values. You can copy it to
|
||||
`cfg/conf.php` and change it as needed. Alternatively you can copy it anywhere
|
||||
and set the `CONFIG_PATH` environment variable (see above notes). The config
|
||||
file is divided into multiple sections, which are enclosed in square brackets.
|
||||
|
||||
In the `[main]` section you can enable or disable the discussion feature, set
|
||||
the limit of stored pastes and comments in bytes. The `[traffic]` section lets
|
||||
@@ -107,28 +120,28 @@ A `robots.txt` file is provided in the root dir of PrivateBin. It disallows all
|
||||
robots from accessing your pastes. It is recommend to place it into the root of
|
||||
your web directory if you have installed PrivateBin in a subdirectory. Make sure
|
||||
to adjust it, so that the file paths match your installation. Of course also
|
||||
adjust the file if you already use a `robots.txt`.
|
||||
adjust the file, if you already use a `robots.txt`.
|
||||
|
||||
A `.htaccess.disabled` file is provided in the root dir of PrivateBin. It blocks
|
||||
some known robots and link-scanning bots. If you use Apache, you can rename the
|
||||
file to `.htaccess` to enable this feature. If you use another webserver, you
|
||||
have to configure it manually to do the same.
|
||||
|
||||
### When using Cloudflare
|
||||
### On using Cloudflare
|
||||
|
||||
If you want to use PrivateBin behind Cloudflare, make sure you have disabled the Rocket
|
||||
loader and unchecked "Javascript" for Auto Minify, found in your domain settings,
|
||||
under "Speed". (More information
|
||||
[in this FAQ entry](https://github.com/PrivateBin/PrivateBin/wiki/FAQ#user-content-how-to-make-privatebin-work-when-using-cloudflare-for-ddos-protection))
|
||||
If you want to use PrivateBin behind Cloudflare, make sure you have disabled the
|
||||
Rocket loader and unchecked "Javascript" for Auto Minify, found in your domain
|
||||
settings, under "Speed". More information can be found in our
|
||||
[FAQ entry on Cloudflare related issues](https://github.com/PrivateBin/PrivateBin/wiki/FAQ#user-content-how-to-make-privatebin-work-when-using-cloudflare-for-ddos-protection).
|
||||
|
||||
### Using a database instead of flat files
|
||||
### Using a Database Instead of Flat Files
|
||||
|
||||
In the configuration file the `[model]` and `[model_options]` sections let you
|
||||
configure your favourite way of storing the pastes and discussions on your
|
||||
server.
|
||||
|
||||
`Filesystem` is the default model, which stores everything in files in the
|
||||
data folder. This is the recommended setup for most sites.
|
||||
data folder. This is the recommended setup for most sites on single hosts.
|
||||
|
||||
Under high load, in distributed setups or if you are not allowed to store files
|
||||
locally, you might want to switch to the `Database` model. This lets you
|
||||
@@ -142,21 +155,26 @@ to use a prefix for
|
||||
The table prefix option is called `tbl`.
|
||||
|
||||
> #### Note
|
||||
> The `Database` model has only been tested with SQLite, MySQL and PostgreSQL,
|
||||
> although it would not be recommended to use SQLite in a production environment.
|
||||
> If you gain any experience running PrivateBin on other RDBMS, please let us
|
||||
> know.
|
||||
> The `Database` model has only been tested with SQLite, MariaDB/MySQL and
|
||||
> PostgreSQL, although it would not be recommended to use SQLite in a production
|
||||
> environment. If you gain any experience running PrivateBin on other RDBMS,
|
||||
> please let us know.
|
||||
|
||||
The following GRANTs (privileges) are required for the PrivateBin user in **MySQL**. In normal operation:
|
||||
The following GRANTs (privileges) are required for the PrivateBin user in
|
||||
**MariaDB/MySQL**. In normal operation:
|
||||
- INSERT, SELECT, DELETE on the paste and comment tables
|
||||
- SELECT on the config table
|
||||
|
||||
If you want PrivateBin to handle table creation (when you create the first paste) and updates (after you update PrivateBin to a new release), you need to give the user these additional privileges:
|
||||
If you want PrivateBin to handle table creation (when you create the first paste)
|
||||
and updates (after you update PrivateBin to a new release), you need to give the
|
||||
user these additional privileges:
|
||||
- CREATE, INDEX and ALTER on the database
|
||||
- INSERT and UPDATE on the config table
|
||||
|
||||
For reference or if you want to create the table schema for yourself to avoid having to give PrivateBin too many permissions (replace
|
||||
`prefix_` with your own table prefix and create the table schema with your favourite MySQL console):
|
||||
For reference or if you want to create the table schema for yourself to avoid
|
||||
having to give PrivateBin too many permissions (replace `prefix_` with your own
|
||||
table prefix and create the table schema with your favourite MariaDB/MySQL
|
||||
client):
|
||||
|
||||
```sql
|
||||
CREATE TABLE prefix_paste (
|
||||
@@ -187,7 +205,30 @@ CREATE INDEX parent ON prefix_comment(pasteid);
|
||||
CREATE TABLE prefix_config (
|
||||
id CHAR(16) NOT NULL, value TEXT, PRIMARY KEY (id)
|
||||
);
|
||||
INSERT INTO prefix_config VALUES('VERSION', '1.3.5');
|
||||
INSERT INTO prefix_config VALUES('VERSION', '1.4.0');
|
||||
```
|
||||
|
||||
In **PostgreSQL**, the data, attachment, nickname and vizhash columns needs to be TEXT and not BLOB or MEDIUMBLOB.
|
||||
In **PostgreSQL**, the `data`, `attachment`, `nickname` and `vizhash` columns
|
||||
need to be `TEXT` and not `BLOB` or `MEDIUMBLOB`. The key names in brackets,
|
||||
after `PRIMARY KEY`, need to be removed.
|
||||
|
||||
In **Oracle**, the `data`, `attachment`, `nickname` and `vizhash` columns need
|
||||
to be `CLOB` and not `BLOB` or `MEDIUMBLOB`, the `id` column in the `config`
|
||||
table needs to be `VARCHAR2(16)` and the `meta` column in the `paste` table
|
||||
and the `value` column in the `config` table need to be `VARCHAR2(4000)`.
|
||||
|
||||
#### Using Google Cloud Storage
|
||||
If you want to deploy PrivateBin in a serverless manner in the Google Cloud, you
|
||||
can choose the `GoogleCloudStorage` as backend. To use this backend, you create
|
||||
a GCS bucket and specify the name as the model option `bucket`. Alternatively,
|
||||
you can set the name through the environment variable `PRIVATEBIN_GCS_BUCKET`.
|
||||
|
||||
The default prefix for pastes stored in the bucket is `pastes`. To change the
|
||||
prefix, specify the option `prefix`.
|
||||
|
||||
Google Cloud Storage buckets may be significantly slower than a `FileSystem` or
|
||||
`Database` backend. The big advantage is that the deployment on Google Cloud
|
||||
Platform using Google Cloud Run is easy and cheap.
|
||||
|
||||
To use the Google Cloud Storage backend you have to install the suggested
|
||||
library using the command `composer require google/cloud-storage`.
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
.PHONY: all coverage coverage-js coverage-php doc doc-js doc-php increment sign test test-js test-php help
|
||||
|
||||
CURRENT_VERSION = 1.3.5
|
||||
VERSION ?= 1.3.6
|
||||
VERSION_FILES = index.php cfg/ *.md css/ i18n/ img/ js/privatebin.js lib/ Makefile tpl/ tst/
|
||||
CURRENT_VERSION = 1.4.0
|
||||
VERSION ?= 1.4.1
|
||||
VERSION_FILES = index.php cfg/ *.md css/ i18n/ img/ js/package.json js/privatebin.js lib/ Makefile tpl/ tst/
|
||||
REGEX_CURRENT_VERSION := $(shell echo $(CURRENT_VERSION) | sed "s/\./\\\./g")
|
||||
REGEX_VERSION := $(shell echo $(VERSION) | sed "s/\./\\\./g")
|
||||
|
||||
@@ -33,12 +33,13 @@ increment: ## Increment and commit new version number, set target version using
|
||||
do \
|
||||
sed -i "s/$(REGEX_CURRENT_VERSION)/$(REGEX_VERSION)/g" $$F; \
|
||||
done
|
||||
git add $(VERSION_FILES)
|
||||
cd tst && phpunit --no-coverage && cd ..
|
||||
git add $(VERSION_FILES) tpl/
|
||||
git commit -m "incrementing version"
|
||||
|
||||
sign: ## Sign a release.
|
||||
git tag $(VERSION)
|
||||
git push --tags
|
||||
git push origin $(VERSION)
|
||||
signrelease.sh
|
||||
|
||||
test: test-js test-php ## Run all unit tests.
|
||||
|
||||
@@ -1,25 +1,27 @@
|
||||
# [](https://privatebin.info/)
|
||||
|
||||
*Current version: 1.3.5*
|
||||
*Current version: 1.4.0*
|
||||
|
||||
**PrivateBin** is a minimalist, open source online [pastebin](https://en.wikipedia.org/wiki/Pastebin)
|
||||
**PrivateBin** is a minimalist, open source online
|
||||
[pastebin](https://en.wikipedia.org/wiki/Pastebin)
|
||||
where the server has zero knowledge of pasted data.
|
||||
|
||||
Data is encrypted and decrypted in the browser using 256bit AES in [Galois Counter mode](https://en.wikipedia.org/wiki/Galois/Counter_Mode).
|
||||
Data is encrypted and decrypted in the browser using 256bit AES in
|
||||
[Galois Counter mode](https://en.wikipedia.org/wiki/Galois/Counter_Mode).
|
||||
|
||||
This is a fork of ZeroBin, originally developed by
|
||||
[Sébastien Sauvage](https://github.com/sebsauvage/ZeroBin). ZeroBin was refactored
|
||||
to allow easier and cleaner extensions. PrivateBin has many more features than the
|
||||
original ZeroBin. It is, however, still fully compatible to the original ZeroBin 0.19
|
||||
[Sébastien Sauvage](https://github.com/sebsauvage/ZeroBin). PrivateBin was
|
||||
refactored to allow easier and cleaner extensions and has many additional
|
||||
features. It is, however, still fully compatible to the original ZeroBin 0.19
|
||||
data storage scheme. Therefore, such installations can be upgraded to PrivateBin
|
||||
without losing any data.
|
||||
|
||||
## What PrivateBin provides
|
||||
|
||||
+ As a server administrator you don't have to worry if your users post content
|
||||
that is considered illegal in your country. You have no knowledge of any
|
||||
of the pastes content. If requested or enforced, you can delete any paste from
|
||||
your system.
|
||||
that is considered illegal in your country. You have plausible deniability of
|
||||
any of the pastes content. If requested or enforced, you can delete any paste
|
||||
from your system.
|
||||
|
||||
+ Pastebin-like system to store text documents, code samples, etc.
|
||||
|
||||
@@ -31,13 +33,13 @@ without losing any data.
|
||||
|
||||
## What it doesn't provide
|
||||
|
||||
- As a user you have to trust the server administrator not to inject any malicious
|
||||
javascript code.
|
||||
For basic security, the PrivateBin installation *has to provide HTTPS*!
|
||||
Otherwise you would also have to trust your internet provider, and any country
|
||||
the traffic passes through.
|
||||
Additionally the instance should be secured by
|
||||
[HSTS](https://en.wikipedia.org/wiki/HTTP_Strict_Transport_Security). It can use traditional certificate authorities and/or use
|
||||
- As a user you have to trust the server administrator not to inject any
|
||||
malicious code. For security, a PrivateBin installation *has to be used over*
|
||||
*HTTPS*! Otherwise you would also have to trust your internet provider, and
|
||||
any jurisdiction the traffic passes through. Additionally the instance should
|
||||
be secured by
|
||||
[HSTS](https://en.wikipedia.org/wiki/HTTP_Strict_Transport_Security). It can
|
||||
use traditional certificate authorities and/or use a
|
||||
[DNSSEC](https://en.wikipedia.org/wiki/Domain_Name_System_Security_Extensions)
|
||||
protected
|
||||
[DANE](https://en.wikipedia.org/wiki/DNS-based_Authentication_of_Named_Entities)
|
||||
@@ -45,18 +47,17 @@ without losing any data.
|
||||
|
||||
- The "key" used to encrypt the paste is part of the URL. If you publicly post
|
||||
the URL of a paste that is not password-protected, anyone can read it.
|
||||
Use a password if you want your paste to be private. In this case, make sure to
|
||||
use a strong password and only share it privately and end-to-end-encrypted.
|
||||
Use a password if you want your paste to remain private. In that case, make
|
||||
sure to use a strong password and share it privately and end-to-end-encrypted.
|
||||
|
||||
- A server admin might be forced to hand over access logs to the authorities.
|
||||
- A server admin can be forced to hand over access logs to the authorities.
|
||||
PrivateBin encrypts your text and the discussion contents, but who accessed a
|
||||
paste (first) might still be disclosed via access logs.
|
||||
|
||||
- In case of a server breach your data is secure as it is only stored encrypted
|
||||
on the server. However, the server could be misused or the server admin could
|
||||
be legally forced into sending malicious JavaScript to all web users, which
|
||||
grabs the decryption key and sends it to the server when a user accesses a
|
||||
PrivateBin.
|
||||
on the server. However, the server could be absused or the server admin could
|
||||
be legally forced into sending malicious code to their users, which logs
|
||||
the decryption key and sends it to a server when a user accesses a paste.
|
||||
Therefore, do not access any PrivateBin instance if you think it has been
|
||||
compromised. As long as no user accesses this instance with a previously
|
||||
generated URL, the content can't be decrypted.
|
||||
@@ -77,8 +78,8 @@ file](https://github.com/PrivateBin/PrivateBin/wiki/Configuration):
|
||||
* Syntax highlighting for source code using prettify.js, including 4 prettify
|
||||
themes
|
||||
|
||||
* File upload support, images get displayed (disabled by default, possibility
|
||||
to adjust size limit)
|
||||
* File upload support, image, media and PDF preview (disabled by default, size
|
||||
limit adjustable)
|
||||
|
||||
* Templates: By default there are bootstrap CSS, darkstrap and "classic ZeroBin"
|
||||
to choose from and it is easy to adapt these to your own websites layout or
|
||||
@@ -89,7 +90,7 @@ file](https://github.com/PrivateBin/PrivateBin/wiki/Configuration):
|
||||
|
||||
* Language selection (disabled by default, as it uses a session cookie)
|
||||
|
||||
* QR code generation of URL, to easily transfer pastes over to a mobile device
|
||||
* QR code for paste URLs, to easily transfer them over to mobile devices
|
||||
|
||||
## Further resources
|
||||
|
||||
|
||||
+2
-2
@@ -4,8 +4,8 @@
|
||||
|
||||
| Version | Supported |
|
||||
| ------- | ------------------ |
|
||||
| 1.3.5 | :heavy_check_mark: |
|
||||
| < 1.3.5 | :x: |
|
||||
| 1.4.0 | :heavy_check_mark: |
|
||||
| < 1.4.0 | :x: |
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
|
||||
+20
-7
@@ -87,7 +87,7 @@ languageselection = false
|
||||
; async functions and display an error if not and for Chrome to enable
|
||||
; webassembly support (used for zlib compression). You can remove it if Chrome
|
||||
; doesn't need to be supported and old browsers don't need to be warned.
|
||||
; cspheader = "default-src 'none'; manifest-src 'self'; connect-src * blob:; script-src 'self' 'unsafe-eval' resource:; style-src 'self'; font-src 'self'; img-src 'self' data: blob:; media-src blob:; object-src blob:; sandbox allow-same-origin allow-scripts allow-forms allow-popups allow-modals allow-downloads"
|
||||
; cspheader = "default-src 'none'; base-uri 'self'; form-action 'none'; manifest-src 'self'; connect-src * blob:; script-src 'self' 'unsafe-eval'; style-src 'self'; font-src 'self'; frame-ancestors 'none'; img-src 'self' data: blob:; media-src blob:; object-src blob:; sandbox allow-same-origin allow-scripts allow-forms allow-popups allow-modals allow-downloads"
|
||||
|
||||
; stay compatible with PrivateBin Alpha 0.19, less secure
|
||||
; if enabled will use base64.js version 1.7 instead of 2.1.9 and sha1 instead of
|
||||
@@ -135,13 +135,22 @@ markdown = "Markdown"
|
||||
; Set this to 0 to disable rate limiting.
|
||||
limit = 10
|
||||
|
||||
; (optional) Set IPs addresses (v4 or v6) or subnets (CIDR) which are exempted
|
||||
; from the rate-limit. Invalid IPs will be ignored. If multiple values are to
|
||||
; be exempted, the list needs to be comma separated. Leave unset to disable
|
||||
; exemptions.
|
||||
; exempted = "1.2.3.4,10.10.10/24"
|
||||
|
||||
; (optional) If you want only some source IP addresses (v4 or v6) or subnets
|
||||
; (CIDR) to be allowed to create pastes, set these here. Invalid IPs will be
|
||||
; ignored. If multiple values are to be exempted, the list needs to be comma
|
||||
; separated. Leave unset to allow anyone to create pastes.
|
||||
; creators = "1.2.3.4,10.10.10/24"
|
||||
|
||||
; (optional) if your website runs behind a reverse proxy or load balancer,
|
||||
; set the HTTP header containing the visitors IP address, i.e. X_FORWARDED_FOR
|
||||
; header = "X_FORWARDED_FOR"
|
||||
|
||||
; directory to store the traffic limits in
|
||||
dir = PATH "data"
|
||||
|
||||
[purge]
|
||||
; minimum time limit between two purgings of expired pastes, it is only
|
||||
; triggered when pastes are created
|
||||
@@ -153,9 +162,6 @@ limit = 300
|
||||
; site
|
||||
batchsize = 10
|
||||
|
||||
; directory to store the purge limit in
|
||||
dir = PATH "data"
|
||||
|
||||
[model]
|
||||
; name of data model class to load and directory for storage
|
||||
; the default model "Filesystem" stores everything in the filesystem
|
||||
@@ -163,6 +169,13 @@ class = Filesystem
|
||||
[model_options]
|
||||
dir = PATH "data"
|
||||
|
||||
;[model]
|
||||
; example of a Google Cloud Storage configuration
|
||||
;class = GoogleCloudStorage
|
||||
;[model_options]
|
||||
;bucket = "my-private-bin"
|
||||
;prefix = "pastes"
|
||||
|
||||
;[model]
|
||||
; example of DB configuration for MySQL
|
||||
;class = Database
|
||||
|
||||
+7
-3
@@ -25,8 +25,12 @@
|
||||
},
|
||||
"require" : {
|
||||
"php" : "^5.6.0 || ^7.0 || ^8.0",
|
||||
"paragonie/random_compat" : "2.0.19",
|
||||
"yzalis/identicon" : "2.0.0"
|
||||
"paragonie/random_compat" : "2.0.21",
|
||||
"yzalis/identicon" : "2.0.0",
|
||||
"mlocati/ip-lib" : "1.18.0"
|
||||
},
|
||||
"suggest" : {
|
||||
"google/cloud-storage" : "1.26.1"
|
||||
},
|
||||
"require-dev" : {
|
||||
"phpunit/phpunit" : "^4.6 || ^5.0"
|
||||
@@ -39,4 +43,4 @@
|
||||
"config" : {
|
||||
"autoloader-suffix" : "DontChange"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Generated
+108
-36
@@ -4,27 +4,94 @@
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "9d110873bf15a6abd66734e8a818134c",
|
||||
"content-hash": "fa52d4988bfe17d4b27e3a4789a1ec49",
|
||||
"packages": [
|
||||
{
|
||||
"name": "paragonie/random_compat",
|
||||
"version": "v2.0.19",
|
||||
"name": "mlocati/ip-lib",
|
||||
"version": "1.18.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/paragonie/random_compat.git",
|
||||
"reference": "446fc9faa5c2a9ddf65eb7121c0af7e857295241"
|
||||
"url": "https://github.com/mlocati/ip-lib.git",
|
||||
"reference": "c77bd0b1f3e3956c7e9661e75cb1f54ed67d95d2"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/paragonie/random_compat/zipball/446fc9faa5c2a9ddf65eb7121c0af7e857295241",
|
||||
"reference": "446fc9faa5c2a9ddf65eb7121c0af7e857295241",
|
||||
"url": "https://api.github.com/repos/mlocati/ip-lib/zipball/c77bd0b1f3e3956c7e9661e75cb1f54ed67d95d2",
|
||||
"reference": "c77bd0b1f3e3956c7e9661e75cb1f54ed67d95d2",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.3.3"
|
||||
},
|
||||
"require-dev": {
|
||||
"ext-pdo_sqlite": "*",
|
||||
"phpunit/phpunit": "^4.8 || ^5.7 || ^6.5 || ^7.5 || ^8.5 || ^9.5"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"IPLib\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Michele Locati",
|
||||
"email": "mlocati@gmail.com",
|
||||
"homepage": "https://github.com/mlocati",
|
||||
"role": "Author"
|
||||
}
|
||||
],
|
||||
"description": "Handle IPv4, IPv6 addresses and ranges",
|
||||
"homepage": "https://github.com/mlocati/ip-lib",
|
||||
"keywords": [
|
||||
"IP",
|
||||
"address",
|
||||
"addresses",
|
||||
"ipv4",
|
||||
"ipv6",
|
||||
"manage",
|
||||
"managing",
|
||||
"matching",
|
||||
"network",
|
||||
"networking",
|
||||
"range",
|
||||
"subnet"
|
||||
],
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://github.com/sponsors/mlocati",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://paypal.me/mlocati",
|
||||
"type": "other"
|
||||
}
|
||||
],
|
||||
"time": "2022-01-13T18:05:33+00:00"
|
||||
},
|
||||
{
|
||||
"name": "paragonie/random_compat",
|
||||
"version": "v2.0.21",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/paragonie/random_compat.git",
|
||||
"reference": "96c132c7f2f7bc3230723b66e89f8f150b29d5ae"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/paragonie/random_compat/zipball/96c132c7f2f7bc3230723b66e89f8f150b29d5ae",
|
||||
"reference": "96c132c7f2f7bc3230723b66e89f8f150b29d5ae",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.2.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "4.*|5.*"
|
||||
"phpunit/phpunit": "*"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes."
|
||||
@@ -53,7 +120,7 @@
|
||||
"pseudorandom",
|
||||
"random"
|
||||
],
|
||||
"time": "2020-10-15T10:06:57+00:00"
|
||||
"time": "2022-02-16T17:07:03+00:00"
|
||||
},
|
||||
{
|
||||
"name": "yzalis/identicon",
|
||||
@@ -202,12 +269,12 @@
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"DeepCopy\\": "src/DeepCopy/"
|
||||
},
|
||||
"files": [
|
||||
"src/DeepCopy/deep_copy.php"
|
||||
]
|
||||
],
|
||||
"psr-4": {
|
||||
"DeepCopy\\": "src/DeepCopy/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
@@ -280,16 +347,16 @@
|
||||
},
|
||||
{
|
||||
"name": "phpdocumentor/reflection-docblock",
|
||||
"version": "5.2.2",
|
||||
"version": "5.3.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/phpDocumentor/ReflectionDocBlock.git",
|
||||
"reference": "069a785b2141f5bcf49f3e353548dc1cce6df556"
|
||||
"reference": "622548b623e81ca6d78b721c5e029f4ce664f170"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/069a785b2141f5bcf49f3e353548dc1cce6df556",
|
||||
"reference": "069a785b2141f5bcf49f3e353548dc1cce6df556",
|
||||
"url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/622548b623e81ca6d78b721c5e029f4ce664f170",
|
||||
"reference": "622548b623e81ca6d78b721c5e029f4ce664f170",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -300,7 +367,8 @@
|
||||
"webmozart/assert": "^1.9.1"
|
||||
},
|
||||
"require-dev": {
|
||||
"mockery/mockery": "~1.3.2"
|
||||
"mockery/mockery": "~1.3.2",
|
||||
"psalm/phar": "^4.8"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
@@ -328,20 +396,20 @@
|
||||
}
|
||||
],
|
||||
"description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.",
|
||||
"time": "2020-09-03T19:13:55+00:00"
|
||||
"time": "2021-10-19T17:43:47+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpdocumentor/type-resolver",
|
||||
"version": "1.4.0",
|
||||
"version": "1.6.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/phpDocumentor/TypeResolver.git",
|
||||
"reference": "6a467b8989322d92aa1c8bf2bebcc6e5c2ba55c0"
|
||||
"reference": "93ebd0014cab80c4ea9f5e297ea48672f1b87706"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/6a467b8989322d92aa1c8bf2bebcc6e5c2ba55c0",
|
||||
"reference": "6a467b8989322d92aa1c8bf2bebcc6e5c2ba55c0",
|
||||
"url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/93ebd0014cab80c4ea9f5e297ea48672f1b87706",
|
||||
"reference": "93ebd0014cab80c4ea9f5e297ea48672f1b87706",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -349,7 +417,8 @@
|
||||
"phpdocumentor/reflection-common": "^2.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"ext-tokenizer": "*"
|
||||
"ext-tokenizer": "*",
|
||||
"psalm/phar": "^4.8"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
@@ -373,7 +442,7 @@
|
||||
}
|
||||
],
|
||||
"description": "A PSR-5 based resolver of Class names, Types and Structural Element Names",
|
||||
"time": "2020-09-17T18:55:26+00:00"
|
||||
"time": "2022-01-04T19:58:01+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpspec/prophecy",
|
||||
@@ -1351,28 +1420,31 @@
|
||||
},
|
||||
{
|
||||
"name": "symfony/polyfill-ctype",
|
||||
"version": "v1.22.1",
|
||||
"version": "v1.24.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/polyfill-ctype.git",
|
||||
"reference": "c6c942b1ac76c82448322025e084cadc56048b4e"
|
||||
"reference": "30885182c981ab175d4d034db0f6f469898070ab"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/c6c942b1ac76c82448322025e084cadc56048b4e",
|
||||
"reference": "c6c942b1ac76c82448322025e084cadc56048b4e",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/30885182c981ab175d4d034db0f6f469898070ab",
|
||||
"reference": "30885182c981ab175d4d034db0f6f469898070ab",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.1"
|
||||
},
|
||||
"provide": {
|
||||
"ext-ctype": "*"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-ctype": "For best performance"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-main": "1.22-dev"
|
||||
"dev-main": "1.23-dev"
|
||||
},
|
||||
"thanks": {
|
||||
"name": "symfony/polyfill",
|
||||
@@ -1423,20 +1495,20 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2021-01-07T16:49:33+00:00"
|
||||
"time": "2021-10-20T20:35:02+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/yaml",
|
||||
"version": "v4.4.21",
|
||||
"version": "v4.4.37",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/yaml.git",
|
||||
"reference": "3871c720871029f008928244e56cf43497da7e9d"
|
||||
"reference": "d7f637cc0f0cc14beb0984f2bb50da560b271311"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/yaml/zipball/3871c720871029f008928244e56cf43497da7e9d",
|
||||
"reference": "3871c720871029f008928244e56cf43497da7e9d",
|
||||
"url": "https://api.github.com/repos/symfony/yaml/zipball/d7f637cc0f0cc14beb0984f2bb50da560b271311",
|
||||
"reference": "d7f637cc0f0cc14beb0984f2bb50da560b271311",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -1491,7 +1563,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2021-03-05T17:58:50+00:00"
|
||||
"time": "2022-01-24T20:11:01+00:00"
|
||||
},
|
||||
{
|
||||
"name": "webmozart/assert",
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
* @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.3.5
|
||||
* @version 1.4.0
|
||||
*/
|
||||
|
||||
body {
|
||||
|
||||
+1
-1
@@ -6,7 +6,7 @@
|
||||
* @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.3.5
|
||||
* @version 1.4.0
|
||||
*/
|
||||
|
||||
/* When there is no script at all other */
|
||||
|
||||
+5
-1
@@ -6,7 +6,7 @@
|
||||
* @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.3.5
|
||||
* @version 1.4.0
|
||||
*/
|
||||
|
||||
/* CSS Reset from YUI 3.4.1 (build 4118) - Copyright 2011 Yahoo! Inc. All rights reserved.
|
||||
@@ -249,6 +249,10 @@ button img {
|
||||
padding: 1px 0 1px 0;
|
||||
}
|
||||
|
||||
#downloadtextbutton img {
|
||||
padding: 1px 0 1px 0;
|
||||
}
|
||||
|
||||
#remainingtime, #password {
|
||||
color: #94a3b4;
|
||||
display: inline;
|
||||
|
||||
+3
-1
@@ -184,5 +184,7 @@
|
||||
"Close": "Close",
|
||||
"Encrypted note on PrivateBin": "Encrypted note on PrivateBin",
|
||||
"Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.": "Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.",
|
||||
"URL shortener may expose your decrypt key in URL.": "URL shortener may expose your decrypt key in URL."
|
||||
"URL shortener may expose your decrypt key in URL.": "URL shortener may expose your decrypt key in URL.",
|
||||
"Save paste": "Save paste",
|
||||
"Your IP is not authorized to create pastes.": "Your IP is not authorized to create pastes."
|
||||
}
|
||||
|
||||
+3
-1
@@ -184,5 +184,7 @@
|
||||
"Close": "Close",
|
||||
"Encrypted note on PrivateBin": "Encrypted note on PrivateBin",
|
||||
"Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.": "Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.",
|
||||
"URL shortener may expose your decrypt key in URL.": "URL shortener may expose your decrypt key in URL."
|
||||
"URL shortener may expose your decrypt key in URL.": "URL shortener may expose your decrypt key in URL.",
|
||||
"Save paste": "Save paste",
|
||||
"Your IP is not authorized to create pastes.": "Your IP is not authorized to create pastes."
|
||||
}
|
||||
|
||||
+56
-54
@@ -8,75 +8,75 @@
|
||||
"%s requires php %s or above to work. Sorry.": "%s requereix php %s o superior per funcionar. Ho sento.",
|
||||
"%s requires configuration section [%s] to be present in configuration file.": "%s requereix que la secció de configuració [%s] sigui present al fitxer de configuració.",
|
||||
"Please wait %d seconds between each post.": [
|
||||
"Please wait %d second between each post. (singular)",
|
||||
"Please wait %d seconds between each post. (1st plural)",
|
||||
"Espereu %d segon entre cada entrada.",
|
||||
"Espereu %d segons entre cada entrada.",
|
||||
"Please wait %d seconds between each post. (2nd plural)",
|
||||
"Please wait %d seconds between each post. (3rd plural)"
|
||||
],
|
||||
"Paste is limited to %s of encrypted data.": "Paste is limited to %s of encrypted data.",
|
||||
"Invalid data.": "Invalid data.",
|
||||
"You are unlucky. Try again.": "You are unlucky. Try again.",
|
||||
"Error saving comment. Sorry.": "Error saving comment. Sorry.",
|
||||
"Error saving paste. Sorry.": "Error saving paste. Sorry.",
|
||||
"Invalid paste ID.": "Invalid paste ID.",
|
||||
"Paste is limited to %s of encrypted data.": "L'enganxat està limitat a %s de dades encriptades.",
|
||||
"Invalid data.": "Dades no vàlides.",
|
||||
"You are unlucky. Try again.": "Mala sort. Torna-ho a provar.",
|
||||
"Error saving comment. Sorry.": "S'ha produït un error en desar el comentari. Ho sento.",
|
||||
"Error saving paste. Sorry.": "S'ha produït un error en desar l'enganxat. Ho sento.",
|
||||
"Invalid paste ID.": "Identificador d'enganxament no vàlid.",
|
||||
"Paste is not of burn-after-reading type.": "Paste is not of burn-after-reading type.",
|
||||
"Wrong deletion token. Paste was not deleted.": "Wrong deletion token. Paste was not deleted.",
|
||||
"Paste was properly deleted.": "Paste was properly deleted.",
|
||||
"JavaScript is required for %s to work. Sorry for the inconvenience.": "JavaScript is required for %s to work. Sorry for the inconvenience.",
|
||||
"%s requires a modern browser to work.": "%s requires a modern browser to work.",
|
||||
"New": "New",
|
||||
"Send": "Send",
|
||||
"Clone": "Clone",
|
||||
"Raw text": "Raw text",
|
||||
"Expires": "Expires",
|
||||
"Burn after reading": "Burn after reading",
|
||||
"Open discussion": "Open discussion",
|
||||
"Password (recommended)": "Password (recommended)",
|
||||
"Discussion": "Discussion",
|
||||
"Toggle navigation": "Toggle navigation",
|
||||
"Wrong deletion token. Paste was not deleted.": "El token d'eliminació és incorrecte. El Paste no s'ha eliminat.",
|
||||
"Paste was properly deleted.": "El Paste s'ha esborrat correctament.",
|
||||
"JavaScript is required for %s to work. Sorry for the inconvenience.": "Cal JavaScript perquè %s funcioni. Em sap greu les molèsties.",
|
||||
"%s requires a modern browser to work.": "%s requereix un navegador modern per funcionar.",
|
||||
"New": "Nou",
|
||||
"Send": "Enviar",
|
||||
"Clone": "Clona",
|
||||
"Raw text": "Text sense processar",
|
||||
"Expires": "Caducitat",
|
||||
"Burn after reading": "Esborra després de ser llegit",
|
||||
"Open discussion": "Discussió oberta",
|
||||
"Password (recommended)": "Contrasenya (recomanat)",
|
||||
"Discussion": "Discussió",
|
||||
"Toggle navigation": "Alternar navegació",
|
||||
"%d seconds": [
|
||||
"%d second (singular)",
|
||||
"%d seconds (1st plural)",
|
||||
"%d segon",
|
||||
"%d segons",
|
||||
"%d seconds (2nd plural)",
|
||||
"%d seconds (3rd plural)"
|
||||
],
|
||||
"%d minutes": [
|
||||
"%d minute (singular)",
|
||||
"%d minutes (1st plural)",
|
||||
"%d minut",
|
||||
"%d minuts",
|
||||
"%d minutes (2nd plural)",
|
||||
"%d minutes (3rd plural)"
|
||||
],
|
||||
"%d hours": [
|
||||
"%d hour (singular)",
|
||||
"%d hours (1st plural)",
|
||||
"%d hora",
|
||||
"%d hores",
|
||||
"%d hours (2nd plural)",
|
||||
"%d hours (3rd plural)"
|
||||
],
|
||||
"%d days": [
|
||||
"%d day (singular)",
|
||||
"%d days (1st plural)",
|
||||
"%d dia",
|
||||
"%d dies",
|
||||
"%d days (2nd plural)",
|
||||
"%d days (3rd plural)"
|
||||
],
|
||||
"%d weeks": [
|
||||
"%d week (singular)",
|
||||
"%d weeks (1st plural)",
|
||||
"%d setmana",
|
||||
"%d setmanes",
|
||||
"%d weeks (2nd plural)",
|
||||
"%d weeks (3rd plural)"
|
||||
],
|
||||
"%d months": [
|
||||
"%d month (singular)",
|
||||
"%d months (1st plural)",
|
||||
"%d mes",
|
||||
"%d mesos",
|
||||
"%d months (2nd plural)",
|
||||
"%d months (3rd plural)"
|
||||
],
|
||||
"%d years": [
|
||||
"%d year (singular)",
|
||||
"%d years (1st plural)",
|
||||
"%d any",
|
||||
"%d anys",
|
||||
"%d years (2nd plural)",
|
||||
"%d years (3rd plural)"
|
||||
],
|
||||
"Never": "Never",
|
||||
"Never": "Mai",
|
||||
"Note: This is a test service: Data may be deleted anytime. Kittens will die if you abuse this service.": "Note: This is a test service: Data may be deleted anytime. Kittens will die if you abuse this service.",
|
||||
"This document will expire in %d seconds.": [
|
||||
"This document will expire in %d second. (singular)",
|
||||
@@ -108,26 +108,26 @@
|
||||
"This document will expire in %d months. (2nd plural)",
|
||||
"This document will expire in %d months. (3rd plural)"
|
||||
],
|
||||
"Please enter the password for this paste:": "Please enter the password for this paste:",
|
||||
"Could not decrypt data (Wrong key?)": "Could not decrypt data (Wrong key?)",
|
||||
"Please enter the password for this paste:": "Si us plau, introdueix la contrasenya per aquest paste:",
|
||||
"Could not decrypt data (Wrong key?)": "No s'han pogut desxifrar les dades (Clau incorrecte?)",
|
||||
"Could not delete the paste, it was not stored in burn after reading mode.": "Could not delete the paste, it was not stored in burn after reading mode.",
|
||||
"FOR YOUR EYES ONLY. Don't close this window, this message can't be displayed again.": "FOR YOUR EYES ONLY. Don't close this window, this message can't be displayed again.",
|
||||
"Could not decrypt comment; Wrong key?": "Could not decrypt comment; Wrong key?",
|
||||
"Reply": "Reply",
|
||||
"Anonymous": "Anonymous",
|
||||
"Avatar generated from IP address": "Avatar generated from IP address",
|
||||
"Add comment": "Add comment",
|
||||
"Optional nickname…": "Optional nickname…",
|
||||
"Post comment": "Post comment",
|
||||
"Sending comment…": "Sending comment…",
|
||||
"Comment posted.": "Comment posted.",
|
||||
"Reply": "Respondre",
|
||||
"Anonymous": "Anònim",
|
||||
"Avatar generated from IP address": "Avatar generat a partir de l'adreça IP",
|
||||
"Add comment": "Afegir comentari",
|
||||
"Optional nickname…": "Pseudònim opcional…",
|
||||
"Post comment": "Publicar comentari",
|
||||
"Sending comment…": "Enviant comentari…",
|
||||
"Comment posted.": "Comentari publicat.",
|
||||
"Could not refresh display: %s": "Could not refresh display: %s",
|
||||
"unknown status": "unknown status",
|
||||
"unknown status": "estat desconegut",
|
||||
"server error or not responding": "server error or not responding",
|
||||
"Could not post comment: %s": "Could not post comment: %s",
|
||||
"Sending paste…": "Sending paste…",
|
||||
"Sending paste…": "Enviant paste…",
|
||||
"Your paste is <a id=\"pasteurl\" href=\"%s\">%s</a> <span id=\"copyhint\">(Hit [Ctrl]+[c] to copy)</span>": "Your paste is <a id=\"pasteurl\" href=\"%s\">%s</a> <span id=\"copyhint\">(Hit [Ctrl]+[c] to copy)</span>",
|
||||
"Delete data": "Delete data",
|
||||
"Delete data": "Esborrar les dades",
|
||||
"Could not create paste: %s": "Could not create paste: %s",
|
||||
"Cannot decrypt paste: Decryption key missing in URL (Did you use a redirector or an URL shortener which strips part of the URL?)": "Cannot decrypt paste: Decryption key missing in URL (Did you use a redirector or an URL shortener which strips part of the URL?)",
|
||||
"B": "B",
|
||||
@@ -140,10 +140,10 @@
|
||||
"ZiB": "ZiB",
|
||||
"YiB": "YiB",
|
||||
"Format": "Format",
|
||||
"Plain Text": "Plain Text",
|
||||
"Source Code": "Source Code",
|
||||
"Plain Text": "Text sense format",
|
||||
"Source Code": "Codi font",
|
||||
"Markdown": "Markdown",
|
||||
"Download attachment": "Download attachment",
|
||||
"Download attachment": "Baixar els adjunts",
|
||||
"Cloned: '%s'": "Cloned: '%s'",
|
||||
"The cloned file '%s' was attached to this paste.": "The cloned file '%s' was attached to this paste.",
|
||||
"Attach a file": "Attach a file",
|
||||
@@ -184,5 +184,7 @@
|
||||
"Close": "Close",
|
||||
"Encrypted note on PrivateBin": "Encrypted note on PrivateBin",
|
||||
"Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.": "Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.",
|
||||
"URL shortener may expose your decrypt key in URL.": "URL shortener may expose your decrypt key in URL."
|
||||
"URL shortener may expose your decrypt key in URL.": "URL shortener may expose your decrypt key in URL.",
|
||||
"Save paste": "Save paste",
|
||||
"Your IP is not authorized to create pastes.": "Your IP is not authorized to create pastes."
|
||||
}
|
||||
|
||||
+190
@@ -0,0 +1,190 @@
|
||||
{
|
||||
"PrivateBin": "PrivateBin",
|
||||
"%s is a minimalist, open source online pastebin where the server has zero knowledge of pasted data. Data is encrypted/decrypted %sin the browser%s using 256 bits AES.": "%s hè un serviziu in linea di tipu « pastebin » (ghjestiunariu d’appiccicu di pezzi di testu è di codice di fonte) minimalistu è à fonte aperta induve u servitore ùn hà micca cunnuscenza di i dati mandati. I dati sò cifrati è dicifrati %sin u navigatore%s cù una cifratura AES di 256 bit.",
|
||||
"More information on the <a href=\"https://privatebin.info/\">project page</a>.": "Più d’infurmazione annant’à a <a href=\"https://privatebin.info/\">pagina di u prughjettu</a>.",
|
||||
"Because ignorance is bliss": "Perchè l’ignurenza hè una campa",
|
||||
"en": "co",
|
||||
"Paste does not exist, has expired or has been deleted.": "L’appiccicu ùn esiste micca, hè scadutu o hè statu squassatu.",
|
||||
"%s requires php %s or above to work. Sorry.": "Per disgrazzia, %s richiede php %s o più recente per funziunà.",
|
||||
"%s requires configuration section [%s] to be present in configuration file.": "%s richiede a presenza di a sezzione di cunfigurazione [%s] in a schedariu di cunfigurazione.",
|
||||
"Please wait %d seconds between each post.": [
|
||||
"Aspettate %d seconda trà dui publicazioni.",
|
||||
"Aspettate %d seconde trà dui publicazioni.",
|
||||
"Aspettate %d seconde trà dui publicazioni.",
|
||||
"Aspettate %d seconde trà dui publicazioni."
|
||||
],
|
||||
"Paste is limited to %s of encrypted data.": "L’appiccicu hè limitatu à %s di dati cifrati.",
|
||||
"Invalid data.": "Dati inaccetevule.",
|
||||
"You are unlucky. Try again.": "Pruvate torna, Serete più furtunati.",
|
||||
"Error saving comment. Sorry.": "Per disgrazzia, ci hè un sbagliu à l’arregistramentu di u cummentu.",
|
||||
"Error saving paste. Sorry.": "Per disgrazzia, ci hè un sbagliu à l’arregistramentu di l’appiccicu.",
|
||||
"Invalid paste ID.": "N° di l’appiccicu inaccettevule.",
|
||||
"Paste is not of burn-after-reading type.": "L’appiccicu ùn hè micca di tipu « Squassà dopu a lettura ».",
|
||||
"Wrong deletion token. Paste was not deleted.": "Gettone di squassatura incurrettu. L’appiccicu ùn hè micca statu squassatu.",
|
||||
"Paste was properly deleted.": "L’appiccicu hè statu squassatu currettamente.",
|
||||
"JavaScript is required for %s to work. Sorry for the inconvenience.": "JavaScript hè richiestu per fà funziunà %s. Scusate per stu penseru.",
|
||||
"%s requires a modern browser to work.": "%s richiede un navigatore mudernu per funziunà.",
|
||||
"New": "Novu",
|
||||
"Send": "Mandà",
|
||||
"Clone": "Duppione",
|
||||
"Raw text": "Testu grossu",
|
||||
"Expires": "Scadenza",
|
||||
"Burn after reading": "Squassà dopu a lettura",
|
||||
"Open discussion": "Apre una chjachjarata",
|
||||
"Password (recommended)": "Parolla d’intesa (ricumandata)",
|
||||
"Discussion": "Chjachjarata",
|
||||
"Toggle navigation": "Invertisce a navigazione",
|
||||
"%d seconds": [
|
||||
"%d seconda",
|
||||
"%d seconde",
|
||||
"%d seconde",
|
||||
"%d seconde"
|
||||
],
|
||||
"%d minutes": [
|
||||
"%d minutu",
|
||||
"%d minuti",
|
||||
"%d minuti",
|
||||
"%d minuti"
|
||||
],
|
||||
"%d hours": [
|
||||
"%d ora",
|
||||
"%d ore",
|
||||
"%d ore",
|
||||
"%d ore"
|
||||
],
|
||||
"%d days": [
|
||||
"%d ghjornu",
|
||||
"%d ghjorni",
|
||||
"%d ghjorni",
|
||||
"%d ghjorni"
|
||||
],
|
||||
"%d weeks": [
|
||||
"%d settimana",
|
||||
"%d settimane",
|
||||
"%d settimane",
|
||||
"%d settimane"
|
||||
],
|
||||
"%d months": [
|
||||
"%d mese",
|
||||
"%d mesi",
|
||||
"%d mesi",
|
||||
"%d mesi"
|
||||
],
|
||||
"%d years": [
|
||||
"%d annu",
|
||||
"%d anni",
|
||||
"%d anni",
|
||||
"%d anni"
|
||||
],
|
||||
"Never": "Mai",
|
||||
"Note: This is a test service: Data may be deleted anytime. Kittens will die if you abuse this service.": "Nota : Què hè un serviziu di prova ; i dati ponu esse squassati à ogni mumentu. Parechji catorni anu da esse tombi s’è vò impiegate troppu stu serviziu.",
|
||||
"This document will expire in %d seconds.": [
|
||||
"Stu ducumentu serà scadutu in %d seconda.",
|
||||
"Stu ducumentu serà scadutu in %d seconde.",
|
||||
"Stu ducumentu serà scadutu in %d seconde.",
|
||||
"Stu ducumentu serà scadutu in %d seconde."
|
||||
],
|
||||
"This document will expire in %d minutes.": [
|
||||
"Stu ducumentu serà scadutu in %d minutu.",
|
||||
"Stu ducumentu serà scadutu in %d minuti.",
|
||||
"Stu ducumentu serà scadutu in %d minuti.",
|
||||
"Stu ducumentu serà scadutu in %d minuti."
|
||||
],
|
||||
"This document will expire in %d hours.": [
|
||||
"Stu ducumentu serà scadutu in %d ora.",
|
||||
"Stu ducumentu serà scadutu in %d ore.",
|
||||
"Stu ducumentu serà scadutu in %d ore.",
|
||||
"Stu ducumentu serà scadutu in %d ore."
|
||||
],
|
||||
"This document will expire in %d days.": [
|
||||
"Stu ducumentu serà scadutu in %d ghjornu.",
|
||||
"Stu ducumentu serà scadutu in %d ghjorni.",
|
||||
"Stu ducumentu serà scadutu in %d ghjorni.",
|
||||
"Stu ducumentu serà scadutu in %d ghjorni."
|
||||
],
|
||||
"This document will expire in %d months.": [
|
||||
"Stu ducumentu serà scadutu in %d mese.",
|
||||
"Stu ducumentu serà scadutu in %d mesi.",
|
||||
"Stu ducumentu serà scadutu in %d mesi.",
|
||||
"Stu ducumentu serà scadutu in %d mesi."
|
||||
],
|
||||
"Please enter the password for this paste:": "Stampittate a parolla d’intesa per st’appiccicu :",
|
||||
"Could not decrypt data (Wrong key?)": "Ùn si pò micca dicifrà i dati ; seria incurretta a chjave ?",
|
||||
"Could not delete the paste, it was not stored in burn after reading mode.": "Ùn si pò micca squassà l’appiccicu, ùn hè micca statu in u modu « Squassà dopu a lettura ».",
|
||||
"FOR YOUR EYES ONLY. Don't close this window, this message can't be displayed again.": "SOLU CÙ L’OCHJI. Ùn chjudite micca sta finestra, stu messaghju un puderà più esse affissatu torna.",
|
||||
"Could not decrypt comment; Wrong key?": "Ùn si pò micca dicifrà u cummentu. Seria incurretta a chjave ?",
|
||||
"Reply": "Risponde",
|
||||
"Anonymous": "Anonimu",
|
||||
"Avatar generated from IP address": "Avatar ingeneratu da l’indirizzu IP",
|
||||
"Add comment": "Aghjunghje un cummentu",
|
||||
"Optional nickname…": "Cugnome ozzionale…",
|
||||
"Post comment": "Impustà u cummentu",
|
||||
"Sending comment…": "Inviu di u cummentu…",
|
||||
"Comment posted.": "Cummentu inviatu.",
|
||||
"Could not refresh display: %s": "Ùn si pò micca attualizà l’affissera : %s",
|
||||
"unknown status": "statu scunnisciutu",
|
||||
"server error or not responding": "sbagliu di u servitore o u servitore ùn risponde micca",
|
||||
"Could not post comment: %s": "Ùn si pò micca impustà u cummentu : %s",
|
||||
"Sending paste…": "Inviu di l’appiccicu…",
|
||||
"Your paste is <a id=\"pasteurl\" href=\"%s\">%s</a> <span id=\"copyhint\">(Hit [Ctrl]+[c] to copy)</span>": "U vostru appiccicu si trova à l’indirizzu<a id=\"pasteurl\" href=\"%s\">%s</a> <span id=\"copyhint\">(Appughjate [Ctrl]+[c] per cupià u liame)</span>",
|
||||
"Delete data": "Squassà i dati",
|
||||
"Could not create paste: %s": "Ùn si pò micca creà l’appiccicu : %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 si pò micca dicifrà l’appiccicu : A chjave di dicifratura hè assente in l’indirizzu. Averiate impiegatu un orientadore d’indirizzu o un riduttore chì ammuzzeghja una parte di l’indirizzu ?",
|
||||
"B": "o",
|
||||
"KiB": "Ko",
|
||||
"MiB": "Mo",
|
||||
"GiB": "Go",
|
||||
"TiB": "To",
|
||||
"PiB": "Po",
|
||||
"EiB": "Eo",
|
||||
"ZiB": "Zo",
|
||||
"YiB": "Yo",
|
||||
"Format": "Furmatu",
|
||||
"Plain Text": "Testu in chjaru",
|
||||
"Source Code": "Codice di fonte",
|
||||
"Markdown": "Markdown",
|
||||
"Download attachment": "Scaricà a pezza aghjunta",
|
||||
"Cloned: '%s'": "Duppiatu : « %s »",
|
||||
"The cloned file '%s' was attached to this paste.": "U schedariu duppiatu « %s » hè statu aghjuntu à st’appiccicu.",
|
||||
"Attach a file": "Aghjunghje un schedariu",
|
||||
"alternatively drag & drop a file or paste an image from the clipboard": "in alternanza, sguillà è depone un schedariu o incullà una fiura da u preme’papei",
|
||||
"File too large, to display a preview. Please download the attachment.": "Schedariu troppu maiò per affissà una fighjulata. Scaricate a pezza aghjunta.",
|
||||
"Remove attachment": "Caccià a pezza aghjunta",
|
||||
"Your browser does not support uploading encrypted files. Please use a newer browser.": "U vostru navigatore ùn accetta micca l’inviu di i schedarii cifrati. Impiegate un navigatore più recente.",
|
||||
"Invalid attachment.": "A pezza aghjunta hè inaccettevule.",
|
||||
"Options": "Ozzioni",
|
||||
"Shorten URL": "Ammuzzà l’indirizzu",
|
||||
"Editor": "Editore",
|
||||
"Preview": "Fighjulata",
|
||||
"%s requires the PATH to end in a \"%s\". Please update the PATH in your index.php.": "%s richiede chì a variabile PATH si compii cù « %s ». Mudificate a variabile PATH in u vostru index.php.",
|
||||
"Decrypt": "Dicifrà",
|
||||
"Enter password": "Stampittate a parolla d’intesa",
|
||||
"Loading…": "Caricamentu…",
|
||||
"Decrypting paste…": "Dicifratura di l’appiccicu…",
|
||||
"Preparing new paste…": "Approntu di u novu appiccicu…",
|
||||
"In case this message never disappears please have a look at <a href=\"%s\">this FAQ for information to troubleshoot</a>.": "S’è stu messaghju ùn smarisce micca, lighjite <a href=\"%s\">sta FAQ per ottene infurmazioni annant’à a risuluzione di i prublemi</a>.",
|
||||
"+++ no paste text +++": "+++ nisunu testu incullatu +++",
|
||||
"Could not get paste data: %s": "Ùn si pò micca ottene i dati di l’appiccicu : %s",
|
||||
"QR code": "Codice QR",
|
||||
"This website is using an insecure HTTP connection! Please use it only for testing.": "Stu situ web impiegheghja una cunnessione HTTP non sicura ! impiegatelu solu per una prova.",
|
||||
"For more information <a href=\"%s\">see this FAQ entry</a>.": "Per sapene di più, <a href=\"%s\">lighjite sta rubrica di a FAQ</a>.",
|
||||
"Your browser may require an HTTPS connection to support the WebCrypto API. Try <a href=\"%s\">switching to HTTPS</a>.": "U vostru navigatore pò richiede una cunnessione HTTPS per permette l’usu di l’API WebCrypto. Pruvate di <a href=\"%s\">passà à HTTPS</a>.",
|
||||
"Your browser doesn't support WebAssembly, used for zlib compression. You can create uncompressed documents, but can't read compressed ones.": "U vostru navigatore ùn accetta micca WebAssembly, impiegatu per a cumpressione zlib. Pudete creà ducumenti micca cumpressi, ma ùn pudete micca leghje quelli chì sò cumpressi.",
|
||||
"waiting on user to provide a password": "in attesa di l’utilizatore per furnisce una parolla d’intesa",
|
||||
"Could not decrypt data. Did you enter a wrong password? Retry with the button at the top.": "Ùn si pò micca dicifrà i dati. Avete stampittatu una parolla d’intesa incurretta ? Pruvate torna cù u buttone insù.",
|
||||
"Retry": "Pruvà torna",
|
||||
"Showing raw text…": "Affissera di u testu grossu…",
|
||||
"Notice:": "Avertimentu :",
|
||||
"This link will expire after %s.": "Stu liame hà da scade dopu à %s.",
|
||||
"This link can only be accessed once, do not use back or refresh button in your browser.": "Stu liame pò esse accessu solu una volta, ùn impiegate micca i buttoni Precedente o Attualizà di u vostru navigatore.",
|
||||
"Link:": "Liame :",
|
||||
"Recipient may become aware of your timezone, convert time to UTC?": "U destinatariu pò cunnnosce u vostru fusu orariu. Vulete cunvertisce l’ora in u furmatu UTC ?",
|
||||
"Use Current Timezone": "Impiegà u fusu orariu attuale",
|
||||
"Convert To UTC": "Cunvertisce in UTC",
|
||||
"Close": "Chjode",
|
||||
"Encrypted note on PrivateBin": "Nota cifrata nant’à PrivateBin",
|
||||
"Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.": "Visitate stu liame per vede a nota. Date l’indirizzu à qualunque li permette d’accede à a nota dinù.",
|
||||
"URL shortener may expose your decrypt key in URL.": "Un ammuzzatore d’indirizzu pò palisà a vostra chjave di dicifratura in l’indirizzu.",
|
||||
"Save paste": "Arregistrà l’appiccicu",
|
||||
"Your IP is not authorized to create pastes.": "U vostru indirizzu IP ùn hè micca auturizatu à creà l’appiccichi."
|
||||
}
|
||||
+46
-44
@@ -6,7 +6,7 @@
|
||||
"en": "cs",
|
||||
"Paste does not exist, has expired or has been deleted.": "Vložený text neexistuje, expiroval nebo byl odstraněn.",
|
||||
"%s requires php %s or above to work. Sorry.": "%s vyžaduje php %s nebo vyšší. Lituji.",
|
||||
"%s requires configuration section [%s] to be present in configuration file.": "%s requires configuration section [%s] to be present in configuration file.",
|
||||
"%s requires configuration section [%s] to be present in configuration file.": "%s vyžaduje, aby byla v konfiguračním souboru přítomna sekce [%s].",
|
||||
"Please wait %d seconds between each post.": [
|
||||
"Počet sekund do dalšího příspěvku: %d.",
|
||||
"Počet sekund do dalšího příspěvku: %d.",
|
||||
@@ -19,10 +19,10 @@
|
||||
"Error saving comment. Sorry.": "Chyba při ukládání komentáře.",
|
||||
"Error saving paste. Sorry.": "Chyba při ukládání příspěvku.",
|
||||
"Invalid paste ID.": "Chybně vložené ID.",
|
||||
"Paste is not of burn-after-reading type.": "Paste is not of burn-after-reading type.",
|
||||
"Wrong deletion token. Paste was not deleted.": "Wrong deletion token. Paste was not deleted.",
|
||||
"Paste was properly deleted.": "Paste was properly deleted.",
|
||||
"JavaScript is required for %s to work. Sorry for the inconvenience.": "JavaScript is required for %s to work. Sorry for the inconvenience.",
|
||||
"Paste is not of burn-after-reading type.": "Příspěvek není nastaven na smazaní po přečtení.",
|
||||
"Wrong deletion token. Paste was not deleted.": "Chybný token pro odstranění. Příspěvek nebyl smazán.",
|
||||
"Paste was properly deleted.": "Příspěvek byl řádně smazán.",
|
||||
"JavaScript is required for %s to work. Sorry for the inconvenience.": "Pro fungování %s je vyžadován JavaScript. Omlouváme se za nepříjemnosti.",
|
||||
"%s requires a modern browser to work.": "%%s requires a modern browser to work.",
|
||||
"New": "Nový",
|
||||
"Send": "Odeslat",
|
||||
@@ -33,7 +33,7 @@
|
||||
"Open discussion": "Povolit komentáře",
|
||||
"Password (recommended)": "Heslo (doporučeno)",
|
||||
"Discussion": "Komentáře",
|
||||
"Toggle navigation": "Toggle navigation",
|
||||
"Toggle navigation": "Přepnout navigaci",
|
||||
"%d seconds": [
|
||||
"%d sekuda",
|
||||
"%d sekundy",
|
||||
@@ -77,7 +77,7 @@
|
||||
"%d years (3rd plural)"
|
||||
],
|
||||
"Never": "Nikdy",
|
||||
"Note: This is a test service: Data may be deleted anytime. Kittens will die if you abuse this service.": "Note: This is a test service: Data may be deleted anytime. Kittens will die if you abuse this service.",
|
||||
"Note: This is a test service: Data may be deleted anytime. Kittens will die if you abuse this service.": "Poznámka: Tato služba slouží k vyzkoušení: Data mohou být kdykoliv smazána. Při zneužití této služby zemřou koťátka.",
|
||||
"This document will expire in %d seconds.": [
|
||||
"Tento dokument expiruje za %d sekundu.",
|
||||
"Tento dokument expiruje za %d sekundy.",
|
||||
@@ -109,19 +109,19 @@
|
||||
"Tento dokument expiruje za %d měsíců."
|
||||
],
|
||||
"Please enter the password for this paste:": "Zadejte prosím heslo:",
|
||||
"Could not decrypt data (Wrong key?)": "Could not decrypt data (Wrong key?)",
|
||||
"Could not delete the paste, it was not stored in burn after reading mode.": "Could not delete the paste, it was not stored in burn after reading mode.",
|
||||
"FOR YOUR EYES ONLY. Don't close this window, this message can't be displayed again.": "FOR YOUR EYES ONLY. Don't close this window, this message can't be displayed again.",
|
||||
"Could not decrypt comment; Wrong key?": "Could not decrypt comment; Wrong key?",
|
||||
"Reply": "Reply",
|
||||
"Could not decrypt data (Wrong key?)": "Nepodařilo se dešifrovat data (Špatný klíč?)",
|
||||
"Could not delete the paste, it was not stored in burn after reading mode.": "Nepodařilo se odstranit příspěvek, nebyl uložen v režimu smazání po přečtení.",
|
||||
"FOR YOUR EYES ONLY. Don't close this window, this message can't be displayed again.": "POUZE PRO VAŠE OČI. Nezavírejte toto okno, tuto zprávu nelze znovu zobrazit.",
|
||||
"Could not decrypt comment; Wrong key?": "Nepodařilo se dešifrovat komentář; Špatný klíč?",
|
||||
"Reply": "Odpovědět",
|
||||
"Anonymous": "Anonym",
|
||||
"Avatar generated from IP address": "Avatar generated from IP address",
|
||||
"Avatar generated from IP address": "Avatar vygenerován z IP adresy",
|
||||
"Add comment": "Přidat komentář",
|
||||
"Optional nickname…": "Volitelný nickname…",
|
||||
"Post comment": "Odeslat komentář",
|
||||
"Sending comment…": "Odesílání komentáře…",
|
||||
"Comment posted.": "Komentář odeslán.",
|
||||
"Could not refresh display: %s": "Could not refresh display: %s",
|
||||
"Could not refresh display: %s": "Nepodařilo se obnovit zobrazení: %s",
|
||||
"unknown status": "neznámý stav",
|
||||
"server error or not responding": "Chyba na serveru nebo server neodpovídá",
|
||||
"Could not post comment: %s": "Nelze odeslat komentář: %s",
|
||||
@@ -145,44 +145,46 @@
|
||||
"Markdown": "Markdown",
|
||||
"Download attachment": "Stáhnout přílohu",
|
||||
"Cloned: '%s'": "Klonováno: '%s'",
|
||||
"The cloned file '%s' was attached to this paste.": "The cloned file '%s' was attached to this paste.",
|
||||
"The cloned file '%s' was attached to this paste.": "Naklonovaný soubor '%s' byl připojen k tomuto příspěvku.",
|
||||
"Attach a file": "Připojit soubor",
|
||||
"alternatively drag & drop a file or paste an image from the clipboard": "alternatively drag & drop a file or paste an image from the clipboard",
|
||||
"alternatively drag & drop a file or paste an image from the clipboard": "alternativně přetáhněte soubor nebo vložte obrázek ze schránky",
|
||||
"File too large, to display a preview. Please download the attachment.": "Soubor je příliš velký pro zobrazení náhledu. Stáhněte si přílohu.",
|
||||
"Remove attachment": "Odstranit přílohu",
|
||||
"Your browser does not support uploading encrypted files. Please use a newer browser.": "Váš prohlížeč nepodporuje nahrávání šifrovaných souborů. Použijte modernější verzi prohlížeče.",
|
||||
"Invalid attachment.": "Chybná příloha.",
|
||||
"Options": "Volby",
|
||||
"Shorten URL": "Shorten URL",
|
||||
"Shorten URL": "Zkrátit URL",
|
||||
"Editor": "Editor",
|
||||
"Preview": "Náhled",
|
||||
"%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.",
|
||||
"Decrypt": "Decrypt",
|
||||
"%s requires the PATH to end in a \"%s\". Please update the PATH in your index.php.": "%s vyžaduje, aby PATH skončil s \"%s\". Aktualizujte PATH ve vašem souboru index.php.",
|
||||
"Decrypt": "Dešifrovat",
|
||||
"Enter password": "Zadejte heslo",
|
||||
"Loading…": "Loading…",
|
||||
"Decrypting paste…": "Decrypting paste…",
|
||||
"Preparing new paste…": "Preparing new paste…",
|
||||
"In case this message never disappears please have a look at <a href=\"%s\">this FAQ for information to troubleshoot</a>.": "In case this message never disappears please have a look at <a href=\"%s\">this FAQ for information to troubleshoot</a>.",
|
||||
"Loading…": "Načítání…",
|
||||
"Decrypting paste…": "Dešifruji příspěvek…",
|
||||
"Preparing new paste…": "Připravuji nový příspěvek…",
|
||||
"In case this message never disappears please have a look at <a href=\"%s\">this FAQ for information to troubleshoot</a>.": "V případě, že tato zpráva nezmizí, se podívejte na <a href=\"%s\">tyto často kladené otázky pro řešení</a>.",
|
||||
"+++ no paste text +++": "+++ žádný vložený text +++",
|
||||
"Could not get paste data: %s": "Could not get paste data: %s",
|
||||
"QR code": "QR code",
|
||||
"This website is using an insecure HTTP connection! Please use it only for testing.": "This website is using an insecure HTTP connection! Please use it only for testing.",
|
||||
"For more information <a href=\"%s\">see this FAQ entry</a>.": "For more information <a href=\"%s\">see this FAQ entry</a>.",
|
||||
"Your browser may require an HTTPS connection to support the WebCrypto API. Try <a href=\"%s\">switching to HTTPS</a>.": "Your browser may require an HTTPS connection to support the WebCrypto API. Try <a href=\"%s\">switching to HTTPS</a>.",
|
||||
"Your browser doesn't support WebAssembly, used for zlib compression. You can create uncompressed documents, but can't read compressed ones.": "Your browser doesn't support WebAssembly, used for zlib compression. You can create uncompressed documents, but can't read compressed ones.",
|
||||
"waiting on user to provide a password": "waiting on user to provide a password",
|
||||
"Could not decrypt data. Did you enter a wrong password? Retry with the button at the top.": "Could not decrypt data. Did you enter a wrong password? Retry with the button at the top.",
|
||||
"Retry": "Retry",
|
||||
"Showing raw text…": "Showing raw text…",
|
||||
"Notice:": "Notice:",
|
||||
"This link will expire after %s.": "This link will expire after %s.",
|
||||
"This link can only be accessed once, do not use back or refresh button in your browser.": "This link can only be accessed once, do not use back or refresh button in your browser.",
|
||||
"Link:": "Link:",
|
||||
"Recipient may become aware of your timezone, convert time to UTC?": "Recipient may become aware of your timezone, convert time to UTC?",
|
||||
"Use Current Timezone": "Use Current Timezone",
|
||||
"Convert To UTC": "Convert To UTC",
|
||||
"Close": "Close",
|
||||
"Encrypted note on PrivateBin": "Encrypted note on PrivateBin",
|
||||
"Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.": "Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.",
|
||||
"URL shortener may expose your decrypt key in URL.": "URL shortener may expose your decrypt key in URL."
|
||||
"Could not get paste data: %s": "Nepodařilo se získat data příspěvku: %s",
|
||||
"QR code": "QR kód",
|
||||
"This website is using an insecure HTTP connection! Please use it only for testing.": "Tato stránka používá nezabezpečený připojení HTTP! Použijte ji prosím jen pro testování.",
|
||||
"For more information <a href=\"%s\">see this FAQ entry</a>.": "Více informací naleznete <a href=\"%s\">v této položce FAQ</a>.",
|
||||
"Your browser may require an HTTPS connection to support the WebCrypto API. Try <a href=\"%s\">switching to HTTPS</a>.": "Váš prohlížeč může vyžadovat připojení HTTPS pro podporu WebCrypto API. Zkuste <a href=\"%s\">přepnout na HTTPS</a>.",
|
||||
"Your browser doesn't support WebAssembly, used for zlib compression. You can create uncompressed documents, but can't read compressed ones.": "Váš prohlížeč nepodporuje WebAssembly, který se používá pro zlib kompresi. Můžete vytvořit nekomprimované dokumenty, ale nebudete moct číst ty komprimované.",
|
||||
"waiting on user to provide a password": "čekám na zadání hesla",
|
||||
"Could not decrypt data. Did you enter a wrong password? Retry with the button at the top.": "Nepodařilo se dešifrovat data. Zadali jste špatné heslo? Zkuste to znovu pomocí tlačítka nahoře.",
|
||||
"Retry": "Opakovat",
|
||||
"Showing raw text…": "Zobrazuji surový text…",
|
||||
"Notice:": "Upozornění:",
|
||||
"This link will expire after %s.": "Tento odkaz vyprší za %s.",
|
||||
"This link can only be accessed once, do not use back or refresh button in your browser.": "Tento odkaz je přístupný pouze jednou, nepoužívejte tlačítko zpět ani neobnovujte tuto stránku ve vašem prohlížeči.",
|
||||
"Link:": "Odkaz:",
|
||||
"Recipient may become aware of your timezone, convert time to UTC?": "Příjemce se může dozvědět o vašem časovém pásmu, převést čas na UTC?",
|
||||
"Use Current Timezone": "Použít aktuální časové pásmo",
|
||||
"Convert To UTC": "Převést na UTC",
|
||||
"Close": "Zavřít",
|
||||
"Encrypted note on PrivateBin": "Šifrovaná poznámka ve službě PrivateBin",
|
||||
"Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.": "Navštivte tento odkaz pro zobrazení poznámky. Přeposláním URL umožníte také jiným lidem přístup.",
|
||||
"URL shortener may expose your decrypt key in URL.": "Zkracovač URL může odhalit váš dešifrovací klíč v URL.",
|
||||
"Save paste": "Uložit příspěvek",
|
||||
"Your IP is not authorized to create pastes.": "Your IP is not authorized to create pastes."
|
||||
}
|
||||
|
||||
+7
-5
@@ -175,14 +175,16 @@
|
||||
"Retry": "Wiederholen",
|
||||
"Showing raw text…": "Rohtext wird angezeigt…",
|
||||
"Notice:": "Hinweis:",
|
||||
"This link will expire after %s.": "Diese Verknüpfung wird in %s ablaufen.",
|
||||
"This link can only be accessed once, do not use back or refresh button in your browser.": "Diese Verknüpfung kann nur einmal geöffnet werden, verwende nicht den Zurück- oder Neu-laden-Knopf Deines Browsers.",
|
||||
"Link:": "Verknüpfung:",
|
||||
"This link will expire after %s.": "Dieser Link wird am %s ablaufen.",
|
||||
"This link can only be accessed once, do not use back or refresh button in your browser.": "Dieser Link kann nur einmal geöffnet werden, verwende nicht den Zurück- oder Neu-laden-Knopf Deines Browsers.",
|
||||
"Link:": "Link:",
|
||||
"Recipient may become aware of your timezone, convert time to UTC?": "Der Empfänger könnte Deine Zeitzone erfahren, möchtest Du die Zeit in UTC umwandeln?",
|
||||
"Use Current Timezone": "Aktuelle Zeitzone verwenden",
|
||||
"Convert To UTC": "In UTC umwandeln",
|
||||
"Close": "Schliessen",
|
||||
"Encrypted note on PrivateBin": "Verschlüsselte Notiz auf PrivateBin",
|
||||
"Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.": "Besuche diese Verknüpfung um das Dokument zu sehen. Wird die URL an eine andere Person gegeben, so kann diese Person ebenfalls auf dieses Dokument zugreifen.",
|
||||
"URL shortener may expose your decrypt key in URL.": "Der URL-Verkürzer kann den Schlüssel in der URL enthüllen."
|
||||
"Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.": "Besuche diesen Link um das Dokument zu sehen. Wird die URL an eine andere Person gegeben, so kann diese Person ebenfalls auf dieses Dokument zugreifen.",
|
||||
"URL shortener may expose your decrypt key in URL.": "Der URL-Verkürzer kann den Schlüssel in der URL enthüllen.",
|
||||
"Save paste": "Text speichern",
|
||||
"Your IP is not authorized to create pastes.": "Deine IP ist nicht berechtigt, Texte zu erstellen."
|
||||
}
|
||||
|
||||
+3
-1
@@ -184,5 +184,7 @@
|
||||
"Close": "Close",
|
||||
"Encrypted note on PrivateBin": "Encrypted note on PrivateBin",
|
||||
"Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.": "Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.",
|
||||
"URL shortener may expose your decrypt key in URL.": "URL shortener may expose your decrypt key in URL."
|
||||
"URL shortener may expose your decrypt key in URL.": "URL shortener may expose your decrypt key in URL.",
|
||||
"Save paste": "Save paste",
|
||||
"Your IP is not authorized to create pastes.": "Your IP is not authorized to create pastes."
|
||||
}
|
||||
|
||||
+3
-1
@@ -184,5 +184,7 @@
|
||||
"Close": "Close",
|
||||
"Encrypted note on PrivateBin": "Encrypted note on PrivateBin",
|
||||
"Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.": "Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.",
|
||||
"URL shortener may expose your decrypt key in URL.": "URL shortener may expose your decrypt key in URL."
|
||||
"URL shortener may expose your decrypt key in URL.": "URL shortener may expose your decrypt key in URL.",
|
||||
"Save paste": "Save paste",
|
||||
"Your IP is not authorized to create pastes.": "Your IP is not authorized to create pastes."
|
||||
}
|
||||
|
||||
+3
-1
@@ -184,5 +184,7 @@
|
||||
"Close": "Cerrar",
|
||||
"Encrypted note on PrivateBin": "Nota cifrada en PrivateBin",
|
||||
"Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.": "Visite este enlace para ver la nota. Dar la URL a cualquier persona también les permite acceder a la nota.",
|
||||
"URL shortener may expose your decrypt key in URL.": "URL shortener may expose your decrypt key in URL."
|
||||
"URL shortener may expose your decrypt key in URL.": "El acortador de URL puede exponer su clave de descifrado en el URL.",
|
||||
"Save paste": "Guardar \"paste\"",
|
||||
"Your IP is not authorized to create pastes.": "Your IP is not authorized to create pastes."
|
||||
}
|
||||
|
||||
+190
@@ -0,0 +1,190 @@
|
||||
{
|
||||
"PrivateBin": "PrivateBin",
|
||||
"%s is a minimalist, open source online pastebin where the server has zero knowledge of pasted data. Data is encrypted/decrypted %sin the browser%s using 256 bits AES.": "%s on minimalistlik, avatud lähtekoodiga online pastebin, kus serveril pole kleebitud andmete kohta teadmist. Andmed krüpteeritakse/dekrüpteeritakse %sbrauseris%s kasutades 256-bitist AES-i.",
|
||||
"More information on the <a href=\"https://privatebin.info/\">project page</a>.": "Lisateave <a href=\"https://privatebin.info/\">projekti lehel</a>.",
|
||||
"Because ignorance is bliss": "Kuna teadmatus on õndsus",
|
||||
"en": "et",
|
||||
"Paste does not exist, has expired or has been deleted.": "Kleebet ei eksisteeri, on aegunud või on kustutatud.",
|
||||
"%s requires php %s or above to work. Sorry.": "%s vajab, et oleks php %s või kõrgem, et töötada. Vabandame.",
|
||||
"%s requires configuration section [%s] to be present in configuration file.": "%s vajab, et [%s] seadistamise jaotis oleks olemas konfiguratsioonifailis.",
|
||||
"Please wait %d seconds between each post.": [
|
||||
"Palun oota %d sekund iga postituse vahel.",
|
||||
"Palun oota %d sekundit iga postituse vahel.",
|
||||
"Palun oota %d sekundit iga postituse vahel.",
|
||||
"Palun oota %d sekundit iga postituse vahel."
|
||||
],
|
||||
"Paste is limited to %s of encrypted data.": "Kleepe limiit on %s krüpteeritud andmeid.",
|
||||
"Invalid data.": "Valed andmed.",
|
||||
"You are unlucky. Try again.": "Sul ei vea. Proovi uuesti.",
|
||||
"Error saving comment. Sorry.": "Viga kommentaari salvestamisel. Vabandame.",
|
||||
"Error saving paste. Sorry.": "Viga kleepe salvestamisel. Vabandame.",
|
||||
"Invalid paste ID.": "Vale kleepe ID.",
|
||||
"Paste is not of burn-after-reading type.": "Kleebe ei ole põleta-pärast-lugemist tüüpi.",
|
||||
"Wrong deletion token. Paste was not deleted.": "Vale kustutamiskood. Kleebet ei kustutatud.",
|
||||
"Paste was properly deleted.": "Kleebe kustutati korralikult.",
|
||||
"JavaScript is required for %s to work. Sorry for the inconvenience.": "JavaScript on vajalik %s'i töötamiseks. Vabandame ebamugavuste pärast.",
|
||||
"%s requires a modern browser to work.": "%s vajab töötamiseks kaasaegset brauserit.",
|
||||
"New": "Uus",
|
||||
"Send": "Saada",
|
||||
"Clone": "Klooni",
|
||||
"Raw text": "Lähtetekst",
|
||||
"Expires": "Aegub",
|
||||
"Burn after reading": "Põleta pärast lugemist",
|
||||
"Open discussion": "Avatud arutelu",
|
||||
"Password (recommended)": "Parool (soovitatav)",
|
||||
"Discussion": "Arutelu",
|
||||
"Toggle navigation": "Näita menüüd",
|
||||
"%d seconds": [
|
||||
"%d sekund",
|
||||
"%d sekundit",
|
||||
"%d sekundit",
|
||||
"%d sekundit"
|
||||
],
|
||||
"%d minutes": [
|
||||
"%d minut",
|
||||
"%d minutit",
|
||||
"%d minutit",
|
||||
"%d minutit"
|
||||
],
|
||||
"%d hours": [
|
||||
"%d tund",
|
||||
"%d tundi",
|
||||
"%d tundi",
|
||||
"%d tundi"
|
||||
],
|
||||
"%d days": [
|
||||
"%d päev",
|
||||
"%d päeva",
|
||||
"%d päeva",
|
||||
"%d päeva"
|
||||
],
|
||||
"%d weeks": [
|
||||
"%d nädal",
|
||||
"%d nädalat",
|
||||
"%d nädalat",
|
||||
"%d nädalat"
|
||||
],
|
||||
"%d months": [
|
||||
"%d kuu",
|
||||
"%d kuud",
|
||||
"%d kuud",
|
||||
"%d kuud"
|
||||
],
|
||||
"%d years": [
|
||||
"%d aasta",
|
||||
"%d aastat",
|
||||
"%d aastat",
|
||||
"%d aastat"
|
||||
],
|
||||
"Never": "Mitte kunagi",
|
||||
"Note: This is a test service: Data may be deleted anytime. Kittens will die if you abuse this service.": "Märge: See on testimisteenus: Andmeid võidakse igal ajal kustutada. Kiisupojad hukuvad, kui seda teenust kuritarvitad.",
|
||||
"This document will expire in %d seconds.": [
|
||||
"See dokument aegub %d sekundi pärast.",
|
||||
"See dokument aegub %d sekundi pärast.",
|
||||
"See dokument aegub %d sekundi pärast.",
|
||||
"See dokument aegub %d sekundi pärast."
|
||||
],
|
||||
"This document will expire in %d minutes.": [
|
||||
"See dokument aegub %d minuti pärast.",
|
||||
"See dokument aegub %d minuti pärast.",
|
||||
"See dokument aegub %d minuti pärast.",
|
||||
"See dokument aegub %d minuti pärast."
|
||||
],
|
||||
"This document will expire in %d hours.": [
|
||||
"See dokument aegub %d tunni pärast.",
|
||||
"See dokument aegub %d tunni pärast.",
|
||||
"See dokument aegub %d tunni pärast.",
|
||||
"See dokument aegub %d tunni pärast."
|
||||
],
|
||||
"This document will expire in %d days.": [
|
||||
"See dokument aegub %d päeva pärast.",
|
||||
"See dokument aegub %d päeva pärast.",
|
||||
"See dokument aegub %d päeva pärast.",
|
||||
"See dokument aegub %d päeva pärast."
|
||||
],
|
||||
"This document will expire in %d months.": [
|
||||
"See dokument aegub %d kuu pärast.",
|
||||
"See dokument aegub %d kuu pärast.",
|
||||
"See dokument aegub %d kuu pärast.",
|
||||
"See dokument aegub %d kuu pärast."
|
||||
],
|
||||
"Please enter the password for this paste:": "Palun sisesta selle kleepe parool:",
|
||||
"Could not decrypt data (Wrong key?)": "Ei suutnud andmeid dekrüpteerida (Vale võti?)",
|
||||
"Could not delete the paste, it was not stored in burn after reading mode.": "Ei suutnud kleebet kustutada, seda ei salvestatud põleta pärast lugemist režiimis.",
|
||||
"FOR YOUR EYES ONLY. Don't close this window, this message can't be displayed again.": "AINULT SINU SILMADELE. Ära sulge seda akent, seda sõnumit ei saa enam kuvada.",
|
||||
"Could not decrypt comment; Wrong key?": "Ei suutnud kommentaari dekrüpteerida; Vale võti?",
|
||||
"Reply": "Vasta",
|
||||
"Anonymous": "Anonüümne",
|
||||
"Avatar generated from IP address": "Avatar genereeritud IP aadressi põhjal",
|
||||
"Add comment": "Lisa kommentaar",
|
||||
"Optional nickname…": "Valikuline hüüdnimi…",
|
||||
"Post comment": "Postita kommentaar",
|
||||
"Sending comment…": "Kommentaari saatmine…",
|
||||
"Comment posted.": "Kommentaar postitatud.",
|
||||
"Could not refresh display: %s": "Ei suutnud kuva värskendada: %s",
|
||||
"unknown status": "tundmatu staatus",
|
||||
"server error or not responding": "serveri viga või ei vasta",
|
||||
"Could not post comment: %s": "Ei suutnud kommentaari postitada: %s",
|
||||
"Sending paste…": "Kleepe saatmine…",
|
||||
"Your paste is <a id=\"pasteurl\" href=\"%s\">%s</a> <span id=\"copyhint\">(Hit [Ctrl]+[c] to copy)</span>": "Sinu kleebe on <a id=\"pasteurl\" href=\"%s\">%s</a> <span id=\"copyhint\">(Kopeerimiseks vajuta [Ctrl]+[c])</span>",
|
||||
"Delete data": "Kustuta andmed",
|
||||
"Could not create paste: %s": "Ei suutnud kleebet luua: %s",
|
||||
"Cannot decrypt paste: Decryption key missing in URL (Did you use a redirector or an URL shortener which strips part of the URL?)": "Ei suutnud kleebet dekrüpteerida: Dekrüpteerimisvõti on URL-ist puudu (Kas kasutasid ümbersuunajat või URL-i lühendajat, mis eemaldab osa URL-ist?)",
|
||||
"B": "B",
|
||||
"KiB": "KiB",
|
||||
"MiB": "MiB",
|
||||
"GiB": "GiB",
|
||||
"TiB": "TiB",
|
||||
"PiB": "PiB",
|
||||
"EiB": "EiB",
|
||||
"ZiB": "ZiB",
|
||||
"YiB": "YiB",
|
||||
"Format": "Formaat",
|
||||
"Plain Text": "Lihttekst",
|
||||
"Source Code": "Lähtekood",
|
||||
"Markdown": "Markdown",
|
||||
"Download attachment": "Laadi manus alla",
|
||||
"Cloned: '%s'": "Kloonitud: '%s'",
|
||||
"The cloned file '%s' was attached to this paste.": "Kloonitud fail '%s' manustati sellele kleepele.",
|
||||
"Attach a file": "Manusta fail",
|
||||
"alternatively drag & drop a file or paste an image from the clipboard": "teise võimalusena lohista fail või kleebi pilt lõikelaualt",
|
||||
"File too large, to display a preview. Please download the attachment.": "Fail on eelvaate kuvamiseks liiga suur. Palun laadi manus alla.",
|
||||
"Remove attachment": "Eemalda manus",
|
||||
"Your browser does not support uploading encrypted files. Please use a newer browser.": "Sinu brauser ei toeta krüpteeritud failide üleslaadimist. Palun kasuta uuemat brauserit.",
|
||||
"Invalid attachment.": "Sobimatu manus.",
|
||||
"Options": "Valikud",
|
||||
"Shorten URL": "Lühenda URL",
|
||||
"Editor": "Toimetaja",
|
||||
"Preview": "Eelvaade",
|
||||
"%s requires the PATH to end in a \"%s\". Please update the PATH in your index.php.": "%s vajab, et PATH lõppeks järgmisega: \"%s\". Palun uuenda PATH-i oma index.php failis.",
|
||||
"Decrypt": "Dekrüpteeri",
|
||||
"Enter password": "Sisesta parool",
|
||||
"Loading…": "Laadimine…",
|
||||
"Decrypting paste…": "Kleepe dekrüpteerimine…",
|
||||
"Preparing new paste…": "Uue kleepe ettevalmistamine…",
|
||||
"In case this message never disappears please have a look at <a href=\"%s\">this FAQ for information to troubleshoot</a>.": "Kui see sõnum ei kao, palun vaata <a href=\"%s\">seda KKK-d, et saada tõrkeotsinguks teavet.</a>.",
|
||||
"+++ no paste text +++": "+++ kleepe tekst puudub +++",
|
||||
"Could not get paste data: %s": "Ei suutnud saada kleepe andmeid: %s",
|
||||
"QR code": "QR kood",
|
||||
"This website is using an insecure HTTP connection! Please use it only for testing.": "See veebisait kasutab ebaturvalist HTTP ühendust! Palun kasuta seda ainult katsetamiseks.",
|
||||
"For more information <a href=\"%s\">see this FAQ entry</a>.": "Lisateabe saamiseks <a href=\"%s\">vaata seda KKK sissekannet</a>.",
|
||||
"Your browser may require an HTTPS connection to support the WebCrypto API. Try <a href=\"%s\">switching to HTTPS</a>.": "Sinu brauser võib vajada HTTPS ühendust, et toetada WebCrypto API-d. Proovi <a href=\"%s\">üle minna HTTPS-ile</a>.",
|
||||
"Your browser doesn't support WebAssembly, used for zlib compression. You can create uncompressed documents, but can't read compressed ones.": "Sinu brauser ei toeta WebAssembly't, mida kasutatakse zlib tihendamiseks. Sa saad luua tihendamata dokumente, kuid ei saa lugeda tihendatuid.",
|
||||
"waiting on user to provide a password": "ootan parooli sisestamist kasutajalt",
|
||||
"Could not decrypt data. Did you enter a wrong password? Retry with the button at the top.": "Ei suutnud andmeid dekrüpteerida. Kas sisestasid vale parooli? Proovi uuesti üleval asuva nupuga.",
|
||||
"Retry": "Proovi uuesti",
|
||||
"Showing raw text…": "Lähteteksti näitamine…",
|
||||
"Notice:": "Teade:",
|
||||
"This link will expire after %s.": "See link aegub: %s.",
|
||||
"This link can only be accessed once, do not use back or refresh button in your browser.": "Sellele lingile saab vaid üks kord ligi pääseda, ära kasuta tagasi või värskenda nuppe sinu brauseris.",
|
||||
"Link:": "Link:",
|
||||
"Recipient may become aware of your timezone, convert time to UTC?": "Saaja võib saada teada sinu ajavööndi, kas teisendada aeg UTC-ks?",
|
||||
"Use Current Timezone": "Kasuta praegust ajavööndit",
|
||||
"Convert To UTC": "Teisenda UTC-ks",
|
||||
"Close": "Sulge",
|
||||
"Encrypted note on PrivateBin": "Krüpteeritud kiri PrivateBin-is",
|
||||
"Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.": "Kirja nägemiseks külasta seda linki. Teistele URL-i andmine lubab ka neil ligi pääseda kirjale.",
|
||||
"URL shortener may expose your decrypt key in URL.": "URL-i lühendaja võib paljastada sinu dekrüpteerimisvõtme URL-is.",
|
||||
"Save paste": "Salvesta kleebe",
|
||||
"Your IP is not authorized to create pastes.": "Your IP is not authorized to create pastes."
|
||||
}
|
||||
+190
@@ -0,0 +1,190 @@
|
||||
{
|
||||
"PrivateBin": "PrivateBin",
|
||||
"%s is a minimalist, open source online pastebin where the server has zero knowledge of pasted data. Data is encrypted/decrypted %sin the browser%s using 256 bits AES.": "%s on minimalistinen, avoimen lähdekoodin online pastebin jossa palvelimella ei ole tietoa syötetystä datasta. Data salataan/puretaan %sselaimessa%s käyttäen 256-bittistä AES:ää.",
|
||||
"More information on the <a href=\"https://privatebin.info/\">project page</a>.": "Enemmän tietoa <a href=\"https://privatebin.info/\">projektisivulla</a>.",
|
||||
"Because ignorance is bliss": "Koska tieto lisää tuskaa",
|
||||
"en": "fi",
|
||||
"Paste does not exist, has expired or has been deleted.": "Pastea ei ole olemassa, se on vanhentunut, tai se on poistettu.",
|
||||
"%s requires php %s or above to work. Sorry.": "%s tarvitsee php %s-versiota tai uudempaa toimiakseen. Anteeksi.",
|
||||
"%s requires configuration section [%s] to be present in configuration file.": "%s vaatii konfiguraatio-osion [%s] olevan läsnä konfiguraatiotiedostossa.",
|
||||
"Please wait %d seconds between each post.": [
|
||||
"Odotathan %d sekuntin jokaisen lähetyksen välillä.",
|
||||
"Odotathan %d sekuntia jokaisen lähetyksen välillä.",
|
||||
"Odotathan %d sekuntia jokaisen lähetyksen välillä.",
|
||||
"Odotathan %d sekuntia jokaisen lähetyksen välillä."
|
||||
],
|
||||
"Paste is limited to %s of encrypted data.": "Paste on rajoitettu kokoon %s salattua dataa.",
|
||||
"Invalid data.": "Virheellinen data.",
|
||||
"You are unlucky. Try again.": "Olet epäonnekas. Yritä uudelleen.",
|
||||
"Error saving comment. Sorry.": "Virhe kommenttia tallentaessa. Anteeksi.",
|
||||
"Error saving paste. Sorry.": "Virhe pastea tallentaessa. Anteeksi.",
|
||||
"Invalid paste ID.": "Virheellinen paste ID.",
|
||||
"Paste is not of burn-after-reading type.": "Paste ei ole polta-lukemisen-jälkeen-tyyppiä.",
|
||||
"Wrong deletion token. Paste was not deleted.": "Virheellinen poistotunniste. Pastea ei poistettu.",
|
||||
"Paste was properly deleted.": "Paste poistettiin kunnolla.",
|
||||
"JavaScript is required for %s to work. Sorry for the inconvenience.": "JavaScriptiä tarvitaan jotta %s toimisi. Anteeksi haitasta.",
|
||||
"%s requires a modern browser to work.": "%s tarvitsee modernia selainta toimiakseen.",
|
||||
"New": "Uusi",
|
||||
"Send": "Lähetä",
|
||||
"Clone": "Kloonaa",
|
||||
"Raw text": "Raaka teksti",
|
||||
"Expires": "Vanhenee",
|
||||
"Burn after reading": "Polta lukemisen jälkeen",
|
||||
"Open discussion": "Avaa keskustelu",
|
||||
"Password (recommended)": "Salasana (suositeltu)",
|
||||
"Discussion": "Keskustelu",
|
||||
"Toggle navigation": "Navigointi päällä/pois",
|
||||
"%d seconds": [
|
||||
"%d sekunti",
|
||||
"%d sekuntia",
|
||||
"%d sekuntia",
|
||||
"%d sekuntia"
|
||||
],
|
||||
"%d minutes": [
|
||||
"%d minuutti",
|
||||
"%d minuuttia",
|
||||
"%d minuuttia",
|
||||
"%d minuuttia"
|
||||
],
|
||||
"%d hours": [
|
||||
"%d tunti",
|
||||
"%d tuntia",
|
||||
"%d tuntia",
|
||||
"%d tuntia"
|
||||
],
|
||||
"%d days": [
|
||||
"%d päivä",
|
||||
"%d päivää",
|
||||
"%d päivää",
|
||||
"%d päivää"
|
||||
],
|
||||
"%d weeks": [
|
||||
"%d viikko",
|
||||
"%d viikkoa",
|
||||
"%d viikkoa",
|
||||
"%d viikkoa"
|
||||
],
|
||||
"%d months": [
|
||||
"%d kuukausi",
|
||||
"%d kuukautta",
|
||||
"%d kuukautta",
|
||||
"%d kuukautta"
|
||||
],
|
||||
"%d years": [
|
||||
"%d vuosi",
|
||||
"%d vuotta",
|
||||
"%d vuotta",
|
||||
"%d vuotta"
|
||||
],
|
||||
"Never": "Ei koskaan",
|
||||
"Note: This is a test service: Data may be deleted anytime. Kittens will die if you abuse this service.": "Huomaa: Tämä on testipalvelu: Data voidaan poistaa milloin tahansa. Kissanpennut kuolevat jos väärinkäytät tätä palvelua.",
|
||||
"This document will expire in %d seconds.": [
|
||||
"Tämä dokumentti vanhenee %d sekuntissa.",
|
||||
"Tämä dokumentti vanhenee %d sekunnissa.",
|
||||
"Tämä dokumentti vanhenee %d sekunnissa.",
|
||||
"Tämä dokumentti vanhenee %d sekunnissa."
|
||||
],
|
||||
"This document will expire in %d minutes.": [
|
||||
"Tämä dokumentti vanhenee %d minuutissa.",
|
||||
"Tämä dokumentti vanhenee %d minuutissa.",
|
||||
"Tämä dokumentti vanhenee %d minuutissa.",
|
||||
"Tämä dokumentti vanhenee %d minuutissa."
|
||||
],
|
||||
"This document will expire in %d hours.": [
|
||||
"Tämä dokumentti vanhenee %d tunnissa.",
|
||||
"Tämä dokumentti vanhenee %d tunnissa.",
|
||||
"Tämä dokumentti vanhenee %d tunnissa.",
|
||||
"Tämä dokumentti vanhenee %d tunnissa."
|
||||
],
|
||||
"This document will expire in %d days.": [
|
||||
"Tämä dokumentti vanhenee %d päivässä.",
|
||||
"Tämä dokumentti vanhenee %d päivässä.",
|
||||
"Tämä dokumentti vanhenee %d päivässä.",
|
||||
"Tämä dokumentti vanhenee %d päivässä."
|
||||
],
|
||||
"This document will expire in %d months.": [
|
||||
"Tämä dokumentti vanhenee %d kuukaudessa.",
|
||||
"Tämä dokumentti vanhenee %d kuukaudessa.",
|
||||
"Tämä dokumentti vanhenee %d kuukaudessa.",
|
||||
"Tämä dokumentti vanhenee %d kuukaudessa."
|
||||
],
|
||||
"Please enter the password for this paste:": "Syötä salasana tälle pastelle:",
|
||||
"Could not decrypt data (Wrong key?)": "Dataa ei voitu purkaa (Väärä avain?)",
|
||||
"Could not delete the paste, it was not stored in burn after reading mode.": "Pastea ei voitu poistaa, sitä ei säilytetty \"Polta lukemisen jälkeen\" -tilassa.",
|
||||
"FOR YOUR EYES ONLY. Don't close this window, this message can't be displayed again.": "VAIN SINUN SILMILLESI. Älä sulje tätä ikkunaa, tätä viestiä ei voida näyttää uudelleen.",
|
||||
"Could not decrypt comment; Wrong key?": "Kommenttia ei voitu purkaa; väärä avain?",
|
||||
"Reply": "Vastaa",
|
||||
"Anonymous": "Anonyymi",
|
||||
"Avatar generated from IP address": "Avatar generoitu IP-osoitteesta",
|
||||
"Add comment": "Lisää kommentti",
|
||||
"Optional nickname…": "Valinnainen nimimerkki…",
|
||||
"Post comment": "Lähetä kommentti",
|
||||
"Sending comment…": "Lähetetään kommenttia…",
|
||||
"Comment posted.": "Kommentti lähetetty.",
|
||||
"Could not refresh display: %s": "Näyttöä ei voitu päivittää: %s",
|
||||
"unknown status": "tuntematon status",
|
||||
"server error or not responding": "palvelinvirhe tai palvelin ei vastaa",
|
||||
"Could not post comment: %s": "Kommenttia ei voitu lähettää: %s",
|
||||
"Sending paste…": "Lähetetään pastea…",
|
||||
"Your paste is <a id=\"pasteurl\" href=\"%s\">%s</a> <span id=\"copyhint\">(Hit [Ctrl]+[c] to copy)</span>": "Pastesi on <a id=\"pasteurl\" href=\"%s\">%s</a> <span id=\"copyhint\">(Paina [Ctrl]+[c] kopioidaksesi)</span>",
|
||||
"Delete data": "Poista data",
|
||||
"Could not create paste: %s": "Pastea ei voitu luoda: %s",
|
||||
"Cannot decrypt paste: Decryption key missing in URL (Did you use a redirector or an URL shortener which strips part of the URL?)": "Pastea ei voitu purkaa: Purkausavain puuttuu URL:stä (Käytitkö uudelleenohjaajaa tai URL-lyhentäjää joka poistaa osan URL:stä?)",
|
||||
"B": "B",
|
||||
"KiB": "KiB",
|
||||
"MiB": "MiB",
|
||||
"GiB": "GiB",
|
||||
"TiB": "TiB",
|
||||
"PiB": "PiB",
|
||||
"EiB": "EiB",
|
||||
"ZiB": "ZiB",
|
||||
"YiB": "YiB",
|
||||
"Format": "Formaatti",
|
||||
"Plain Text": "Perusteksti",
|
||||
"Source Code": "Lähdekoodi",
|
||||
"Markdown": "Markdown",
|
||||
"Download attachment": "Lataa liite",
|
||||
"Cloned: '%s'": "Kloonattu: '%s'",
|
||||
"The cloned file '%s' was attached to this paste.": "Kloonattu tiedosto '%s' liitettiin tähän pasteen",
|
||||
"Attach a file": "Liitä tiedosto",
|
||||
"alternatively drag & drop a file or paste an image from the clipboard": "vaihtoehtoisesti vedä & pudota tiedosto tai liitä kuva leikepöydältä",
|
||||
"File too large, to display a preview. Please download the attachment.": "Tiedosto on liian iso esikatselun näyttämiseksi. Lataathan liitteen.",
|
||||
"Remove attachment": "Poista liite",
|
||||
"Your browser does not support uploading encrypted files. Please use a newer browser.": "Selaimesi ei tue salattujen tiedostojen lataamista. Käytäthän uudempaa selainta.",
|
||||
"Invalid attachment.": "Virheellinen liite.",
|
||||
"Options": "Asetukset",
|
||||
"Shorten URL": "Lyhennä URL",
|
||||
"Editor": "Muokkaaja",
|
||||
"Preview": "Esikatselu",
|
||||
"%s requires the PATH to end in a \"%s\". Please update the PATH in your index.php.": "%s vaatii PATH:in loppuvan \"%s\"-merkkiin. Päivitäthän PATH:in index.php:ssäsi.",
|
||||
"Decrypt": "Pura",
|
||||
"Enter password": "Syötä salasana",
|
||||
"Loading…": "Ladataan…",
|
||||
"Decrypting paste…": "Puretaan pastea…",
|
||||
"Preparing new paste…": "Valmistellaan uutta pastea",
|
||||
"In case this message never disappears please have a look at <a href=\"%s\">this FAQ for information to troubleshoot</a>.": "Jos tämä viesti ei katoa koskaan, katsothan <a href=\"%s\">tämän FAQ:n ongelmanratkaisutiedon löytämiseksi</a>.",
|
||||
"+++ no paste text +++": "+++ ei paste-tekstiä +++",
|
||||
"Could not get paste data: %s": "Paste-tietoja ei löydetty: %s",
|
||||
"QR code": "QR-koodi",
|
||||
"This website is using an insecure HTTP connection! Please use it only for testing.": "Tämä sivusto käyttää epäturvallista HTTP-yhteyttä! Käytäthän sitä vain testaukseen.",
|
||||
"For more information <a href=\"%s\">see this FAQ entry</a>.": "Lisätietoja varten <a href=\"%s\">lue tämä FAQ-kohta</a>.",
|
||||
"Your browser may require an HTTPS connection to support the WebCrypto API. Try <a href=\"%s\">switching to HTTPS</a>.": "Selaimesi ehkä tarvitsee HTTPS-yhteyden tukeakseen WebCrypto API:a. Yritä <a href=\"%s\">vaihtamista HTTPS:ään</a>.",
|
||||
"Your browser doesn't support WebAssembly, used for zlib compression. You can create uncompressed documents, but can't read compressed ones.": "Selaimesi ei tue WebAssemblyä jota käytetään zlib-pakkaamiseen. Voit luoda pakkaamattomia dokumentteja, mutta et voi lukea pakattuja dokumentteja.",
|
||||
"waiting on user to provide a password": "odotetaan käyttäjän antavan salasanan",
|
||||
"Could not decrypt data. Did you enter a wrong password? Retry with the button at the top.": "Dataa ei voitu purkaa. Syötitkö väärän salasanan? Yritä uudelleen ylhäällä olevalla painikkeella.",
|
||||
"Retry": "Yritä uudelleen",
|
||||
"Showing raw text…": "Näytetään raaka reksti…",
|
||||
"Notice:": "Huomautus:",
|
||||
"This link will expire after %s.": "Tämä linkki vanhenee ajan %s jälkeen.",
|
||||
"This link can only be accessed once, do not use back or refresh button in your browser.": "Tätä linkkiä voidaan käyttää vain kerran, älä käytä taaksepäin- tai päivityspainiketta selaimessasi.",
|
||||
"Link:": "Linkki:",
|
||||
"Recipient may become aware of your timezone, convert time to UTC?": "Vastaanottaja saattaa tulla tietoiseksi aikavyöhykkeestäsi, muutetaanko aika UTC:ksi?",
|
||||
"Use Current Timezone": "Käytä nykyistä aikavyöhykettä",
|
||||
"Convert To UTC": "Muuta UTC:ksi",
|
||||
"Close": "Sulje",
|
||||
"Encrypted note on PrivateBin": "Salattu viesti PrivateBinissä",
|
||||
"Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.": "Käy tässä linkissä nähdäksesi viestin. URL:n antaminen kenellekään antaa heidänkin päästä katsomeen viestiä. ",
|
||||
"URL shortener may expose your decrypt key in URL.": "URL-lyhentäjä voi paljastaa purkuavaimesi URL:ssä.",
|
||||
"Save paste": "Tallenna paste",
|
||||
"Your IP is not authorized to create pastes.": "IP:llesi ei ole annettu oikeutta luoda pasteja."
|
||||
}
|
||||
+4
-2
@@ -2,7 +2,7 @@
|
||||
"PrivateBin": "PrivateBin",
|
||||
"%s is a minimalist, open source online pastebin where the server has zero knowledge of pasted data. Data is encrypted/decrypted %sin the browser%s using 256 bits AES.": "%s est un 'pastebin' (ou gestionnaire d'extraits de texte et de code source) minimaliste et open source, dans lequel le serveur n'a aucune connaissance des données envoyées. Les données sont chiffrées/déchiffrées %sdans le navigateur%s par un chiffrement AES 256 bits.",
|
||||
"More information on the <a href=\"https://privatebin.info/\">project page</a>.": "Plus d'informations sur <a href=\"https://privatebin.info/\">la page du projet</a>.",
|
||||
"Because ignorance is bliss": "Parce que l'ignorance c'est le bonheur",
|
||||
"Because ignorance is bliss": "Vivons heureux, vivons cachés",
|
||||
"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 %s or above to work. Sorry.": "Désolé, %s nécessite php %s ou supérieur pour fonctionner.",
|
||||
@@ -184,5 +184,7 @@
|
||||
"Close": "Fermer",
|
||||
"Encrypted note on PrivateBin": "Message chiffré sur PrivateBin",
|
||||
"Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.": "Visiter ce lien pour voir la note. Donner l'URL à une autre personne lui permet également d'accéder à la note.",
|
||||
"URL shortener may expose your decrypt key in URL.": "Raccourcir l'URL peut exposer votre clé de déchiffrement dans l'URL."
|
||||
"URL shortener may expose your decrypt key in URL.": "Raccourcir l'URL peut exposer votre clé de déchiffrement dans l'URL.",
|
||||
"Save paste": "Sauver le paste",
|
||||
"Your IP is not authorized to create pastes.": "Votre adresse IP n'est pas autorisée à créer des pastes."
|
||||
}
|
||||
|
||||
+3
-1
@@ -184,5 +184,7 @@
|
||||
"Close": "סגירה",
|
||||
"Encrypted note on PrivateBin": "הערה מוצפנת ב־PrivateBin",
|
||||
"Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.": "נא לבקר בקישור כדי לצפות בהערה. מסירת הקישור לאנשים כלשהם תאפשר גם להם לגשת להערה.",
|
||||
"URL shortener may expose your decrypt key in URL.": "URL shortener may expose your decrypt key in URL."
|
||||
"URL shortener may expose your decrypt key in URL.": "URL shortener may expose your decrypt key in URL.",
|
||||
"Save paste": "Save paste",
|
||||
"Your IP is not authorized to create pastes.": "Your IP is not authorized to create pastes."
|
||||
}
|
||||
|
||||
+3
-1
@@ -184,5 +184,7 @@
|
||||
"Close": "Close",
|
||||
"Encrypted note on PrivateBin": "Encrypted note on PrivateBin",
|
||||
"Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.": "Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.",
|
||||
"URL shortener may expose your decrypt key in URL.": "URL shortener may expose your decrypt key in URL."
|
||||
"URL shortener may expose your decrypt key in URL.": "URL shortener may expose your decrypt key in URL.",
|
||||
"Save paste": "Save paste",
|
||||
"Your IP is not authorized to create pastes.": "Your IP is not authorized to create pastes."
|
||||
}
|
||||
|
||||
+3
-1
@@ -184,5 +184,7 @@
|
||||
"Close": "Bezárás",
|
||||
"Encrypted note on PrivateBin": "Titkosított jegyzet a PrivateBinen",
|
||||
"Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.": "Látogasd meg ezt a hivatkozást a bejegyzés megtekintéséhez. Ha mások számára is megadod ezt a linket, azzal hozzáférnek ők is.",
|
||||
"URL shortener may expose your decrypt key in URL.": "URL shortener may expose your decrypt key in URL."
|
||||
"URL shortener may expose your decrypt key in URL.": "URL shortener may expose your decrypt key in URL.",
|
||||
"Save paste": "Save paste",
|
||||
"Your IP is not authorized to create pastes.": "Your IP is not authorized to create pastes."
|
||||
}
|
||||
|
||||
+24
-22
@@ -8,10 +8,10 @@
|
||||
"%s requires php %s or above to work. Sorry.": "%s memerlukan php %s atau versi diatasnya untuk dapat dijalankan. Maaf.",
|
||||
"%s requires configuration section [%s] to be present in configuration file.": "%s membutuhkan bagian konfigurasi [%s] untuk ada di file konfigurasi.",
|
||||
"Please wait %d seconds between each post.": [
|
||||
"Silahkan menunggu %d detik antara masing-masing postingan. (tunggal)",
|
||||
"Silahkan menunggu %d detik antara masing-masing postingan. (jamak ke-1)",
|
||||
"Silahkan menunggu %d detik antara masing-masing postingan. (jamak ke-2)",
|
||||
"Silahkan menunggu %d detik antara masing-masing postingan. (jamak ke-3)"
|
||||
"Silahkan menunggu %d detik antara masing-masing postingan.",
|
||||
"Silahkan menunggu %d detik antara masing-masing postingan.",
|
||||
"Silahkan menunggu %d detik antara masing-masing postingan.",
|
||||
"Silahkan menunggu %d detik antara masing-masing postingan."
|
||||
],
|
||||
"Paste is limited to %s of encrypted data.": "Paste dibatasi sampai %s dari data yang dienskripsi.",
|
||||
"Invalid data.": "Data tidak valid.",
|
||||
@@ -35,31 +35,31 @@
|
||||
"Discussion": "Diskusi",
|
||||
"Toggle navigation": "Alihkan navigasi",
|
||||
"%d seconds": [
|
||||
"%d detik (tunggal)",
|
||||
"%d detik (jamak ke-1)",
|
||||
"%d detik (jamak ke-2)",
|
||||
"%d detik (jamak ke-3)"
|
||||
"%d detik",
|
||||
"%d detik",
|
||||
"%d detik",
|
||||
"%d detik"
|
||||
],
|
||||
"%d minutes": [
|
||||
"%d menit (tunggal)",
|
||||
"%d menit (jamak ke-1)",
|
||||
"%d menit (jamak ke-2)",
|
||||
"%d menit (jamak ke-3)"
|
||||
"%d menit",
|
||||
"%d menit",
|
||||
"%d menit",
|
||||
"%d menit"
|
||||
],
|
||||
"%d hours": [
|
||||
"%d jam (tunggal)",
|
||||
"%d jam (jamak ke-1)",
|
||||
"%d jam (jamak ke-2)",
|
||||
"%d jam (jamak ke-3)"
|
||||
"%d jam",
|
||||
"%d jam",
|
||||
"%d jam",
|
||||
"%d jam"
|
||||
],
|
||||
"%d days": [
|
||||
"%d hari (tunggal)",
|
||||
"%d hari (jamak ke-1)",
|
||||
"%d hari (jamak ke-2)",
|
||||
"%d hari (jamak ke-3)"
|
||||
"%d hari",
|
||||
"%d hari",
|
||||
"%d hari",
|
||||
"%d hari"
|
||||
],
|
||||
"%d weeks": [
|
||||
"%d minggu (tunggal)",
|
||||
"%d minggu",
|
||||
"%d minggu",
|
||||
"%d minggu",
|
||||
"%d minggu"
|
||||
@@ -184,5 +184,7 @@
|
||||
"Close": "Tutup",
|
||||
"Encrypted note on PrivateBin": "Catatan ter-ekrip di PrivateBin",
|
||||
"Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.": "Kunjungi tautan ini untuk melihat catatan. Memberikan alamat URL pada siapapun juga, akan mengizinkan mereka untuk mengakses catatan, so pasti gitu loh Kaka.",
|
||||
"URL shortener may expose your decrypt key in URL.": "URL shortener may expose your decrypt key in URL."
|
||||
"URL shortener may expose your decrypt key in URL.": "Pemendek URL mungkin akan menampakkan kunci dekrip Anda dalam URL.",
|
||||
"Save paste": "Simpan paste",
|
||||
"Your IP is not authorized to create pastes.": "Your IP is not authorized to create pastes."
|
||||
}
|
||||
|
||||
+3
-1
@@ -184,5 +184,7 @@
|
||||
"Close": "Chiudi",
|
||||
"Encrypted note on PrivateBin": "Nota crittografata su PrivateBin",
|
||||
"Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.": "Visita questo collegamento per vedere la nota. Dare l'URL a chiunque consente anche a loro di accedere alla nota.",
|
||||
"URL shortener may expose your decrypt key in URL.": "URL shortener may expose your decrypt key in URL."
|
||||
"URL shortener may expose your decrypt key in URL.": "URL shortener può esporre la tua chiave decrittografata nell'URL.",
|
||||
"Save paste": "Salva il messagio",
|
||||
"Your IP is not authorized to create pastes.": "Il tuo IP non è autorizzato a creare dei messaggi."
|
||||
}
|
||||
|
||||
+3
-1
@@ -184,5 +184,7 @@
|
||||
"Close": "Close",
|
||||
"Encrypted note on PrivateBin": "Encrypted note on PrivateBin",
|
||||
"Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.": "Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.",
|
||||
"URL shortener may expose your decrypt key in URL.": "URL shortener may expose your decrypt key in URL."
|
||||
"URL shortener may expose your decrypt key in URL.": "URL shortener may expose your decrypt key in URL.",
|
||||
"Save paste": "Save paste",
|
||||
"Your IP is not authorized to create pastes.": "Your IP is not authorized to create pastes."
|
||||
}
|
||||
|
||||
+190
@@ -0,0 +1,190 @@
|
||||
{
|
||||
"PrivateBin": "sivlolnitvanku'a",
|
||||
"%s is a minimalist, open source online pastebin where the server has zero knowledge of pasted data. Data is encrypted/decrypted %sin the browser%s using 256 bits AES.": ".i la %s mupli lo sorcu lo'e se setca kibro .i ji'a zo'e se zancari gi'e fingubni .i lo samse'u na djuno lo datni selru'e cu .i ba'e %sle brauzero%s ku mipri le do datni ku fi la'oi AES poi bitni li 256",
|
||||
"More information on the <a href=\"https://privatebin.info/\">project page</a>.": "More information on the <a href=\"https://privatebin.info/\">project page</a>.",
|
||||
"Because ignorance is bliss": ".i ki'u le ka na djuno cu ka saxfri",
|
||||
"en": "jbo",
|
||||
"Paste does not exist, has expired or has been deleted.": "Paste does not exist, has expired or has been deleted.",
|
||||
"%s requires php %s or above to work. Sorry.": "%s requires php %s or above to work. Sorry.",
|
||||
"%s requires configuration section [%s] to be present in configuration file.": "%s requires configuration section [%s] to be present in configuration file.",
|
||||
"Please wait %d seconds between each post.": [
|
||||
"Please wait %d second between each post. (singular)",
|
||||
"Please wait %d seconds between each post. (1st plural)",
|
||||
"Please wait %d seconds between each post. (2nd plural)",
|
||||
"Please wait %d seconds between each post. (3rd plural)"
|
||||
],
|
||||
"Paste is limited to %s of encrypted data.": "Paste is limited to %s of encrypted data.",
|
||||
"Invalid data.": ".i le selru'e cu na drani",
|
||||
"You are unlucky. Try again.": "You are unlucky. Try again.",
|
||||
"Error saving comment. Sorry.": "Error saving comment. Sorry.",
|
||||
"Error saving paste. Sorry.": "Error saving paste. Sorry.",
|
||||
"Invalid paste ID.": "Invalid paste ID.",
|
||||
"Paste is not of burn-after-reading type.": "Paste is not of burn-after-reading type.",
|
||||
"Wrong deletion token. Paste was not deleted.": "Wrong deletion token. Paste was not deleted.",
|
||||
"Paste was properly deleted.": "Paste was properly deleted.",
|
||||
"JavaScript is required for %s to work. Sorry for the inconvenience.": "JavaScript is required for %s to work. Sorry for the inconvenience.",
|
||||
"%s requires a modern browser to work.": "%s requires a modern browser to work.",
|
||||
"New": "cnino",
|
||||
"Send": "benji",
|
||||
"Clone": "fukpi",
|
||||
"Raw text": "vlapoi nalselrucyzu'e",
|
||||
"Expires": "vimcu",
|
||||
"Burn after reading": "vimcu ba la tcidu",
|
||||
"Open discussion": "lo zbasu cu casnu",
|
||||
"Password (recommended)": "japyvla (nelti'i)",
|
||||
"Discussion": "casnu",
|
||||
"Toggle navigation": "Toggle navigation",
|
||||
"%d seconds": [
|
||||
"%d second (singular)",
|
||||
"%d seconds (1st plural)",
|
||||
"%d seconds (2nd plural)",
|
||||
"%d seconds (3rd plural)"
|
||||
],
|
||||
"%d minutes": [
|
||||
"%d minute (singular)",
|
||||
"%d minutes (1st plural)",
|
||||
"%d minutes (2nd plural)",
|
||||
"%d minutes (3rd plural)"
|
||||
],
|
||||
"%d hours": [
|
||||
"%d hour (singular)",
|
||||
"%d hours (1st plural)",
|
||||
"%d hours (2nd plural)",
|
||||
"%d hours (3rd plural)"
|
||||
],
|
||||
"%d days": [
|
||||
"%d day (singular)",
|
||||
"%d days (1st plural)",
|
||||
"%d days (2nd plural)",
|
||||
"%d days (3rd plural)"
|
||||
],
|
||||
"%d weeks": [
|
||||
"%d week (singular)",
|
||||
"%d weeks (1st plural)",
|
||||
"%d weeks (2nd plural)",
|
||||
"%d weeks (3rd plural)"
|
||||
],
|
||||
"%d months": [
|
||||
"%d month (singular)",
|
||||
"%d months (1st plural)",
|
||||
"%d months (2nd plural)",
|
||||
"%d months (3rd plural)"
|
||||
],
|
||||
"%d years": [
|
||||
"%d year (singular)",
|
||||
"%d years (1st plural)",
|
||||
"%d years (2nd plural)",
|
||||
"%d years (3rd plural)"
|
||||
],
|
||||
"Never": "Never",
|
||||
"Note: This is a test service: Data may be deleted anytime. Kittens will die if you abuse this service.": "Note: This is a test service: Data may be deleted anytime. Kittens will die if you abuse this service.",
|
||||
"This document will expire in %d seconds.": [
|
||||
"This document will expire in %d second. (singular)",
|
||||
"This document will expire in %d seconds. (1st plural)",
|
||||
"This document will expire in %d seconds. (2nd plural)",
|
||||
"This document will expire in %d seconds. (3rd plural)"
|
||||
],
|
||||
"This document will expire in %d minutes.": [
|
||||
"This document will expire in %d minute. (singular)",
|
||||
"This document will expire in %d minutes. (1st plural)",
|
||||
"This document will expire in %d minutes. (2nd plural)",
|
||||
"This document will expire in %d minutes. (3rd plural)"
|
||||
],
|
||||
"This document will expire in %d hours.": [
|
||||
"This document will expire in %d hour. (singular)",
|
||||
"This document will expire in %d hours. (1st plural)",
|
||||
"This document will expire in %d hours. (2nd plural)",
|
||||
"This document will expire in %d hours. (3rd plural)"
|
||||
],
|
||||
"This document will expire in %d days.": [
|
||||
"This document will expire in %d day. (singular)",
|
||||
"This document will expire in %d days. (1st plural)",
|
||||
"This document will expire in %d days. (2nd plural)",
|
||||
"This document will expire in %d days. (3rd plural)"
|
||||
],
|
||||
"This document will expire in %d months.": [
|
||||
"This document will expire in %d month. (singular)",
|
||||
"This document will expire in %d months. (1st plural)",
|
||||
"This document will expire in %d months. (2nd plural)",
|
||||
"This document will expire in %d months. (3rd plural)"
|
||||
],
|
||||
"Please enter the password for this paste:": "Please enter the password for this paste:",
|
||||
"Could not decrypt data (Wrong key?)": "Could not decrypt data (Wrong key?)",
|
||||
"Could not delete the paste, it was not stored in burn after reading mode.": "Could not delete the paste, it was not stored in burn after reading mode.",
|
||||
"FOR YOUR EYES ONLY. Don't close this window, this message can't be displayed again.": "FOR YOUR EYES ONLY. Don't close this window, this message can't be displayed again.",
|
||||
"Could not decrypt comment; Wrong key?": "Could not decrypt comment; Wrong key?",
|
||||
"Reply": "Reply",
|
||||
"Anonymous": "Anonymous",
|
||||
"Avatar generated from IP address": "Avatar generated from IP address",
|
||||
"Add comment": "Add comment",
|
||||
"Optional nickname…": "Optional nickname…",
|
||||
"Post comment": "Post comment",
|
||||
"Sending comment…": "Sending comment…",
|
||||
"Comment posted.": "Comment posted.",
|
||||
"Could not refresh display: %s": "Could not refresh display: %s",
|
||||
"unknown status": "unknown status",
|
||||
"server error or not responding": "server error or not responding",
|
||||
"Could not post comment: %s": "Could not post comment: %s",
|
||||
"Sending paste…": "Sending paste…",
|
||||
"Your paste is <a id=\"pasteurl\" href=\"%s\">%s</a> <span id=\"copyhint\">(Hit [Ctrl]+[c] to copy)</span>": "Your paste is <a id=\"pasteurl\" href=\"%s\">%s</a> <span id=\"copyhint\">(Hit [Ctrl]+[c] to copy)</span>",
|
||||
"Delete data": "Delete data",
|
||||
"Could not create paste: %s": "Could not create paste: %s",
|
||||
"Cannot decrypt paste: Decryption key missing in URL (Did you use a redirector or an URL shortener which strips part of the URL?)": "Cannot decrypt paste: Decryption key missing in URL (Did you use a redirector or an URL shortener which strips part of the URL?)",
|
||||
"B": "B",
|
||||
"KiB": "KiB",
|
||||
"MiB": "MiB",
|
||||
"GiB": "GiB",
|
||||
"TiB": "TiB",
|
||||
"PiB": "PiB",
|
||||
"EiB": "EiB",
|
||||
"ZiB": "ZiB",
|
||||
"YiB": "YiB",
|
||||
"Format": "Format",
|
||||
"Plain Text": "Plain Text",
|
||||
"Source Code": "Source Code",
|
||||
"Markdown": "Markdown",
|
||||
"Download attachment": "Download attachment",
|
||||
"Cloned: '%s'": "Cloned: '%s'",
|
||||
"The cloned file '%s' was attached to this paste.": "The cloned file '%s' was attached to this paste.",
|
||||
"Attach a file": "Attach a file",
|
||||
"alternatively drag & drop a file or paste an image from the clipboard": "alternatively drag & drop a file or paste an image from the clipboard",
|
||||
"File too large, to display a preview. Please download the attachment.": "File too large, to display a preview. Please download the attachment.",
|
||||
"Remove attachment": "Remove attachment",
|
||||
"Your browser does not support uploading encrypted files. Please use a newer browser.": "Your browser does not support uploading encrypted files. Please use a newer browser.",
|
||||
"Invalid attachment.": "Invalid attachment.",
|
||||
"Options": "Options",
|
||||
"Shorten URL": "Shorten URL",
|
||||
"Editor": "Editor",
|
||||
"Preview": "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.",
|
||||
"Decrypt": "Decrypt",
|
||||
"Enter password": "Enter password",
|
||||
"Loading…": "Loading…",
|
||||
"Decrypting paste…": "Decrypting paste…",
|
||||
"Preparing new paste…": "Preparing new paste…",
|
||||
"In case this message never disappears please have a look at <a href=\"%s\">this FAQ for information to troubleshoot</a>.": "In case this message never disappears please have a look at <a href=\"%s\">this FAQ for information to troubleshoot</a>.",
|
||||
"+++ no paste text +++": "+++ no paste text +++",
|
||||
"Could not get paste data: %s": "Could not get paste data: %s",
|
||||
"QR code": "ky.bu ry termifra",
|
||||
"This website is using an insecure HTTP connection! Please use it only for testing.": "This website is using an insecure HTTP connection! Please use it only for testing.",
|
||||
"For more information <a href=\"%s\">see this FAQ entry</a>.": "For more information <a href=\"%s\">see this FAQ entry</a>.",
|
||||
"Your browser may require an HTTPS connection to support the WebCrypto API. Try <a href=\"%s\">switching to HTTPS</a>.": "Your browser may require an HTTPS connection to support the WebCrypto API. Try <a href=\"%s\">switching to HTTPS</a>.",
|
||||
"Your browser doesn't support WebAssembly, used for zlib compression. You can create uncompressed documents, but can't read compressed ones.": "Your browser doesn't support WebAssembly, used for zlib compression. You can create uncompressed documents, but can't read compressed ones.",
|
||||
"waiting on user to provide a password": "waiting on user to provide a password",
|
||||
"Could not decrypt data. Did you enter a wrong password? Retry with the button at the top.": "Could not decrypt data. Did you enter a wrong password? Retry with the button at the top.",
|
||||
"Retry": "refcfa",
|
||||
"Showing raw text…": "Showing raw text…",
|
||||
"Notice:": "Notice:",
|
||||
"This link will expire after %s.": "This link will expire after %s.",
|
||||
"This link can only be accessed once, do not use back or refresh button in your browser.": "This link can only be accessed once, do not use back or refresh button in your browser.",
|
||||
"Link:": "urli:",
|
||||
"Recipient may become aware of your timezone, convert time to UTC?": "Recipient may become aware of your timezone, convert time to UTC?",
|
||||
"Use Current Timezone": "Use Current Timezone",
|
||||
"Convert To UTC": "galfi lo cabni la utc",
|
||||
"Close": "ganlo",
|
||||
"Encrypted note on PrivateBin": ".i lo lo notci ku mifra cu zvati sivlolnitvanku'a",
|
||||
"Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.": "Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.",
|
||||
"URL shortener may expose your decrypt key in URL.": "URL shortener may expose your decrypt key in URL.",
|
||||
"Save paste": "rejgau fukpi",
|
||||
"Your IP is not authorized to create pastes.": "Your IP is not authorized to create pastes."
|
||||
}
|
||||
+3
-1
@@ -184,5 +184,7 @@
|
||||
"Close": "Close",
|
||||
"Encrypted note on PrivateBin": "Encrypted note on PrivateBin",
|
||||
"Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.": "Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.",
|
||||
"URL shortener may expose your decrypt key in URL.": "URL shortener may expose your decrypt key in URL."
|
||||
"URL shortener may expose your decrypt key in URL.": "URL shortener may expose your decrypt key in URL.",
|
||||
"Save paste": "Save paste",
|
||||
"Your IP is not authorized to create pastes.": "Your IP is not authorized to create pastes."
|
||||
}
|
||||
|
||||
+3
-1
@@ -184,5 +184,7 @@
|
||||
"Close": "Close",
|
||||
"Encrypted note on PrivateBin": "Encrypted note on PrivateBin",
|
||||
"Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.": "Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.",
|
||||
"URL shortener may expose your decrypt key in URL.": "URL shortener may expose your decrypt key in URL."
|
||||
"URL shortener may expose your decrypt key in URL.": "URL shortener may expose your decrypt key in URL.",
|
||||
"Save paste": "Save paste",
|
||||
"Your IP is not authorized to create pastes.": "Your IP is not authorized to create pastes."
|
||||
}
|
||||
|
||||
+2
-1
@@ -63,10 +63,10 @@
|
||||
"ho": ["Hiri Motu", "Hiri Motu"],
|
||||
"hu": ["magyar", "Hungarian"],
|
||||
"ia": ["Interlingua", "Interlingua"],
|
||||
"id": ["bahasa Indonesia","Indonesian"],
|
||||
"ie": ["Interlingue", "Interlingue"],
|
||||
"ga": ["Gaeilge", "Irish"],
|
||||
"ig": ["Asụsụ Igbo", "Igbo"],
|
||||
"in": ["bahasa Indonesia","Indonesian"],
|
||||
"ik": ["Iñupiaq", "Inupiaq"],
|
||||
"io": ["Ido", "Ido"],
|
||||
"is": ["Íslenska", "Icelandic"],
|
||||
@@ -89,6 +89,7 @@
|
||||
"ku": ["Kurdî", "Kurdish"],
|
||||
"kj": ["Kuanyama", "Kwanyama"],
|
||||
"la": ["lingua latina", "Latin"],
|
||||
"jbo":["jbobau", "Lojban"],
|
||||
"lb": ["Lëtzebuergesch", "Luxembourgish"],
|
||||
"lg": ["Luganda", "Ganda"],
|
||||
"li": ["Limburgs", "Limburgish"],
|
||||
|
||||
+4
-2
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"PrivateBin": "PrivateBin",
|
||||
"%s is a minimalist, open source online pastebin where the server has zero knowledge of pasted data. Data is encrypted/decrypted %sin the browser%s using 256 bits AES.": "%s yra minimalistinis, atvirojo kodo internetinis įdėjimų dėklas, kurį naudojant, serveris nieko nenutuokia apie įdėtus duomenis. Duomenys yra šifruojami/iššifruojami %snaršyklėje%s naudojant 256 bitų AES.",
|
||||
"More information on the <a href=\"https://privatebin.info/\">project page</a>.": "Daugiau informacijos rasite <a href=\"https://privatebin.info/\">projeketo puslapyje</a>.",
|
||||
"More information on the <a href=\"https://privatebin.info/\">project page</a>.": "Daugiau informacijos rasite <a href=\"https://privatebin.info/\">projekto puslapyje</a>.",
|
||||
"Because ignorance is bliss": "Nes nežinojimas yra palaima",
|
||||
"en": "lt",
|
||||
"Paste does not exist, has expired or has been deleted.": "Įdėjimo nėra, jis nebegalioja arba buvo ištrintas.",
|
||||
@@ -184,5 +184,7 @@
|
||||
"Close": "Užverti",
|
||||
"Encrypted note on PrivateBin": "Šifruoti užrašai ties PrivateBin",
|
||||
"Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.": "Norėdami matyti užrašus, aplankykite šį tinklalapį. Pasidalinus šiuo URL adresu su kitais žmonėmis, jiems taip pat bus leidžiama prieiga prie šių užrašų.",
|
||||
"URL shortener may expose your decrypt key in URL.": "URL shortener may expose your decrypt key in URL."
|
||||
"URL shortener may expose your decrypt key in URL.": "URL trumpinimo įrankis gali atskleisti URL adrese jūsų iššifravimo raktą.",
|
||||
"Save paste": "Įrašyti įdėjimą",
|
||||
"Your IP is not authorized to create pastes.": "Your IP is not authorized to create pastes."
|
||||
}
|
||||
|
||||
+3
-1
@@ -184,5 +184,7 @@
|
||||
"Close": "Close",
|
||||
"Encrypted note on PrivateBin": "Encrypted note on PrivateBin",
|
||||
"Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.": "Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.",
|
||||
"URL shortener may expose your decrypt key in URL.": "URL shortener may expose your decrypt key in URL."
|
||||
"URL shortener may expose your decrypt key in URL.": "URL shortener may expose your decrypt key in URL.",
|
||||
"Save paste": "Save paste",
|
||||
"Your IP is not authorized to create pastes.": "Your IP is not authorized to create pastes."
|
||||
}
|
||||
|
||||
+3
-1
@@ -184,5 +184,7 @@
|
||||
"Close": "Steng",
|
||||
"Encrypted note on PrivateBin": "Kryptert notat på PrivateBin",
|
||||
"Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.": "Besøk denne lenken for å se notatet. Hvis lenken deles med andre, vil de også kunne se notatet.",
|
||||
"URL shortener may expose your decrypt key in URL.": "URL shortener may expose your decrypt key in URL."
|
||||
"URL shortener may expose your decrypt key in URL.": "URL forkorter kan avsløre dekrypteringsnøkkelen.",
|
||||
"Save paste": "Lagre utklipp",
|
||||
"Your IP is not authorized to create pastes.": "Your IP is not authorized to create pastes."
|
||||
}
|
||||
|
||||
+38
-36
@@ -37,76 +37,76 @@
|
||||
"%d seconds": [
|
||||
"%d segonda",
|
||||
"%d segondas",
|
||||
"%d seconds (2nd plural)",
|
||||
"%d seconds (3rd plural)"
|
||||
"%d segondas",
|
||||
"%d segondas"
|
||||
],
|
||||
"%d minutes": [
|
||||
"%d minuta",
|
||||
"%d minutas",
|
||||
"%d minutes (2nd plural)",
|
||||
"%d minutes (3rd plural)"
|
||||
"%d minutas",
|
||||
"%d minutas"
|
||||
],
|
||||
"%d hours": [
|
||||
"%d ora",
|
||||
"%d oras",
|
||||
"%d hours (2nd plural)",
|
||||
"%d hours (3rd plural)"
|
||||
"%d oras",
|
||||
"%d oras"
|
||||
],
|
||||
"%d days": [
|
||||
"%d jorn",
|
||||
"%d jorns",
|
||||
"%d days (2nd plural)",
|
||||
"%d days (3rd plural)"
|
||||
"%d jorns",
|
||||
"%d jorns"
|
||||
],
|
||||
"%d weeks": [
|
||||
"%d setmana",
|
||||
"%d setmanas",
|
||||
"%d weeks (2nd plural)",
|
||||
"%d weeks (3rd plural)"
|
||||
"%d setmanas",
|
||||
"%d setmanas"
|
||||
],
|
||||
"%d months": [
|
||||
"%d mes",
|
||||
"%d meses",
|
||||
"%d months (2nd plural)",
|
||||
"%d months (3rd plural)"
|
||||
"%d meses",
|
||||
"%d meses"
|
||||
],
|
||||
"%d years": [
|
||||
"%d an",
|
||||
"%d ans",
|
||||
"%d years (2nd plural)",
|
||||
"%d years (3rd plural)"
|
||||
"%d ans",
|
||||
"%d ans"
|
||||
],
|
||||
"Never": "Jamai",
|
||||
"Note: This is a test service: Data may be deleted anytime. Kittens will die if you abuse this service.": "Nota : Aquò es un servici d’espròva : las donadas pòdon èsser suprimidas a cada moment. De catons moriràn s’abusatz d’aqueste servici.",
|
||||
"This document will expire in %d seconds.": [
|
||||
"Ce document expirera dans %d seconde.",
|
||||
"Aqueste document expirarà dins %d segondas.",
|
||||
"Aqueste document expirarà dins %d segondas.",
|
||||
"Aqueste document expirarà dins %d segondas."
|
||||
"Aqueste document expirarà d’aquí %d segonda.",
|
||||
"Aqueste document expirarà d’aquí %d segondas.",
|
||||
"Aqueste document expirarà d’aquí %d segondas.",
|
||||
"Aqueste document expirarà d’aquí %d segondas."
|
||||
],
|
||||
"This document will expire in %d minutes.": [
|
||||
"Ce document expirera dans %d minute.",
|
||||
"Aqueste document expirarà dins %d minutas.",
|
||||
"Aqueste document expirarà dins %d minutas.",
|
||||
"Aqueste document expirarà dins %d minutas."
|
||||
"Aqueste document expirarà d’aquí %d minuta.",
|
||||
"Aqueste document expirarà d’aquí %d minutas.",
|
||||
"Aqueste document expirarà d’aquí %d minutas.",
|
||||
"Aqueste document expirarà d’aquí %d minutas."
|
||||
],
|
||||
"This document will expire in %d hours.": [
|
||||
"Ce document expirera dans %d heure.",
|
||||
"Aqueste document expirarà dins %d oras.",
|
||||
"Aqueste document expirarà dins %d oras.",
|
||||
"Aqueste document expirarà dins %d oras."
|
||||
"Aqueste document expirarà d’aquí %d ora.",
|
||||
"Aqueste document expirarà d’aquí %d oras.",
|
||||
"Aqueste document expirarà d’aquí %d oras.",
|
||||
"Aqueste document expirarà d’aquí %d oras."
|
||||
],
|
||||
"This document will expire in %d days.": [
|
||||
"Ce document expirera dans %d jour.",
|
||||
"Aqueste document expirarà dins %d jorns.",
|
||||
"Aqueste document expirarà dins %d jorns.",
|
||||
"Aqueste document expirarà dins %d jorns."
|
||||
"Aqueste document expirarà d’aquí %d jorn.",
|
||||
"Aqueste document expirarà d’aquí %d jorns.",
|
||||
"Aqueste document expirarà d’aquí %d jorns.",
|
||||
"Aqueste document expirarà d’aquí %d jorns."
|
||||
],
|
||||
"This document will expire in %d months.": [
|
||||
"Ce document expirera dans %d mois.",
|
||||
"Aqueste document expirarà dins %d meses.",
|
||||
"Aqueste document expirarà dins %d meses.",
|
||||
"Aqueste document expirarà dins %d meses."
|
||||
"Aqueste document expirarà d’aquí %d mes.",
|
||||
"Aqueste document expirarà d’aquí %d meses.",
|
||||
"Aqueste document expirarà d’aquí %d meses.",
|
||||
"Aqueste document expirarà d’aquí %d meses."
|
||||
],
|
||||
"Please enter the password for this paste:": "Picatz lo senhal per aqueste tèxte :",
|
||||
"Could not decrypt data (Wrong key?)": "Impossible de deschifrar las donadas (marrida clau ?)",
|
||||
@@ -156,7 +156,7 @@
|
||||
"Shorten URL": "Acorchir l’URL",
|
||||
"Editor": "Editar",
|
||||
"Preview": "Previsualizar",
|
||||
"%s requires the PATH to end in a \"%s\". Please update the PATH in your index.php.": "%s demanda que lo PATH termine en \"%s\". Mercés de metre a jorn lo PATH dins vòstre index.php.",
|
||||
"%s requires the PATH to end in a \"%s\". Please update the PATH in your index.php.": "%s demanda que lo PATH termine en « %s ». Mercés de metre a jorn lo PATH dins vòstre index.php.",
|
||||
"Decrypt": "Deschifrar",
|
||||
"Enter password": "Picatz lo senhal",
|
||||
"Loading…": "Cargament…",
|
||||
@@ -184,5 +184,7 @@
|
||||
"Close": "Tampar",
|
||||
"Encrypted note on PrivateBin": "Nòtas chifradas sus PrivateBin",
|
||||
"Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.": "Visitatz aqueste ligam per veire la nòta. Fornir lo ligam a qualqu’un mai li permet tanben d’accedir a la nòta.",
|
||||
"URL shortener may expose your decrypt key in URL.": "URL shortener may expose your decrypt key in URL."
|
||||
"URL shortener may expose your decrypt key in URL.": "Los espleches d’acorchiment d’URL pòdon expausar la clau de deschiframent dins l’URL.",
|
||||
"Save paste": "Enregistrar lo tèxt",
|
||||
"Your IP is not authorized to create pastes.": "Vòstra adreça IP a pas l’autorizacion de crear de tèxtes."
|
||||
}
|
||||
|
||||
+3
-1
@@ -184,5 +184,7 @@
|
||||
"Close": "Close",
|
||||
"Encrypted note on PrivateBin": "Encrypted note on PrivateBin",
|
||||
"Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.": "Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.",
|
||||
"URL shortener may expose your decrypt key in URL.": "URL shortener may expose your decrypt key in URL."
|
||||
"URL shortener may expose your decrypt key in URL.": "URL shortener may expose your decrypt key in URL.",
|
||||
"Save paste": "Save paste",
|
||||
"Your IP is not authorized to create pastes.": "Your IP is not authorized to create pastes."
|
||||
}
|
||||
|
||||
+3
-1
@@ -184,5 +184,7 @@
|
||||
"Close": "Fechar",
|
||||
"Encrypted note on PrivateBin": "Nota criptografada no PrivateBin",
|
||||
"Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.": "Visite esse link para ver a nota. Dar a URL para qualquer um permite que eles também acessem a nota.",
|
||||
"URL shortener may expose your decrypt key in URL.": "URL shortener may expose your decrypt key in URL."
|
||||
"URL shortener may expose your decrypt key in URL.": "URL shortener may expose your decrypt key in URL.",
|
||||
"Save paste": "Save paste",
|
||||
"Your IP is not authorized to create pastes.": "Your IP is not authorized to create pastes."
|
||||
}
|
||||
|
||||
+4
-2
@@ -77,7 +77,7 @@
|
||||
"%d лет"
|
||||
],
|
||||
"Never": "Никогда",
|
||||
"Note: This is a test service: Data may be deleted anytime. Kittens will die if you abuse this service.": "Примечание: Этот сервис тестовый: Данные могут быть удалены в любое время. Котята умрут, если вы будете злоупотреблять серсисом.",
|
||||
"Note: This is a test service: Data may be deleted anytime. Kittens will die if you abuse this service.": "Примечание: Этот сервис тестовый: Данные могут быть удалены в любое время. Котята умрут, если вы будете злоупотреблять сервисом.",
|
||||
"This document will expire in %d seconds.": [
|
||||
"Документ будет удален через %d секунду.",
|
||||
"Документ будет удален через %d секунды.",
|
||||
@@ -184,5 +184,7 @@
|
||||
"Close": "Закрыть",
|
||||
"Encrypted note on PrivateBin": "Зашифрованная запись на PrivateBin",
|
||||
"Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.": "Посетите эту ссылку чтобы просмотреть запись. Передача ссылки кому либо позволит им получить доступ к записи тоже.",
|
||||
"URL shortener may expose your decrypt key in URL.": "URL shortener may expose your decrypt key in URL."
|
||||
"URL shortener may expose your decrypt key in URL.": "Сервис сокращения ссылок может получить ваш ключ расшифровки из ссылки.",
|
||||
"Save paste": "Сохранить запись",
|
||||
"Your IP is not authorized to create pastes.": "Your IP is not authorized to create pastes."
|
||||
}
|
||||
|
||||
+3
-1
@@ -184,5 +184,7 @@
|
||||
"Close": "Close",
|
||||
"Encrypted note on PrivateBin": "Encrypted note on PrivateBin",
|
||||
"Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.": "Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.",
|
||||
"URL shortener may expose your decrypt key in URL.": "URL shortener may expose your decrypt key in URL."
|
||||
"URL shortener may expose your decrypt key in URL.": "URL shortener may expose your decrypt key in URL.",
|
||||
"Save paste": "Save paste",
|
||||
"Your IP is not authorized to create pastes.": "Your IP is not authorized to create pastes."
|
||||
}
|
||||
|
||||
+3
-1
@@ -184,5 +184,7 @@
|
||||
"Close": "Close",
|
||||
"Encrypted note on PrivateBin": "Encrypted note on PrivateBin",
|
||||
"Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.": "Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.",
|
||||
"URL shortener may expose your decrypt key in URL.": "URL shortener may expose your decrypt key in URL."
|
||||
"URL shortener may expose your decrypt key in URL.": "URL shortener may expose your decrypt key in URL.",
|
||||
"Save paste": "Save paste",
|
||||
"Your IP is not authorized to create pastes.": "Your IP is not authorized to create pastes."
|
||||
}
|
||||
|
||||
+30
-28
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"PrivateBin": "PrivateBin",
|
||||
"%s is a minimalist, open source online pastebin where the server has zero knowledge of pasted data. Data is encrypted/decrypted %sin the browser%s using 256 bits AES.": "%s is a minimalist, open source online pastebin where the server has zero knowledge of pasted data. Data is encrypted/decrypted %sin the browser%s using 256 bits AES.",
|
||||
"More information on the <a href=\"https://privatebin.info/\">project page</a>.": "More information on the <a href=\"https://privatebin.info/\">project page</a>.",
|
||||
"Because ignorance is bliss": "Because ignorance is bliss",
|
||||
"More information on the <a href=\"https://privatebin.info/\">project page</a>.": "Daha fazla bilgi için <a href=\"https://privatebin.info/\">proje sayfası</a>'na göz atabilirsiniz.",
|
||||
"Because ignorance is bliss": "Çünkü, cehalet mutluluktur",
|
||||
"en": "tr",
|
||||
"Paste does not exist, has expired or has been deleted.": "Paste does not exist, has expired or has been deleted.",
|
||||
"%s requires php %s or above to work. Sorry.": "%s requires php %s or above to work. Sorry.",
|
||||
@@ -14,8 +14,8 @@
|
||||
"Please wait %d seconds between each post. (3rd plural)"
|
||||
],
|
||||
"Paste is limited to %s of encrypted data.": "Paste is limited to %s of encrypted data.",
|
||||
"Invalid data.": "Invalid data.",
|
||||
"You are unlucky. Try again.": "You are unlucky. Try again.",
|
||||
"Invalid data.": "Geçersiz veri.",
|
||||
"You are unlucky. Try again.": "Lütfen tekrar deneyiniz.",
|
||||
"Error saving comment. Sorry.": "Error saving comment. Sorry.",
|
||||
"Error saving paste. Sorry.": "Error saving paste. Sorry.",
|
||||
"Invalid paste ID.": "Invalid paste ID.",
|
||||
@@ -24,16 +24,16 @@
|
||||
"Paste was properly deleted.": "Paste was properly deleted.",
|
||||
"JavaScript is required for %s to work. Sorry for the inconvenience.": "JavaScript is required for %s to work. Sorry for the inconvenience.",
|
||||
"%s requires a modern browser to work.": "%s requires a modern browser to work.",
|
||||
"New": "New",
|
||||
"Send": "Send",
|
||||
"Clone": "Clone",
|
||||
"New": "Yeni",
|
||||
"Send": "Gönder",
|
||||
"Clone": "Kopyala",
|
||||
"Raw text": "Raw text",
|
||||
"Expires": "Expires",
|
||||
"Expires": "Süre Sonu",
|
||||
"Burn after reading": "Burn after reading",
|
||||
"Open discussion": "Open discussion",
|
||||
"Open discussion": "Açık Tartışmalar",
|
||||
"Password (recommended)": "Password (recommended)",
|
||||
"Discussion": "Discussion",
|
||||
"Toggle navigation": "Toggle navigation",
|
||||
"Discussion": "Tartışma",
|
||||
"Toggle navigation": "Gezinmeyi değiştir",
|
||||
"%d seconds": [
|
||||
"%d second (singular)",
|
||||
"%d seconds (1st plural)",
|
||||
@@ -59,8 +59,8 @@
|
||||
"%d days (3rd plural)"
|
||||
],
|
||||
"%d weeks": [
|
||||
"%d week (singular)",
|
||||
"%d weeks (1st plural)",
|
||||
"%d hafta (tekil)",
|
||||
"%d haftalar (çoğul)",
|
||||
"%d weeks (2nd plural)",
|
||||
"%d weeks (3rd plural)"
|
||||
],
|
||||
@@ -113,21 +113,21 @@
|
||||
"Could not delete the paste, it was not stored in burn after reading mode.": "Could not delete the paste, it was not stored in burn after reading mode.",
|
||||
"FOR YOUR EYES ONLY. Don't close this window, this message can't be displayed again.": "FOR YOUR EYES ONLY. Don't close this window, this message can't be displayed again.",
|
||||
"Could not decrypt comment; Wrong key?": "Could not decrypt comment; Wrong key?",
|
||||
"Reply": "Reply",
|
||||
"Anonymous": "Anonymous",
|
||||
"Reply": "Cevapla",
|
||||
"Anonymous": "Anonim",
|
||||
"Avatar generated from IP address": "Avatar generated from IP address",
|
||||
"Add comment": "Add comment",
|
||||
"Add comment": "Yorum ekle",
|
||||
"Optional nickname…": "Optional nickname…",
|
||||
"Post comment": "Post comment",
|
||||
"Post comment": "Yorumu gönder",
|
||||
"Sending comment…": "Sending comment…",
|
||||
"Comment posted.": "Comment posted.",
|
||||
"Comment posted.": "Yorum gönderildi.",
|
||||
"Could not refresh display: %s": "Could not refresh display: %s",
|
||||
"unknown status": "unknown status",
|
||||
"server error or not responding": "server error or not responding",
|
||||
"Could not post comment: %s": "Could not post comment: %s",
|
||||
"Sending paste…": "Sending paste…",
|
||||
"Your paste is <a id=\"pasteurl\" href=\"%s\">%s</a> <span id=\"copyhint\">(Hit [Ctrl]+[c] to copy)</span>": "Your paste is <a id=\"pasteurl\" href=\"%s\">%s</a> <span id=\"copyhint\">(Hit [Ctrl]+[c] to copy)</span>",
|
||||
"Delete data": "Delete data",
|
||||
"Delete data": "Veriyi sil",
|
||||
"Could not create paste: %s": "Could not create paste: %s",
|
||||
"Cannot decrypt paste: Decryption key missing in URL (Did you use a redirector or an URL shortener which strips part of the URL?)": "Cannot decrypt paste: Decryption key missing in URL (Did you use a redirector or an URL shortener which strips part of the URL?)",
|
||||
"B": "B",
|
||||
@@ -155,34 +155,36 @@
|
||||
"Options": "Options",
|
||||
"Shorten URL": "Shorten URL",
|
||||
"Editor": "Editor",
|
||||
"Preview": "Preview",
|
||||
"Preview": "Ön izleme",
|
||||
"%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.",
|
||||
"Decrypt": "Decrypt",
|
||||
"Enter password": "Enter password",
|
||||
"Loading…": "Loading…",
|
||||
"Enter password": "Şifreyi girin",
|
||||
"Loading…": "Yükleniyor…",
|
||||
"Decrypting paste…": "Decrypting paste…",
|
||||
"Preparing new paste…": "Preparing new paste…",
|
||||
"In case this message never disappears please have a look at <a href=\"%s\">this FAQ for information to troubleshoot</a>.": "In case this message never disappears please have a look at <a href=\"%s\">this FAQ for information to troubleshoot</a>.",
|
||||
"+++ no paste text +++": "+++ no paste text +++",
|
||||
"Could not get paste data: %s": "Could not get paste data: %s",
|
||||
"QR code": "QR code",
|
||||
"QR code": "QR kodu",
|
||||
"This website is using an insecure HTTP connection! Please use it only for testing.": "This website is using an insecure HTTP connection! Please use it only for testing.",
|
||||
"For more information <a href=\"%s\">see this FAQ entry</a>.": "For more information <a href=\"%s\">see this FAQ entry</a>.",
|
||||
"Your browser may require an HTTPS connection to support the WebCrypto API. Try <a href=\"%s\">switching to HTTPS</a>.": "Your browser may require an HTTPS connection to support the WebCrypto API. Try <a href=\"%s\">switching to HTTPS</a>.",
|
||||
"Your browser doesn't support WebAssembly, used for zlib compression. You can create uncompressed documents, but can't read compressed ones.": "Your browser doesn't support WebAssembly, used for zlib compression. You can create uncompressed documents, but can't read compressed ones.",
|
||||
"waiting on user to provide a password": "waiting on user to provide a password",
|
||||
"Could not decrypt data. Did you enter a wrong password? Retry with the button at the top.": "Could not decrypt data. Did you enter a wrong password? Retry with the button at the top.",
|
||||
"Retry": "Retry",
|
||||
"Retry": "Yeniden Dene",
|
||||
"Showing raw text…": "Showing raw text…",
|
||||
"Notice:": "Notice:",
|
||||
"Notice:": "Bildirim:",
|
||||
"This link will expire after %s.": "This link will expire after %s.",
|
||||
"This link can only be accessed once, do not use back or refresh button in your browser.": "This link can only be accessed once, do not use back or refresh button in your browser.",
|
||||
"Link:": "Link:",
|
||||
"Link:": "Bağlantı:",
|
||||
"Recipient may become aware of your timezone, convert time to UTC?": "Recipient may become aware of your timezone, convert time to UTC?",
|
||||
"Use Current Timezone": "Use Current Timezone",
|
||||
"Convert To UTC": "Convert To UTC",
|
||||
"Close": "Close",
|
||||
"Close": "Kapat",
|
||||
"Encrypted note on PrivateBin": "Encrypted note on PrivateBin",
|
||||
"Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.": "Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.",
|
||||
"URL shortener may expose your decrypt key in URL.": "URL shortener may expose your decrypt key in URL."
|
||||
"URL shortener may expose your decrypt key in URL.": "URL shortener may expose your decrypt key in URL.",
|
||||
"Save paste": "Save paste",
|
||||
"Your IP is not authorized to create pastes.": "Your IP is not authorized to create pastes."
|
||||
}
|
||||
|
||||
+3
-1
@@ -184,5 +184,7 @@
|
||||
"Close": "Close",
|
||||
"Encrypted note on PrivateBin": "Encrypted note on PrivateBin",
|
||||
"Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.": "Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.",
|
||||
"URL shortener may expose your decrypt key in URL.": "URL shortener may expose your decrypt key in URL."
|
||||
"URL shortener may expose your decrypt key in URL.": "URL shortener may expose your decrypt key in URL.",
|
||||
"Save paste": "Save paste",
|
||||
"Your IP is not authorized to create pastes.": "Your IP is not authorized to create pastes."
|
||||
}
|
||||
|
||||
+33
-31
@@ -1,29 +1,29 @@
|
||||
{
|
||||
"PrivateBin": "PrivateBin",
|
||||
"%s is a minimalist, open source online pastebin where the server has zero knowledge of pasted data. Data is encrypted/decrypted %sin the browser%s using 256 bits AES.": "%s是一个极简、开源、对粘贴内容毫不知情的在线粘贴板,数据%s在浏览器内%s进行AES-256加密。",
|
||||
"%s is a minimalist, open source online pastebin where the server has zero knowledge of pasted data. Data is encrypted/decrypted %sin the browser%s using 256 bits AES.": "%s 是一个极简、开源、对粘贴内容毫不知情的在线粘贴板,数据%s在浏览器内%s进行 AES-256 加密和解密。",
|
||||
"More information on the <a href=\"https://privatebin.info/\">project page</a>.": "更多信息请查看<a href=\"https://privatebin.info/\">项目主页</a>。",
|
||||
"Because ignorance is bliss": "因为无知是福",
|
||||
"en": "zh",
|
||||
"Paste does not exist, has expired or has been deleted.": "粘贴内容不存在,已过期或已被删除。",
|
||||
"%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] 部分。",
|
||||
"Paste does not exist, has expired or has been deleted.": "粘贴内容不存在、已过期或已被删除。",
|
||||
"%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.": [
|
||||
"每 %d 秒只能粘贴一次。",
|
||||
"每 %d 秒只能粘贴一次。",
|
||||
"每 %d 秒只能粘贴一次。",
|
||||
"每 %d 秒只能粘贴一次。"
|
||||
],
|
||||
"Paste is limited to %s of encrypted data.": "粘贴受限于 %s 加密数据。",
|
||||
"Paste is limited to %s of encrypted data.": "对于加密数据,上限为 %s。",
|
||||
"Invalid data.": "无效的数据。",
|
||||
"You are unlucky. Try again.": "请再试一次。",
|
||||
"Error saving comment. Sorry.": "保存评论时出现错误,抱歉。",
|
||||
"Error saving paste. Sorry.": "保存粘贴内容时出现错误,抱歉。",
|
||||
"Invalid paste ID.": "无效的ID。",
|
||||
"Invalid paste ID.": "无效的 ID。",
|
||||
"Paste is not of burn-after-reading type.": "粘贴内容不是阅后即焚类型。",
|
||||
"Wrong deletion token. Paste was not deleted.": "错误的删除token,粘贴内容没有被删除。",
|
||||
"Paste was properly deleted.": "粘贴内容已被正确删除。",
|
||||
"JavaScript is required for %s to work. Sorry for the inconvenience.": "%s需要JavaScript来进行加解密。 给你带来的不便敬请谅解。",
|
||||
"%s requires a modern browser to work.": "%s需要在现代浏览器上工作。",
|
||||
"JavaScript is required for %s to work. Sorry for the inconvenience.": "%s 需要 JavaScript 来进行加解密。 给你带来的不便敬请谅解。",
|
||||
"%s requires a modern browser to work.": "%s 需要在现代浏览器上工作。",
|
||||
"New": "新建",
|
||||
"Send": "送出",
|
||||
"Clone": "复制",
|
||||
@@ -111,8 +111,8 @@
|
||||
"Please enter the password for this paste:": "请输入这份粘贴内容的密码:",
|
||||
"Could not decrypt data (Wrong key?)": "无法解密数据(密钥错误?)",
|
||||
"Could not delete the paste, it was not stored in burn after reading mode.": "无法删除此粘贴内容,它没有以阅后即焚模式保存。",
|
||||
"FOR YOUR EYES ONLY. Don't close this window, this message can't be displayed again.": "注意啦!!!不要关闭窗口,否则你再也见不到这条消息了。",
|
||||
"Could not decrypt comment; Wrong key?": "无法解密评论; 密钥错误?",
|
||||
"FOR YOUR EYES ONLY. Don't close this window, this message can't be displayed again.": "看!仔!细!了!不要关闭窗口,否则你再也见不到这条消息了。",
|
||||
"Could not decrypt comment; Wrong key?": "无法解密评论;密钥错误?",
|
||||
"Reply": "回复",
|
||||
"Anonymous": "匿名",
|
||||
"Avatar generated from IP address": "由IP生成的头像",
|
||||
@@ -126,7 +126,7 @@
|
||||
"server error or not responding": "服务器错误或无回应",
|
||||
"Could not post comment: %s": "无法发送评论: %s",
|
||||
"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>",
|
||||
"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": "删除数据",
|
||||
"Could not create paste: %s": "无法创建粘贴:%s",
|
||||
"Cannot decrypt paste: Decryption key missing in URL (Did you use a redirector or an URL shortener which strips part of the URL?)": "无法解密粘贴:URL中缺失解密密钥(是否使用了重定向或者短链接导致密钥丢失?)",
|
||||
@@ -144,45 +144,47 @@
|
||||
"Source Code": "源代码",
|
||||
"Markdown": "Markdown",
|
||||
"Download attachment": "下载附件",
|
||||
"Cloned: '%s'": "副本: '%s'",
|
||||
"The cloned file '%s' was attached to this paste.": "副本 '%s' 已附加到此粘贴内容。",
|
||||
"Cloned: '%s'": "副本:“%s”",
|
||||
"The cloned file '%s' was attached to this paste.": "副本“%s”已附加到此粘贴内容。",
|
||||
"Attach a file": "添加一个附件",
|
||||
"alternatively drag & drop a file or paste an image from the clipboard": "拖放文件或从剪贴板粘贴图片",
|
||||
"File too large, to display a preview. Please download the attachment.": "文件过大。要显示预览,请下载附件。",
|
||||
"File too large, to display a preview. Please download the attachment.": "文件过大,无法显示预览。请下载附件。",
|
||||
"Remove attachment": "移除附件",
|
||||
"Your browser does not support uploading encrypted files. Please use a newer browser.": "您的浏览器不支持上传加密的文件,请使用更新的浏览器。",
|
||||
"Your browser does not support uploading encrypted files. Please use a newer browser.": "您的浏览器不支持上传加密的文件,请使用新版本的浏览器。",
|
||||
"Invalid attachment.": "无效的附件",
|
||||
"Options": "选项",
|
||||
"Shorten URL": "缩短链接",
|
||||
"Editor": "编辑",
|
||||
"Preview": "预览",
|
||||
"%s requires the PATH to end in a \"%s\". Please update the PATH in your index.php.": "%s 的 PATH 变量必须结束于 \"%s\"。 请修改你的 index.php 中的 PATH 变量。",
|
||||
"%s requires the PATH to end in a \"%s\". Please update the PATH in your index.php.": "%s 的 PATH 变量必须结束于“%s”。 请修改你的 index.php 中的 PATH 变量。",
|
||||
"Decrypt": "解密",
|
||||
"Enter password": "输入密码",
|
||||
"Loading…": "载入中…",
|
||||
"Decrypting paste…": "正在解密",
|
||||
"Preparing new paste…": "正在准备新的粘贴内容",
|
||||
"In case this message never disappears please have a look at <a href=\"%s\">this FAQ for information to troubleshoot</a>.": "如果这个消息一直存在,请参考 <a href=\"%s\">这里的 参考文档(英文版)</a>进行故障排除。",
|
||||
"+++ no paste text +++": "+++ 没有粘贴内容 +++",
|
||||
"In case this message never disappears please have a look at <a href=\"%s\">this FAQ for information to troubleshoot</a>.": "如果此消息一直存在,请参考 <a href=\"%s\">这里的 FAQ(英文版)</a>排除故障。",
|
||||
"+++ no paste text +++": "+++ 无粘贴内容 +++",
|
||||
"Could not get paste data: %s": "无法获取粘贴数据:%s",
|
||||
"QR code": "二维码",
|
||||
"This website is using an insecure HTTP connection! Please use it only for testing.": "该网站使用了不安全的HTTP连接! 请仅将其用于测试。",
|
||||
"For more information <a href=\"%s\">see this FAQ entry</a>.": "有关更多信息,<a href=\"%s\">请参阅此常见问题解答</a>。",
|
||||
"Your browser may require an HTTPS connection to support the WebCrypto API. Try <a href=\"%s\">switching to HTTPS</a>.": "您的浏览器可能需要HTTPS连接才能支持WebCrypto API。 尝试<a href=\"%s\">切换到HTTPS </a>。",
|
||||
"Your browser doesn't support WebAssembly, used for zlib compression. You can create uncompressed documents, but can't read compressed ones.": "您的浏览器不支持用于zlib压缩的WebAssembly。 您可以创建未压缩的文档,但不能读取压缩的文档。",
|
||||
"This website is using an insecure HTTP connection! Please use it only for testing.": "该网站使用了不安全的 HTTP 连接!请仅将其用于测试。",
|
||||
"For more information <a href=\"%s\">see this FAQ entry</a>.": "有关更多信息,请参阅<a href=\"%s\">此常见问题解答</a>。",
|
||||
"Your browser may require an HTTPS connection to support the WebCrypto API. Try <a href=\"%s\">switching to HTTPS</a>.": "您的浏览器可能需要 HTTPS 连接才能支持 WebCrypto API。 尝试<a href=\"%s\">切换到 HTTPS</a>。",
|
||||
"Your browser doesn't support WebAssembly, used for zlib compression. You can create uncompressed documents, but can't read compressed ones.": "您的浏览器不支持用于 zlib 压缩的 WebAssembly。 您可以创建未压缩的文档,但不能读取压缩的文档。",
|
||||
"waiting on user to provide a password": "请输入密码",
|
||||
"Could not decrypt data. Did you enter a wrong password? Retry with the button at the top.": "无法解密数据。 您输入了错误的密码吗? 点顶部的按钮重试。",
|
||||
"Could not decrypt data. Did you enter a wrong password? Retry with the button at the top.": "无法解密数据。您是否输入了错误的密码?按顶部的按钮重试。",
|
||||
"Retry": "重试",
|
||||
"Showing raw text…": "显示原始文字…",
|
||||
"Notice:": "注意:",
|
||||
"Notice:": "注意:",
|
||||
"This link will expire after %s.": "这个链接将会在 %s 过期。",
|
||||
"This link can only be accessed once, do not use back or refresh button in your browser.": "这个链接只能被访问一次,请勿使用浏览器中的返回和刷新按钮。",
|
||||
"Link:": "链接地址:",
|
||||
"Recipient may become aware of your timezone, convert time to UTC?": "收件人可能会知道您的时区,将时间转换为UTC?",
|
||||
"This link can only be accessed once, do not use back or refresh button in your browser.": "此链接只能被访问一次,请勿使用浏览器中的返回和刷新按钮。",
|
||||
"Link:": "链接:",
|
||||
"Recipient may become aware of your timezone, convert time to UTC?": "收件人可能会知道您的时区,将时间转换为 UTC?",
|
||||
"Use Current Timezone": "使用当前时区",
|
||||
"Convert To UTC": "转换为UTC",
|
||||
"Convert To UTC": "转换为 UTC",
|
||||
"Close": "关闭",
|
||||
"Encrypted note on PrivateBin": "PrivateBin上的加密笔记",
|
||||
"Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.": "访问这个链接来查看该笔记。 将这个URL发送给任何人即可允许其访问该笔记。",
|
||||
"URL shortener may expose your decrypt key in URL.": "URL shortener may expose your decrypt key in URL."
|
||||
"Encrypted note on PrivateBin": "PrivateBin 上的加密笔记",
|
||||
"Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.": "访问此链接来查看该笔记。将此 URL 发送给任何人即可允许其访问该笔记。",
|
||||
"URL shortener may expose your decrypt key in URL.": "短链接服务可能会暴露您在 URL 中的解密密钥。",
|
||||
"Save paste": "保存内容",
|
||||
"Your IP is not authorized to create pastes.": "Your IP is not authorized to create pastes."
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
* @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.3.5
|
||||
* @version 1.4.0
|
||||
*/
|
||||
|
||||
// change this, if your php files and data is outside of your webservers document root
|
||||
|
||||
@@ -1,17 +1,16 @@
|
||||
'use strict';
|
||||
// base-x encoding / decoding
|
||||
// based on https://github.com/cryptocoinjs/base-x 3.0.7
|
||||
// modification: removed Buffer dependency and node.modules entry
|
||||
// Copyright (c) 2018 base-x contributors
|
||||
// Copyright (c) 2014-2018 The Bitcoin Core developers (base58.cpp)
|
||||
// Distributed under the MIT software license, see the accompanying
|
||||
// file LICENSE or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
(function(){
|
||||
this.baseX = function base (ALPHABET) {
|
||||
if (ALPHABET.length >= 255) { throw new TypeError('Alphabet too long') }
|
||||
var BASE_MAP = new Uint8Array(256)
|
||||
BASE_MAP.fill(255)
|
||||
for (var j = 0; j < BASE_MAP.length; j++) {
|
||||
BASE_MAP[j] = 255
|
||||
}
|
||||
for (var i = 0; i < ALPHABET.length; i++) {
|
||||
var x = ALPHABET.charAt(i)
|
||||
var xc = x.charCodeAt(0)
|
||||
@@ -23,6 +22,13 @@ this.baseX = function base (ALPHABET) {
|
||||
var FACTOR = Math.log(BASE) / Math.log(256) // log(BASE) / log(256), rounded up
|
||||
var iFACTOR = Math.log(256) / Math.log(BASE) // log(256) / log(BASE), rounded up
|
||||
function encode (source) {
|
||||
if (source instanceof Uint8Array) {
|
||||
} else if (ArrayBuffer.isView(source)) {
|
||||
source = new Uint8Array(source.buffer, source.byteOffset, source.byteLength)
|
||||
} else if (Array.isArray(source)) {
|
||||
source = Uint8Array.from(source)
|
||||
}
|
||||
if (!(source instanceof Uint8Array)) { throw new TypeError('Expected Uint8Array') }
|
||||
if (source.length === 0) { return '' }
|
||||
// Skip & count leading zeroes.
|
||||
var zeroes = 0
|
||||
@@ -62,10 +68,8 @@ this.baseX = function base (ALPHABET) {
|
||||
}
|
||||
function decodeUnsafe (source) {
|
||||
if (typeof source !== 'string') { throw new TypeError('Expected String') }
|
||||
if (source.length === 0) { return '' }
|
||||
if (source.length === 0) { return new Uint8Array() }
|
||||
var psz = 0
|
||||
// Skip leading spaces.
|
||||
if (source[psz] === ' ') { return }
|
||||
// Skip and count leading '1's.
|
||||
var zeroes = 0
|
||||
var length = 0
|
||||
@@ -92,14 +96,12 @@ this.baseX = function base (ALPHABET) {
|
||||
length = i
|
||||
psz++
|
||||
}
|
||||
// Skip trailing spaces.
|
||||
if (source[psz] === ' ') { return }
|
||||
// Skip leading zeroes in b256.
|
||||
var it4 = size - length
|
||||
while (it4 !== size && b256[it4] === 0) {
|
||||
it4++
|
||||
}
|
||||
var vch = []
|
||||
var vch = new Uint8Array(zeroes + (size - it4))
|
||||
var j = zeroes
|
||||
while (it4 !== size) {
|
||||
vch[j++] = b256[it4++]
|
||||
Vendored
-7
File diff suppressed because one or more lines are too long
Vendored
+6
File diff suppressed because one or more lines are too long
+6
-7
@@ -10,17 +10,17 @@ global.fs = require('fs');
|
||||
global.WebCrypto = require('@peculiar/webcrypto').Crypto;
|
||||
|
||||
// application libraries to test
|
||||
global.$ = global.jQuery = require('./jquery-3.4.1');
|
||||
global.$ = global.jQuery = require('./jquery-3.6.0');
|
||||
global.RawDeflate = require('./rawinflate-0.3').RawDeflate;
|
||||
global.zlib = require('./zlib-1.2.11').zlib;
|
||||
global.zlib = require('./zlib-1.2.12').zlib;
|
||||
require('./prettify');
|
||||
global.prettyPrint = window.PR.prettyPrint;
|
||||
global.prettyPrintOne = window.PR.prettyPrintOne;
|
||||
global.showdown = require('./showdown-1.9.1');
|
||||
global.DOMPurify = require('./purify-2.2.7');
|
||||
global.baseX = require('./base-x-3.0.7').baseX;
|
||||
global.showdown = require('./showdown-2.0.3');
|
||||
global.DOMPurify = require('./purify-2.3.6');
|
||||
global.baseX = require('./base-x-4.0.0').baseX;
|
||||
global.Legacy = require('./legacy').Legacy;
|
||||
require('./bootstrap-3.3.7');
|
||||
require('./bootstrap-3.4.1');
|
||||
require('./privatebin');
|
||||
|
||||
// internal variables
|
||||
@@ -131,4 +131,3 @@ exports.jscMimeTypes = function() {
|
||||
exports.jscFormats = function() {
|
||||
return jsc.elements(formats);
|
||||
};
|
||||
|
||||
|
||||
Vendored
-2
File diff suppressed because one or more lines are too long
Vendored
+2
File diff suppressed because one or more lines are too long
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "privatebin",
|
||||
"version": "1.3.0",
|
||||
"version": "1.4.0",
|
||||
"description": "PrivateBin is a minimalist, open source online pastebin where the server has zero knowledge of pasted data. Data is encrypted/decrypted in the browser using 256 bit AES in Galois Counter mode (GCM).",
|
||||
"main": "privatebin.js",
|
||||
"directories": {
|
||||
|
||||
+144
-43
@@ -6,7 +6,7 @@
|
||||
* @see {@link https://github.com/PrivateBin/PrivateBin}
|
||||
* @copyright 2012 Sébastien SAUVAGE ({@link http://sebsauvage.net})
|
||||
* @license {@link https://www.opensource.org/licenses/zlib-license.php The zlib/libpng License}
|
||||
* @version 1.3.5
|
||||
* @version 1.4.0
|
||||
* @name PrivateBin
|
||||
* @namespace
|
||||
*/
|
||||
@@ -52,6 +52,31 @@ jQuery.PrivateBin = (function($, RawDeflate) {
|
||||
*/
|
||||
let z;
|
||||
|
||||
/**
|
||||
* DOMpurify settings for HTML content
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
const purifyHtmlConfig = {
|
||||
ALLOWED_URI_REGEXP: /^(?:(?:(?:f|ht)tps?|mailto|magnet):)/i,
|
||||
SAFE_FOR_JQUERY: true,
|
||||
USE_PROFILES: {
|
||||
html: true
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* DOMpurify settings for SVG content
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
const purifySvgConfig = {
|
||||
USE_PROFILES: {
|
||||
svg: true,
|
||||
svgFilters: true
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* CryptoData class
|
||||
*
|
||||
@@ -409,7 +434,8 @@ jQuery.PrivateBin = (function($, RawDeflate) {
|
||||
element.html().replace(
|
||||
/(((https?|ftp):\/\/[\w?!=&.\/-;#@~%+*-]+(?![\w\s?!&.\/;#~%"=-]>))|((magnet):[\w?=&.\/-;#@~%+*-]+))/ig,
|
||||
'<a href="$1" rel="nofollow noopener noreferrer">$1</a>'
|
||||
)
|
||||
),
|
||||
purifyHtmlConfig
|
||||
)
|
||||
);
|
||||
};
|
||||
@@ -601,7 +627,7 @@ jQuery.PrivateBin = (function($, RawDeflate) {
|
||||
* @prop {string[]}
|
||||
* @readonly
|
||||
*/
|
||||
const supportedLanguages = ['bg', 'cs', 'de', 'es', 'fr', 'he', 'hu', 'it', 'lt', 'no', 'nl', 'pl', 'pt', 'oc', 'ru', 'sl', 'uk', 'zh'];
|
||||
const supportedLanguages = ['bg', 'ca', 'co', 'cs', 'de', 'es', 'et', 'fi', 'fr', 'he', 'hu', 'id', 'it', 'jbo', 'lt', 'no', 'nl', 'pl', 'pt', 'oc', 'ru', 'sl', 'uk', 'zh'];
|
||||
|
||||
/**
|
||||
* built in language
|
||||
@@ -767,7 +793,7 @@ jQuery.PrivateBin = (function($, RawDeflate) {
|
||||
/**
|
||||
* per language functions to use to determine the plural form
|
||||
*
|
||||
* @see {@link http://localization-guide.readthedocs.org/en/latest/l10n/pluralforms.html}
|
||||
* @see {@link https://docs.translatehouse.org/projects/localization-guide/en/latest/l10n/pluralforms.html}
|
||||
* @name I18n.getPluralForm
|
||||
* @function
|
||||
* @param {int} n
|
||||
@@ -778,6 +804,7 @@ jQuery.PrivateBin = (function($, RawDeflate) {
|
||||
{
|
||||
case 'cs':
|
||||
return n === 1 ? 0 : (n >= 2 && n <=4 ? 1 : 2);
|
||||
case 'co':
|
||||
case 'fr':
|
||||
case 'oc':
|
||||
case 'zh':
|
||||
@@ -785,6 +812,7 @@ jQuery.PrivateBin = (function($, RawDeflate) {
|
||||
case 'he':
|
||||
return n === 1 ? 0 : (n === 2 ? 1 : ((n < 0 || n > 10) && (n % 10 === 0) ? 2 : 3));
|
||||
case 'id':
|
||||
case 'jbo':
|
||||
return 0;
|
||||
case 'lt':
|
||||
return n % 10 === 1 && n % 100 !== 11 ? 0 : ((n % 10 >= 2 && n % 100 < 10 || n % 100 >= 20) ? 1 : 2);
|
||||
@@ -795,7 +823,7 @@ jQuery.PrivateBin = (function($, RawDeflate) {
|
||||
return n % 10 === 1 && n % 100 !== 11 ? 0 : (n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 10 || n % 100 >= 20) ? 1 : 2);
|
||||
case 'sl':
|
||||
return n % 100 === 1 ? 1 : (n % 100 === 2 ? 2 : (n % 100 === 3 || n % 100 === 4 ? 3 : 0));
|
||||
// bg, ca, de, en, es, hu, it, nl, no, pt
|
||||
// bg, ca, de, en, es, et, fi, hu, it, nl, no, pt
|
||||
default:
|
||||
return n !== 1 ? 1 : 0;
|
||||
}
|
||||
@@ -2534,7 +2562,8 @@ jQuery.PrivateBin = (function($, RawDeflate) {
|
||||
// let showdown convert the HTML and sanitize HTML *afterwards*!
|
||||
$plainText.html(
|
||||
DOMPurify.sanitize(
|
||||
converter.makeHtml(text)
|
||||
converter.makeHtml(text),
|
||||
purifyHtmlConfig
|
||||
)
|
||||
);
|
||||
// add table classes from bootstrap css
|
||||
@@ -2750,6 +2779,34 @@ jQuery.PrivateBin = (function($, RawDeflate) {
|
||||
$dropzone;
|
||||
|
||||
/**
|
||||
* get blob URL from string data and mime type
|
||||
*
|
||||
* @name AttachmentViewer.getBlobUrl
|
||||
* @private
|
||||
* @function
|
||||
* @param {string} data - raw data of attachment
|
||||
* @param {string} data - mime type of attachment
|
||||
* @return {string} objectURL
|
||||
*/
|
||||
function getBlobUrl(data, mimeType)
|
||||
{
|
||||
// Transform into a Blob
|
||||
const buf = new Uint8Array(data.length);
|
||||
for (let i = 0; i < data.length; ++i) {
|
||||
buf[i] = data.charCodeAt(i);
|
||||
}
|
||||
const blob = new window.Blob(
|
||||
[buf],
|
||||
{
|
||||
type: mimeType
|
||||
}
|
||||
);
|
||||
|
||||
// Get blob URL
|
||||
return window.URL.createObjectURL(blob);
|
||||
}
|
||||
|
||||
/**
|
||||
* sets the attachment but does not yet show it
|
||||
*
|
||||
* @name AttachmentViewer.setAttachment
|
||||
@@ -2759,44 +2816,42 @@ jQuery.PrivateBin = (function($, RawDeflate) {
|
||||
*/
|
||||
me.setAttachment = function(attachmentData, fileName)
|
||||
{
|
||||
// data URI format: data:[<mediaType>][;base64],<data>
|
||||
// skip, if attachments got disabled
|
||||
if (!$attachmentLink || !$attachmentPreview) return;
|
||||
|
||||
// data URI format: data:[<mimeType>][;base64],<data>
|
||||
|
||||
// position in data URI string of where data begins
|
||||
const base64Start = attachmentData.indexOf(',') + 1;
|
||||
// position in data URI string of where mediaType ends
|
||||
const mediaTypeEnd = attachmentData.indexOf(';');
|
||||
// position in data URI string of where mimeType ends
|
||||
const mimeTypeEnd = attachmentData.indexOf(';');
|
||||
|
||||
// extract mediaType
|
||||
const mediaType = attachmentData.substring(5, mediaTypeEnd);
|
||||
// extract mimeType
|
||||
const mimeType = attachmentData.substring(5, mimeTypeEnd);
|
||||
// extract data and convert to binary
|
||||
const rawData = attachmentData.substring(base64Start);
|
||||
const decodedData = rawData.length > 0 ? atob(rawData) : '';
|
||||
|
||||
// Transform into a Blob
|
||||
const buf = new Uint8Array(decodedData.length);
|
||||
for (let i = 0; i < decodedData.length; ++i) {
|
||||
buf[i] = decodedData.charCodeAt(i);
|
||||
}
|
||||
const blob = new window.Blob([ buf ], { type: mediaType });
|
||||
|
||||
// Get Blob URL
|
||||
const blobUrl = window.URL.createObjectURL(blob);
|
||||
|
||||
// IE does not support setting a data URI on an a element
|
||||
// Using msSaveBlob to download
|
||||
if (window.Blob && navigator.msSaveBlob) {
|
||||
$attachmentLink.off('click').on('click', function () {
|
||||
navigator.msSaveBlob(blob, fileName);
|
||||
});
|
||||
} else {
|
||||
$attachmentLink.attr('href', blobUrl);
|
||||
}
|
||||
let blobUrl = getBlobUrl(decodedData, mimeType);
|
||||
$attachmentLink.attr('href', blobUrl);
|
||||
|
||||
if (typeof fileName !== 'undefined') {
|
||||
$attachmentLink.attr('download', fileName);
|
||||
}
|
||||
|
||||
me.handleBlobAttachmentPreview($attachmentPreview, blobUrl, mediaType);
|
||||
// sanitize SVG preview
|
||||
// prevents executing embedded scripts when CSP is not set and user
|
||||
// right-clicks/long-taps and opens the SVG in a new tab - prevented
|
||||
// in the preview by use of an img tag, which disables scripts, too
|
||||
if (mimeType.match(/^image\/.*svg/i)) {
|
||||
const sanitizedData = DOMPurify.sanitize(
|
||||
decodedData,
|
||||
purifySvgConfig
|
||||
);
|
||||
blobUrl = getBlobUrl(sanitizedData, mimeType);
|
||||
}
|
||||
|
||||
me.handleBlobAttachmentPreview($attachmentPreview, blobUrl, mimeType);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -2807,6 +2862,9 @@ jQuery.PrivateBin = (function($, RawDeflate) {
|
||||
*/
|
||||
me.showAttachment = function()
|
||||
{
|
||||
// skip, if attachments got disabled
|
||||
if (!$attachment || !$attachmentPreview) return;
|
||||
|
||||
$attachment.removeClass('hidden');
|
||||
|
||||
if (attachmentHasPreview) {
|
||||
@@ -3014,13 +3072,13 @@ jQuery.PrivateBin = (function($, RawDeflate) {
|
||||
me.handleBlobAttachmentPreview = function ($targetElement, blobUrl, mimeType) {
|
||||
if (blobUrl) {
|
||||
attachmentHasPreview = true;
|
||||
if (mimeType.match(/image\//i)) {
|
||||
if (mimeType.match(/^image\//i)) {
|
||||
$targetElement.html(
|
||||
$(document.createElement('img'))
|
||||
.attr('src', blobUrl)
|
||||
.attr('class', 'img-thumbnail')
|
||||
);
|
||||
} else if (mimeType.match(/video\//i)) {
|
||||
} else if (mimeType.match(/^video\//i)) {
|
||||
$targetElement.html(
|
||||
$(document.createElement('video'))
|
||||
.attr('controls', 'true')
|
||||
@@ -3031,7 +3089,7 @@ jQuery.PrivateBin = (function($, RawDeflate) {
|
||||
.attr('type', mimeType)
|
||||
.attr('src', blobUrl))
|
||||
);
|
||||
} else if (mimeType.match(/audio\//i)) {
|
||||
} else if (mimeType.match(/^audio\//i)) {
|
||||
$targetElement.html(
|
||||
$(document.createElement('audio'))
|
||||
.attr('controls', 'true')
|
||||
@@ -3525,6 +3583,7 @@ jQuery.PrivateBin = (function($, RawDeflate) {
|
||||
$password,
|
||||
$passwordInput,
|
||||
$rawTextButton,
|
||||
$downloadTextButton,
|
||||
$qrCodeLink,
|
||||
$emailLink,
|
||||
$sendButton,
|
||||
@@ -3662,10 +3721,41 @@ jQuery.PrivateBin = (function($, RawDeflate) {
|
||||
for (let i = 0; i < $head.length; ++i) {
|
||||
newDoc.write($head[i].outerHTML);
|
||||
}
|
||||
newDoc.write('</head><body><pre>' + DOMPurify.sanitize(Helper.htmlEntities(paste)) + '</pre></body></html>');
|
||||
newDoc.write(
|
||||
'</head><body><pre>' +
|
||||
DOMPurify.sanitize(
|
||||
Helper.htmlEntities(paste),
|
||||
purifyHtmlConfig
|
||||
) +
|
||||
'</pre></body></html>'
|
||||
);
|
||||
newDoc.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* download text
|
||||
*
|
||||
* @name TopNav.downloadText
|
||||
* @private
|
||||
* @function
|
||||
*/
|
||||
function downloadText()
|
||||
{
|
||||
var filename='paste-' + Model.getPasteId() + '.txt';
|
||||
var text = PasteViewer.getText();
|
||||
|
||||
var element = document.createElement('a');
|
||||
element.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(text));
|
||||
element.setAttribute('download', filename);
|
||||
|
||||
element.style.display = 'none';
|
||||
document.body.appendChild(element);
|
||||
|
||||
element.click();
|
||||
|
||||
document.body.removeChild(element);
|
||||
}
|
||||
|
||||
/**
|
||||
* saves the language in a cookie and reloads the page
|
||||
*
|
||||
@@ -3676,7 +3766,7 @@ jQuery.PrivateBin = (function($, RawDeflate) {
|
||||
*/
|
||||
function setLanguage(event)
|
||||
{
|
||||
document.cookie = 'lang=' + $(event.target).data('lang');
|
||||
document.cookie = 'lang=' + $(event.target).data('lang') + ';secure';
|
||||
UiHelper.reloadHome();
|
||||
}
|
||||
|
||||
@@ -3892,6 +3982,7 @@ jQuery.PrivateBin = (function($, RawDeflate) {
|
||||
$newButton.removeClass('hidden');
|
||||
$cloneButton.removeClass('hidden');
|
||||
$rawTextButton.removeClass('hidden');
|
||||
$downloadTextButton.removeClass('hidden');
|
||||
$qrCodeLink.removeClass('hidden');
|
||||
|
||||
viewButtonsDisplayed = true;
|
||||
@@ -3912,6 +4003,7 @@ jQuery.PrivateBin = (function($, RawDeflate) {
|
||||
$cloneButton.addClass('hidden');
|
||||
$newButton.addClass('hidden');
|
||||
$rawTextButton.addClass('hidden');
|
||||
$downloadTextButton.addClass('hidden');
|
||||
$qrCodeLink.addClass('hidden');
|
||||
me.hideEmailButton();
|
||||
|
||||
@@ -4073,6 +4165,17 @@ jQuery.PrivateBin = (function($, RawDeflate) {
|
||||
$rawTextButton.addClass('hidden');
|
||||
};
|
||||
|
||||
/**
|
||||
* only hides the download text button
|
||||
*
|
||||
* @name TopNav.hideRawButton
|
||||
* @function
|
||||
*/
|
||||
me.hideDownloadButton = function()
|
||||
{
|
||||
$downloadTextButton.addClass('hidden');
|
||||
};
|
||||
|
||||
/**
|
||||
* only hides the qr code button
|
||||
*
|
||||
@@ -4334,6 +4437,7 @@ jQuery.PrivateBin = (function($, RawDeflate) {
|
||||
$password = $('#password');
|
||||
$passwordInput = $('#passwordinput');
|
||||
$rawTextButton = $('#rawtextbutton');
|
||||
$downloadTextButton = $('#downloadtextbutton');
|
||||
$retryButton = $('#retrybutton');
|
||||
$sendButton = $('#sendbutton');
|
||||
$qrCodeLink = $('#qrcodelink');
|
||||
@@ -4351,6 +4455,7 @@ jQuery.PrivateBin = (function($, RawDeflate) {
|
||||
$sendButton.click(PasteEncrypter.sendPaste);
|
||||
$cloneButton.click(Controller.clonePaste);
|
||||
$rawTextButton.click(rawText);
|
||||
$downloadTextButton.click(downloadText);
|
||||
$retryButton.click(clickRetryButton);
|
||||
$fileRemoveButton.click(removeAttachment);
|
||||
$qrCodeLink.click(displayQrCode);
|
||||
@@ -4689,6 +4794,7 @@ jQuery.PrivateBin = (function($, RawDeflate) {
|
||||
TopNav.showEmailButton();
|
||||
|
||||
TopNav.hideRawButton();
|
||||
TopNav.hideDownloadButton();
|
||||
Editor.hide();
|
||||
|
||||
// parse and show text
|
||||
@@ -5351,11 +5457,6 @@ jQuery.PrivateBin = (function($, RawDeflate) {
|
||||
// first load translations
|
||||
I18n.loadTranslations();
|
||||
|
||||
DOMPurify.setConfig({
|
||||
ALLOWED_URI_REGEXP: /^(?:(?:(?:f|ht)tps?|mailto|magnet):)/i,
|
||||
SAFE_FOR_JQUERY: true
|
||||
});
|
||||
|
||||
// Add a hook to make all links open a new window
|
||||
DOMPurify.addHook('afterSanitizeAttributes', function(node) {
|
||||
// set all elements owning target to target=_blank
|
||||
@@ -5363,8 +5464,8 @@ jQuery.PrivateBin = (function($, RawDeflate) {
|
||||
node.setAttribute('target', '_blank');
|
||||
}
|
||||
// set non-HTML/MathML links to xlink:show=new
|
||||
if (!node.hasAttribute('target')
|
||||
&& (node.hasAttribute('xlink:href')
|
||||
if (!node.hasAttribute('target')
|
||||
&& (node.hasAttribute('xlink:href')
|
||||
|| node.hasAttribute('href'))) {
|
||||
node.setAttribute('xlink:show', 'new');
|
||||
}
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -110,4 +110,3 @@ describe('DiscussionViewer', function () {
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
+2
-3
@@ -52,12 +52,12 @@ describe('Editor', function () {
|
||||
!$.PrivateBin.Editor.isPreview() &&
|
||||
!$('#message').hasClass('hidden')
|
||||
);
|
||||
$('#messagepreview').click();
|
||||
$('#messagepreview').trigger('click');
|
||||
results.push(
|
||||
$.PrivateBin.Editor.isPreview() &&
|
||||
$('#message').hasClass('hidden')
|
||||
);
|
||||
$('#messageedit').click();
|
||||
$('#messageedit').trigger('click');
|
||||
results.push(
|
||||
!$.PrivateBin.Editor.isPreview() &&
|
||||
!$('#message').hasClass('hidden')
|
||||
@@ -68,4 +68,3 @@ describe('Editor', function () {
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
+9
-4
@@ -280,7 +280,8 @@ describe('TopNav', function () {
|
||||
it(
|
||||
'collapses the navigation when displayed on a small screen',
|
||||
function () {
|
||||
var results = [];
|
||||
var clean = jsdom(),
|
||||
results = [];
|
||||
$('body').html(
|
||||
'<nav><div class="navbar-header"><button type="button" ' +
|
||||
'class="navbar-toggle collapsed" data-toggle="collapse" ' +
|
||||
@@ -301,7 +302,11 @@ describe('TopNav', function () {
|
||||
$('.navbar-toggle').hasClass('collapsed') &&
|
||||
$('#navbar').attr('aria-expanded') != 'true'
|
||||
);
|
||||
$('.navbar-toggle').click();
|
||||
/*
|
||||
with the upgrade for bootstrap-3.3.7.js to bootstrap-3.4.1.js
|
||||
the mobile interface detection changed to check if the
|
||||
ontouchstart event exists, which broke this section of the test
|
||||
$('.navbar-toggle').trigger('click');
|
||||
results.push(
|
||||
!$('.navbar-toggle').hasClass('collapsed') &&
|
||||
$('#navbar').attr('aria-expanded') == 'true'
|
||||
@@ -311,7 +316,8 @@ describe('TopNav', function () {
|
||||
$('.navbar-toggle').hasClass('collapsed') &&
|
||||
$('#navbar').attr('aria-expanded') == 'false'
|
||||
);
|
||||
cleanup();
|
||||
*/
|
||||
clean();
|
||||
assert.ok(results.every(element => element));
|
||||
}
|
||||
);
|
||||
@@ -670,4 +676,3 @@ describe('TopNav', function () {
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
Binary file not shown.
@@ -26,9 +26,9 @@
|
||||
|
||||
let buff;
|
||||
if (typeof fetch === 'undefined') {
|
||||
buff = fs.readFileSync('zlib-1.2.11.wasm');
|
||||
buff = fs.readFileSync('zlib-1.2.12.wasm');
|
||||
} else {
|
||||
const resp = await fetch('js/zlib-1.2.11.wasm');
|
||||
const resp = await fetch('js/zlib-1.2.12.wasm');
|
||||
buff = await resp.arrayBuffer();
|
||||
}
|
||||
const module = await WebAssembly.compile(buff);
|
||||
Binary file not shown.
+31
-27
@@ -7,14 +7,13 @@
|
||||
* @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.3.5
|
||||
* @version 1.4.0
|
||||
*/
|
||||
|
||||
namespace PrivateBin;
|
||||
|
||||
use Exception;
|
||||
use PDO;
|
||||
use PrivateBin\Persistence\DataStore;
|
||||
|
||||
/**
|
||||
* Configuration
|
||||
@@ -45,7 +44,7 @@ class Configuration
|
||||
'fileupload' => false,
|
||||
'burnafterreadingselected' => false,
|
||||
'defaultformatter' => 'plaintext',
|
||||
'syntaxhighlightingtheme' => null,
|
||||
'syntaxhighlightingtheme' => '',
|
||||
'sizelimit' => 10485760,
|
||||
'template' => 'bootstrap',
|
||||
'info' => 'More information on the <a href=\'https://privatebin.info/\'>project page</a>.',
|
||||
@@ -55,7 +54,7 @@ class Configuration
|
||||
'urlshortener' => '',
|
||||
'qrcode' => true,
|
||||
'icon' => 'identicon',
|
||||
'cspheader' => 'default-src \'none\'; manifest-src \'self\'; connect-src * blob:; script-src \'self\' \'unsafe-eval\' resource:; style-src \'self\'; font-src \'self\'; img-src \'self\' data: blob:; media-src blob:; object-src blob:; sandbox allow-same-origin allow-scripts allow-forms allow-popups allow-modals allow-downloads',
|
||||
'cspheader' => 'default-src \'none\'; base-uri \'self\'; form-action \'none\'; manifest-src \'self\'; connect-src * blob:; script-src \'self\' \'unsafe-eval\'; style-src \'self\'; font-src \'self\'; frame-ancestors \'none\'; img-src \'self\' data: blob:; media-src blob:; object-src blob:; sandbox allow-same-origin allow-scripts allow-forms allow-popups allow-modals allow-downloads',
|
||||
'zerobincompatibility' => false,
|
||||
'httpwarning' => true,
|
||||
'compression' => 'zlib',
|
||||
@@ -79,14 +78,14 @@ class Configuration
|
||||
'markdown' => 'Markdown',
|
||||
),
|
||||
'traffic' => array(
|
||||
'limit' => 10,
|
||||
'header' => null,
|
||||
'dir' => 'data',
|
||||
'limit' => 10,
|
||||
'header' => '',
|
||||
'exempted' => '',
|
||||
'creators' => '',
|
||||
),
|
||||
'purge' => array(
|
||||
'limit' => 300,
|
||||
'batchsize' => 10,
|
||||
'dir' => 'data',
|
||||
),
|
||||
'model' => array(
|
||||
'class' => 'Filesystem',
|
||||
@@ -103,28 +102,23 @@ class Configuration
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$basePaths = array();
|
||||
$config = array();
|
||||
$basePath = (getenv('CONFIG_PATH') !== false ? getenv('CONFIG_PATH') : PATH . 'cfg') . DIRECTORY_SEPARATOR;
|
||||
$configIni = $basePath . 'conf.ini';
|
||||
$configFile = $basePath . 'conf.php';
|
||||
|
||||
// rename INI files to avoid configuration leakage
|
||||
if (is_readable($configIni)) {
|
||||
DataStore::prependRename($configIni, $configFile, ';');
|
||||
|
||||
// cleanup sample, too
|
||||
$configIniSample = $configIni . '.sample';
|
||||
if (is_readable($configIniSample)) {
|
||||
DataStore::prependRename($configIniSample, $basePath . 'conf.sample.php', ';');
|
||||
}
|
||||
$configPath = getenv('CONFIG_PATH');
|
||||
if ($configPath !== false && !empty($configPath)) {
|
||||
$basePaths[] = $configPath;
|
||||
}
|
||||
|
||||
if (is_readable($configFile)) {
|
||||
$config = parse_ini_file($configFile, true);
|
||||
foreach (array('main', 'model', 'model_options') as $section) {
|
||||
if (!array_key_exists($section, $config)) {
|
||||
throw new Exception(I18n::_('PrivateBin requires configuration section [%s] to be present in configuration file.', $section), 2);
|
||||
$basePaths[] = PATH . 'cfg';
|
||||
foreach ($basePaths as $basePath) {
|
||||
$configFile = $basePath . DIRECTORY_SEPARATOR . 'conf.php';
|
||||
if (is_readable($configFile)) {
|
||||
$config = parse_ini_file($configFile, true);
|
||||
foreach (array('main', 'model', 'model_options') as $section) {
|
||||
if (!array_key_exists($section, $config)) {
|
||||
throw new Exception(I18n::_('PrivateBin requires configuration section [%s] to be present in configuration file.', $section), 2);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -152,6 +146,16 @@ class Configuration
|
||||
'pwd' => null,
|
||||
'opt' => array(PDO::ATTR_PERSISTENT => true),
|
||||
);
|
||||
} elseif (
|
||||
$section == 'model_options' && in_array(
|
||||
$this->_configuration['model']['class'],
|
||||
array('GoogleCloudStorage')
|
||||
)
|
||||
) {
|
||||
$values = array(
|
||||
'bucket' => getenv('PRIVATEBIN_GCS_BUCKET') ? getenv('PRIVATEBIN_GCS_BUCKET') : null,
|
||||
'prefix' => 'pastes',
|
||||
);
|
||||
}
|
||||
|
||||
// "*_options" sections don't require all defaults to be set
|
||||
|
||||
+27
-19
@@ -7,7 +7,7 @@
|
||||
* @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.3.5
|
||||
* @version 1.4.0
|
||||
*/
|
||||
|
||||
namespace PrivateBin;
|
||||
@@ -28,7 +28,7 @@ class Controller
|
||||
*
|
||||
* @const string
|
||||
*/
|
||||
const VERSION = '1.3.5';
|
||||
const VERSION = '1.4.0';
|
||||
|
||||
/**
|
||||
* minimal required PHP version
|
||||
@@ -162,7 +162,6 @@ class Controller
|
||||
$this->_model = new Model($this->_conf);
|
||||
$this->_request = new Request;
|
||||
$this->_urlBase = $this->_request->getRequestUri();
|
||||
ServerSalt::setPath($this->_conf->getKey('dir', 'traffic'));
|
||||
|
||||
// set default language
|
||||
$lang = $this->_conf->getKey('languagedefault');
|
||||
@@ -170,7 +169,7 @@ class Controller
|
||||
// force default language, if language selection is disabled and a default is set
|
||||
if (!$this->_conf->getKey('languageselection') && strlen($lang) == 2) {
|
||||
$_COOKIE['lang'] = $lang;
|
||||
setcookie('lang', $lang);
|
||||
setcookie('lang', $lang, 0, '', '', true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -196,20 +195,14 @@ class Controller
|
||||
*/
|
||||
private function _create()
|
||||
{
|
||||
// Ensure last paste from visitors IP address was more than configured amount of seconds ago.
|
||||
ServerSalt::setStore($this->_model->getStore());
|
||||
TrafficLimiter::setConfiguration($this->_conf);
|
||||
TrafficLimiter::setStore($this->_model->getStore());
|
||||
try {
|
||||
// Ensure last paste from visitors IP address was more than configured amount of seconds ago.
|
||||
TrafficLimiter::setConfiguration($this->_conf);
|
||||
if (!TrafficLimiter::canPass()) {
|
||||
$this->_return_message(
|
||||
1, I18n::_(
|
||||
'Please wait %d seconds between each post.',
|
||||
$this->_conf->getKey('limit', 'traffic')
|
||||
)
|
||||
);
|
||||
return;
|
||||
}
|
||||
TrafficLimiter::canPass();
|
||||
} catch (Exception $e) {
|
||||
$this->_return_message(1, I18n::_($e->getMessage()));
|
||||
$this->_return_message(1, $e->getMessage());
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -346,10 +339,14 @@ class Controller
|
||||
header('Last-Modified: ' . $time);
|
||||
header('Vary: Accept');
|
||||
header('Content-Security-Policy: ' . $this->_conf->getKey('cspheader'));
|
||||
header('Cross-Origin-Resource-Policy: same-origin');
|
||||
header('Cross-Origin-Embedder-Policy: require-corp');
|
||||
header('Cross-Origin-Opener-Policy: same-origin');
|
||||
header('Permissions-Policy: interest-cohort=()');
|
||||
header('Referrer-Policy: no-referrer');
|
||||
header('X-Xss-Protection: 1; mode=block');
|
||||
header('X-Frame-Options: DENY');
|
||||
header('X-Content-Type-Options: nosniff');
|
||||
header('X-Frame-Options: deny');
|
||||
header('X-XSS-Protection: 1; mode=block');
|
||||
|
||||
// label all the expiration options
|
||||
$expire = array();
|
||||
@@ -364,9 +361,19 @@ class Controller
|
||||
$languageselection = '';
|
||||
if ($this->_conf->getKey('languageselection')) {
|
||||
$languageselection = I18n::getLanguage();
|
||||
setcookie('lang', $languageselection);
|
||||
setcookie('lang', $languageselection, 0, '', '', true);
|
||||
}
|
||||
|
||||
// strip policies that are unsupported in meta tag
|
||||
$metacspheader = str_replace(
|
||||
array(
|
||||
'frame-ancestors \'none\'; ',
|
||||
'; sandbox allow-same-origin allow-scripts allow-forms allow-popups allow-modals allow-downloads',
|
||||
),
|
||||
'',
|
||||
$this->_conf->getKey('cspheader')
|
||||
);
|
||||
|
||||
$page = new View;
|
||||
$page->assign('NAME', $this->_conf->getKey('name'));
|
||||
$page->assign('BASEPATH', I18n::_($this->_conf->getKey('basepath')));
|
||||
@@ -395,6 +402,7 @@ class Controller
|
||||
$page->assign('HTTPWARNING', $this->_conf->getKey('httpwarning'));
|
||||
$page->assign('HTTPSLINK', 'https://' . $this->_request->getHost() . $this->_request->getRequestUri());
|
||||
$page->assign('COMPRESSION', $this->_conf->getKey('compression'));
|
||||
$page->assign('CSPHEADER', $metacspheader);
|
||||
$page->draw($this->_conf->getKey('template'));
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
* @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.3.5
|
||||
* @version 1.4.0
|
||||
*/
|
||||
|
||||
namespace PrivateBin\Data;
|
||||
@@ -15,12 +15,12 @@ namespace PrivateBin\Data;
|
||||
/**
|
||||
* AbstractData
|
||||
*
|
||||
* Abstract model for PrivateBin data access, implemented as a singleton.
|
||||
* Abstract model for data access, implemented as a singleton.
|
||||
*/
|
||||
abstract class AbstractData
|
||||
{
|
||||
/**
|
||||
* singleton instance
|
||||
* Singleton instance
|
||||
*
|
||||
* @access protected
|
||||
* @static
|
||||
@@ -29,9 +29,18 @@ abstract class AbstractData
|
||||
protected static $_instance = null;
|
||||
|
||||
/**
|
||||
* enforce singleton, disable constructor
|
||||
* cache for the traffic limiter
|
||||
*
|
||||
* Instantiate using {@link getInstance()}, privatebin is a singleton object.
|
||||
* @access private
|
||||
* @static
|
||||
* @var array
|
||||
*/
|
||||
protected static $_last_cache = array();
|
||||
|
||||
/**
|
||||
* Enforce singleton, disable constructor
|
||||
*
|
||||
* Instantiate using {@link getInstance()}, this object implements the singleton pattern.
|
||||
*
|
||||
* @access protected
|
||||
*/
|
||||
@@ -40,9 +49,9 @@ abstract class AbstractData
|
||||
}
|
||||
|
||||
/**
|
||||
* enforce singleton, disable cloning
|
||||
* Enforce singleton, disable cloning
|
||||
*
|
||||
* Instantiate using {@link getInstance()}, privatebin is a singleton object.
|
||||
* Instantiate using {@link getInstance()}, this object implements the singleton pattern.
|
||||
*
|
||||
* @access private
|
||||
*/
|
||||
@@ -51,7 +60,7 @@ abstract class AbstractData
|
||||
}
|
||||
|
||||
/**
|
||||
* get instance of singleton
|
||||
* Get instance of singleton
|
||||
*
|
||||
* @access public
|
||||
* @static
|
||||
@@ -130,6 +139,46 @@ abstract class AbstractData
|
||||
*/
|
||||
abstract public function existsComment($pasteid, $parentid, $commentid);
|
||||
|
||||
/**
|
||||
* Purge outdated entries.
|
||||
*
|
||||
* @access public
|
||||
* @param string $namespace
|
||||
* @param int $time
|
||||
* @return void
|
||||
*/
|
||||
public function purgeValues($namespace, $time)
|
||||
{
|
||||
if ($namespace === 'traffic_limiter') {
|
||||
foreach (self::$_last_cache as $key => $last_submission) {
|
||||
if ($last_submission <= $time) {
|
||||
unset(self::$_last_cache[$key]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Save a value.
|
||||
*
|
||||
* @access public
|
||||
* @param string $value
|
||||
* @param string $namespace
|
||||
* @param string $key
|
||||
* @return bool
|
||||
*/
|
||||
abstract public function setValue($value, $namespace, $key = '');
|
||||
|
||||
/**
|
||||
* Load a value.
|
||||
*
|
||||
* @access public
|
||||
* @param string $namespace
|
||||
* @param string $key
|
||||
* @return string
|
||||
*/
|
||||
abstract public function getValue($namespace, $key = '');
|
||||
|
||||
/**
|
||||
* Returns up to batch size number of paste ids that have expired
|
||||
*
|
||||
|
||||
+324
-130
@@ -7,7 +7,7 @@
|
||||
* @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.3.5
|
||||
* @version 1.4.0
|
||||
*/
|
||||
|
||||
namespace PrivateBin\Data;
|
||||
@@ -97,6 +97,11 @@ class Database extends AbstractData
|
||||
self::$_type = strtolower(
|
||||
substr($options['dsn'], 0, strpos($options['dsn'], ':'))
|
||||
);
|
||||
// MySQL uses backticks to quote identifiers by default,
|
||||
// tell it to expect ANSI SQL double quotes
|
||||
if (self::$_type === 'mysql' && defined('PDO::MYSQL_ATTR_INIT_COMMAND')) {
|
||||
$options['opt'][PDO::MYSQL_ATTR_INIT_COMMAND] = "SET sql_mode='ANSI_QUOTES'";
|
||||
}
|
||||
$tableQuery = self::_getTableQuery(self::$_type);
|
||||
self::$_db = new PDO(
|
||||
$options['dsn'],
|
||||
@@ -198,21 +203,25 @@ class Database extends AbstractData
|
||||
$opendiscussion = $paste['adata'][2];
|
||||
$burnafterreading = $paste['adata'][3];
|
||||
}
|
||||
return self::_exec(
|
||||
'INSERT INTO ' . self::_sanitizeIdentifier('paste') .
|
||||
' VALUES(?,?,?,?,?,?,?,?,?)',
|
||||
array(
|
||||
$pasteid,
|
||||
$isVersion1 ? $paste['data'] : Json::encode($paste),
|
||||
$created,
|
||||
$expire_date,
|
||||
(int) $opendiscussion,
|
||||
(int) $burnafterreading,
|
||||
Json::encode($meta),
|
||||
$attachment,
|
||||
$attachmentname,
|
||||
)
|
||||
);
|
||||
try {
|
||||
return self::_exec(
|
||||
'INSERT INTO "' . self::_sanitizeIdentifier('paste') .
|
||||
'" VALUES(?,?,?,?,?,?,?,?,?)',
|
||||
array(
|
||||
$pasteid,
|
||||
$isVersion1 ? $paste['data'] : Json::encode($paste),
|
||||
$created,
|
||||
$expire_date,
|
||||
(int) $opendiscussion,
|
||||
(int) $burnafterreading,
|
||||
Json::encode($meta),
|
||||
$attachment,
|
||||
$attachmentname,
|
||||
)
|
||||
);
|
||||
} catch (Exception $e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -229,11 +238,14 @@ class Database extends AbstractData
|
||||
}
|
||||
|
||||
self::$_cache[$pasteid] = false;
|
||||
$paste = self::_select(
|
||||
'SELECT * FROM ' . self::_sanitizeIdentifier('paste') .
|
||||
' WHERE dataid = ?', array($pasteid), true
|
||||
);
|
||||
|
||||
try {
|
||||
$paste = self::_select(
|
||||
'SELECT * FROM "' . self::_sanitizeIdentifier('paste') .
|
||||
'" WHERE "dataid" = ?', array($pasteid), true
|
||||
);
|
||||
} catch (Exception $e) {
|
||||
$paste = false;
|
||||
}
|
||||
if ($paste === false) {
|
||||
return false;
|
||||
}
|
||||
@@ -290,12 +302,12 @@ class Database extends AbstractData
|
||||
public function delete($pasteid)
|
||||
{
|
||||
self::_exec(
|
||||
'DELETE FROM ' . self::_sanitizeIdentifier('paste') .
|
||||
' WHERE dataid = ?', array($pasteid)
|
||||
'DELETE FROM "' . self::_sanitizeIdentifier('paste') .
|
||||
'" WHERE "dataid" = ?', array($pasteid)
|
||||
);
|
||||
self::_exec(
|
||||
'DELETE FROM ' . self::_sanitizeIdentifier('comment') .
|
||||
' WHERE pasteid = ?', array($pasteid)
|
||||
'DELETE FROM "' . self::_sanitizeIdentifier('comment') .
|
||||
'" WHERE "pasteid" = ?', array($pasteid)
|
||||
);
|
||||
if (
|
||||
array_key_exists($pasteid, self::$_cache)
|
||||
@@ -348,19 +360,23 @@ class Database extends AbstractData
|
||||
$meta[$key] = null;
|
||||
}
|
||||
}
|
||||
return self::_exec(
|
||||
'INSERT INTO ' . self::_sanitizeIdentifier('comment') .
|
||||
' VALUES(?,?,?,?,?,?,?)',
|
||||
array(
|
||||
$commentid,
|
||||
$pasteid,
|
||||
$parentid,
|
||||
$data,
|
||||
$meta['nickname'],
|
||||
$meta[$iconKey],
|
||||
$meta[$createdKey],
|
||||
)
|
||||
);
|
||||
try {
|
||||
return self::_exec(
|
||||
'INSERT INTO "' . self::_sanitizeIdentifier('comment') .
|
||||
'" VALUES(?,?,?,?,?,?,?)',
|
||||
array(
|
||||
$commentid,
|
||||
$pasteid,
|
||||
$parentid,
|
||||
$data,
|
||||
$meta['nickname'],
|
||||
$meta[$iconKey],
|
||||
$meta[$createdKey],
|
||||
)
|
||||
);
|
||||
} catch (Exception $e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -373,13 +389,13 @@ class Database extends AbstractData
|
||||
public function readComments($pasteid)
|
||||
{
|
||||
$rows = self::_select(
|
||||
'SELECT * FROM ' . self::_sanitizeIdentifier('comment') .
|
||||
' WHERE pasteid = ?', array($pasteid)
|
||||
'SELECT * FROM "' . self::_sanitizeIdentifier('comment') .
|
||||
'" WHERE "pasteid" = ?', array($pasteid)
|
||||
);
|
||||
|
||||
// create comment list
|
||||
$comments = array();
|
||||
if (count($rows)) {
|
||||
if (is_array($rows) && count($rows)) {
|
||||
foreach ($rows as $row) {
|
||||
$i = $this->getOpenSlot($comments, (int) $row['postdate']);
|
||||
$data = Json::decode($row['data']);
|
||||
@@ -416,13 +432,85 @@ class Database extends AbstractData
|
||||
*/
|
||||
public function existsComment($pasteid, $parentid, $commentid)
|
||||
{
|
||||
return (bool) self::_select(
|
||||
'SELECT dataid FROM ' . self::_sanitizeIdentifier('comment') .
|
||||
' WHERE pasteid = ? AND parentid = ? AND dataid = ?',
|
||||
array($pasteid, $parentid, $commentid), true
|
||||
try {
|
||||
return (bool) self::_select(
|
||||
'SELECT "dataid" FROM "' . self::_sanitizeIdentifier('comment') .
|
||||
'" WHERE "pasteid" = ? AND "parentid" = ? AND "dataid" = ?',
|
||||
array($pasteid, $parentid, $commentid), true
|
||||
);
|
||||
} catch (Exception $e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Save a value.
|
||||
*
|
||||
* @access public
|
||||
* @param string $value
|
||||
* @param string $namespace
|
||||
* @param string $key
|
||||
* @return bool
|
||||
*/
|
||||
public function setValue($value, $namespace, $key = '')
|
||||
{
|
||||
if ($namespace === 'traffic_limiter') {
|
||||
self::$_last_cache[$key] = $value;
|
||||
try {
|
||||
$value = Json::encode(self::$_last_cache);
|
||||
} catch (Exception $e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return self::_exec(
|
||||
'UPDATE "' . self::_sanitizeIdentifier('config') .
|
||||
'" SET "value" = ? WHERE "id" = ?',
|
||||
array($value, strtoupper($namespace))
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load a value.
|
||||
*
|
||||
* @access public
|
||||
* @param string $namespace
|
||||
* @param string $key
|
||||
* @return string
|
||||
*/
|
||||
public function getValue($namespace, $key = '')
|
||||
{
|
||||
$configKey = strtoupper($namespace);
|
||||
$value = $this->_getConfig($configKey);
|
||||
if ($value === '') {
|
||||
// initialize the row, so that setValue can rely on UPDATE queries
|
||||
self::_exec(
|
||||
'INSERT INTO "' . self::_sanitizeIdentifier('config') .
|
||||
'" VALUES(?,?)',
|
||||
array($configKey, '')
|
||||
);
|
||||
|
||||
// migrate filesystem based salt into database
|
||||
$file = 'data' . DIRECTORY_SEPARATOR . 'salt.php';
|
||||
if ($namespace === 'salt' && is_readable($file)) {
|
||||
$value = Filesystem::getInstance(array('dir' => 'data'))->getValue('salt');
|
||||
$this->setValue($value, 'salt');
|
||||
@unlink($file);
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
if ($value && $namespace === 'traffic_limiter') {
|
||||
try {
|
||||
self::$_last_cache = Json::decode($value);
|
||||
} catch (Exception $e) {
|
||||
self::$_last_cache = array();
|
||||
}
|
||||
if (array_key_exists($key, self::$_last_cache)) {
|
||||
return self::$_last_cache[$key];
|
||||
}
|
||||
}
|
||||
return (string) $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns up to batch size number of paste ids that have expired
|
||||
*
|
||||
@@ -434,11 +522,12 @@ class Database extends AbstractData
|
||||
{
|
||||
$pastes = array();
|
||||
$rows = self::_select(
|
||||
'SELECT dataid FROM ' . self::_sanitizeIdentifier('paste') .
|
||||
' WHERE expiredate < ? AND expiredate != ? LIMIT ?',
|
||||
'SELECT "dataid" FROM "' . self::_sanitizeIdentifier('paste') .
|
||||
'" WHERE "expiredate" < ? AND "expiredate" != ? ' .
|
||||
(self::$_type === 'oci' ? 'FETCH NEXT ? ROWS ONLY' : 'LIMIT ?'),
|
||||
array(time(), 0, $batchsize)
|
||||
);
|
||||
if (count($rows)) {
|
||||
if (is_array($rows) && count($rows)) {
|
||||
foreach ($rows as $row) {
|
||||
$pastes[] = $row['dataid'];
|
||||
}
|
||||
@@ -459,7 +548,17 @@ class Database extends AbstractData
|
||||
private static function _exec($sql, array $params)
|
||||
{
|
||||
$statement = self::$_db->prepare($sql);
|
||||
$result = $statement->execute($params);
|
||||
foreach ($params as $key => &$parameter) {
|
||||
$position = $key + 1;
|
||||
if (is_int($parameter)) {
|
||||
$statement->bindParam($position, $parameter, PDO::PARAM_INT);
|
||||
} elseif (strlen($parameter) >= 4000) {
|
||||
$statement->bindParam($position, $parameter, PDO::PARAM_STR, strlen($parameter));
|
||||
} else {
|
||||
$statement->bindParam($position, $parameter);
|
||||
}
|
||||
}
|
||||
$result = $statement->execute();
|
||||
$statement->closeCursor();
|
||||
return $result;
|
||||
}
|
||||
@@ -479,10 +578,24 @@ class Database extends AbstractData
|
||||
{
|
||||
$statement = self::$_db->prepare($sql);
|
||||
$statement->execute($params);
|
||||
$result = $firstOnly ?
|
||||
$statement->fetch(PDO::FETCH_ASSOC) :
|
||||
$statement->fetchAll(PDO::FETCH_ASSOC);
|
||||
if ($firstOnly) {
|
||||
$result = $statement->fetch(PDO::FETCH_ASSOC);
|
||||
} elseif (self::$_type === 'oci') {
|
||||
// workaround for https://bugs.php.net/bug.php?id=46728
|
||||
$result = array();
|
||||
while ($row = $statement->fetch(PDO::FETCH_ASSOC)) {
|
||||
$result[] = array_map('self::_sanitizeClob', $row);
|
||||
}
|
||||
} else {
|
||||
$result = $statement->fetchAll(PDO::FETCH_ASSOC);
|
||||
}
|
||||
$statement->closeCursor();
|
||||
if (self::$_type === 'oci' && is_array($result)) {
|
||||
// returned CLOB values are streams, convert these into strings
|
||||
$result = $firstOnly ?
|
||||
array_map('self::_sanitizeClob', $result) :
|
||||
$result;
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
@@ -515,14 +628,15 @@ class Database extends AbstractData
|
||||
{
|
||||
switch ($type) {
|
||||
case 'ibm':
|
||||
$sql = 'SELECT tabname FROM SYSCAT.TABLES ';
|
||||
$sql = 'SELECT "tabname" FROM "SYSCAT"."TABLES"';
|
||||
break;
|
||||
case 'informix':
|
||||
$sql = 'SELECT tabname FROM systables ';
|
||||
$sql = 'SELECT "tabname" FROM "systables"';
|
||||
break;
|
||||
case 'mssql':
|
||||
$sql = 'SELECT name FROM sysobjects '
|
||||
. "WHERE type = 'U' ORDER BY name";
|
||||
// U: tables created by the user
|
||||
$sql = 'SELECT "name" FROM "sysobjects" '
|
||||
. 'WHERE "type" = \'U\' ORDER BY "name"';
|
||||
break;
|
||||
case 'mysql':
|
||||
$sql = 'SHOW TABLES';
|
||||
@@ -531,23 +645,23 @@ class Database extends AbstractData
|
||||
$sql = 'SELECT table_name FROM all_tables';
|
||||
break;
|
||||
case 'pgsql':
|
||||
$sql = 'SELECT c.relname AS table_name '
|
||||
. 'FROM pg_class c, pg_user u '
|
||||
. "WHERE c.relowner = u.usesysid AND c.relkind = 'r' "
|
||||
. 'AND NOT EXISTS (SELECT 1 FROM pg_views WHERE viewname = c.relname) '
|
||||
. "AND c.relname !~ '^(pg_|sql_)' "
|
||||
$sql = 'SELECT c."relname" AS "table_name" '
|
||||
. 'FROM "pg_class" c, "pg_user" u '
|
||||
. 'WHERE c."relowner" = u."usesysid" AND c."relkind" = \'r\' '
|
||||
. 'AND NOT EXISTS (SELECT 1 FROM "pg_views" WHERE "viewname" = c."relname") '
|
||||
. "AND c.\"relname\" !~ '^(pg_|sql_)' "
|
||||
. 'UNION '
|
||||
. 'SELECT c.relname AS table_name '
|
||||
. 'FROM pg_class c '
|
||||
. "WHERE c.relkind = 'r' "
|
||||
. 'AND NOT EXISTS (SELECT 1 FROM pg_views WHERE viewname = c.relname) '
|
||||
. 'AND NOT EXISTS (SELECT 1 FROM pg_user WHERE usesysid = c.relowner) '
|
||||
. "AND c.relname !~ '^pg_'";
|
||||
. 'SELECT c."relname" AS "table_name" '
|
||||
. 'FROM "pg_class" c '
|
||||
. "WHERE c.\"relkind\" = 'r' "
|
||||
. 'AND NOT EXISTS (SELECT 1 FROM "pg_views" WHERE "viewname" = c."relname") '
|
||||
. 'AND NOT EXISTS (SELECT 1 FROM "pg_user" WHERE "usesysid" = c."relowner") '
|
||||
. "AND c.\"relname\" !~ '^pg_'";
|
||||
break;
|
||||
case 'sqlite':
|
||||
$sql = "SELECT name FROM sqlite_master WHERE type='table' "
|
||||
. 'UNION ALL SELECT name FROM sqlite_temp_master '
|
||||
. "WHERE type='table' ORDER BY name";
|
||||
$sql = 'SELECT "name" FROM "sqlite_master" WHERE "type"=\'table\' '
|
||||
. 'UNION ALL SELECT "name" FROM "sqlite_temp_master" '
|
||||
. 'WHERE "type"=\'table\' ORDER BY "name"';
|
||||
break;
|
||||
default:
|
||||
throw new Exception(
|
||||
@@ -563,16 +677,19 @@ class Database extends AbstractData
|
||||
* @access private
|
||||
* @static
|
||||
* @param string $key
|
||||
* @throws PDOException
|
||||
* @return string
|
||||
*/
|
||||
private static function _getConfig($key)
|
||||
{
|
||||
$row = self::_select(
|
||||
'SELECT value FROM ' . self::_sanitizeIdentifier('config') .
|
||||
' WHERE id = ?', array($key), true
|
||||
);
|
||||
return $row['value'];
|
||||
try {
|
||||
$row = self::_select(
|
||||
'SELECT "value" FROM "' . self::_sanitizeIdentifier('config') .
|
||||
'" WHERE "id" = ?', array($key), true
|
||||
);
|
||||
} catch (PDOException $e) {
|
||||
return '';
|
||||
}
|
||||
return $row ? $row['value'] : '';
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -586,10 +703,14 @@ class Database extends AbstractData
|
||||
private static function _getPrimaryKeyClauses($key = 'dataid')
|
||||
{
|
||||
$main_key = $after_key = '';
|
||||
if (self::$_type === 'mysql') {
|
||||
$after_key = ", PRIMARY KEY ($key)";
|
||||
} else {
|
||||
$main_key = ' PRIMARY KEY';
|
||||
switch (self::$_type) {
|
||||
case 'mysql':
|
||||
case 'oci':
|
||||
$after_key = ", PRIMARY KEY (\"$key\")";
|
||||
break;
|
||||
default:
|
||||
$main_key = ' PRIMARY KEY';
|
||||
break;
|
||||
}
|
||||
return array($main_key, $after_key);
|
||||
}
|
||||
@@ -597,7 +718,7 @@ class Database extends AbstractData
|
||||
/**
|
||||
* get the data type, depending on the database driver
|
||||
*
|
||||
* PostgreSQL uses a different API for BLOBs then SQL, hence we use TEXT
|
||||
* PostgreSQL and OCI uses a different API for BLOBs then SQL, hence we use TEXT and CLOB
|
||||
*
|
||||
* @access private
|
||||
* @static
|
||||
@@ -605,13 +726,20 @@ class Database extends AbstractData
|
||||
*/
|
||||
private static function _getDataType()
|
||||
{
|
||||
return self::$_type === 'pgsql' ? 'TEXT' : 'BLOB';
|
||||
switch (self::$_type) {
|
||||
case 'oci':
|
||||
return 'CLOB';
|
||||
case 'pgsql':
|
||||
return 'TEXT';
|
||||
default:
|
||||
return 'BLOB';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* get the attachment type, depending on the database driver
|
||||
*
|
||||
* PostgreSQL uses a different API for BLOBs then SQL, hence we use TEXT
|
||||
* PostgreSQL and OCI use different APIs for BLOBs then SQL, hence we use TEXT and CLOB
|
||||
*
|
||||
* @access private
|
||||
* @static
|
||||
@@ -619,7 +747,33 @@ class Database extends AbstractData
|
||||
*/
|
||||
private static function _getAttachmentType()
|
||||
{
|
||||
return self::$_type === 'pgsql' ? 'TEXT' : 'MEDIUMBLOB';
|
||||
switch (self::$_type) {
|
||||
case 'oci':
|
||||
return 'CLOB';
|
||||
case 'pgsql':
|
||||
return 'TEXT';
|
||||
default:
|
||||
return 'MEDIUMBLOB';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* get the meta type, depending on the database driver
|
||||
*
|
||||
* OCI doesn't accept TEXT so it has to be VARCHAR2(4000)
|
||||
*
|
||||
* @access private
|
||||
* @static
|
||||
* @return string
|
||||
*/
|
||||
private static function _getMetaType()
|
||||
{
|
||||
switch (self::$_type) {
|
||||
case 'oci':
|
||||
return 'VARCHAR2(4000)';
|
||||
default:
|
||||
return 'TEXT';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -633,17 +787,18 @@ class Database extends AbstractData
|
||||
list($main_key, $after_key) = self::_getPrimaryKeyClauses();
|
||||
$dataType = self::_getDataType();
|
||||
$attachmentType = self::_getAttachmentType();
|
||||
$metaType = self::_getMetaType();
|
||||
self::$_db->exec(
|
||||
'CREATE TABLE ' . self::_sanitizeIdentifier('paste') . ' ( ' .
|
||||
"dataid CHAR(16) NOT NULL$main_key, " .
|
||||
"data $attachmentType, " .
|
||||
'postdate INT, ' .
|
||||
'expiredate INT, ' .
|
||||
'opendiscussion INT, ' .
|
||||
'burnafterreading INT, ' .
|
||||
'meta TEXT, ' .
|
||||
"attachment $attachmentType, " .
|
||||
"attachmentname $dataType$after_key );"
|
||||
'CREATE TABLE "' . self::_sanitizeIdentifier('paste') . '" ( ' .
|
||||
"\"dataid\" CHAR(16) NOT NULL$main_key, " .
|
||||
"\"data\" $attachmentType, " .
|
||||
'"postdate" INT, ' .
|
||||
'"expiredate" INT, ' .
|
||||
'"opendiscussion" INT, ' .
|
||||
'"burnafterreading" INT, ' .
|
||||
"\"meta\" $metaType, " .
|
||||
"\"attachment\" $attachmentType, " .
|
||||
"\"attachmentname\" $dataType$after_key )"
|
||||
);
|
||||
}
|
||||
|
||||
@@ -658,19 +813,35 @@ class Database extends AbstractData
|
||||
list($main_key, $after_key) = self::_getPrimaryKeyClauses();
|
||||
$dataType = self::_getDataType();
|
||||
self::$_db->exec(
|
||||
'CREATE TABLE ' . self::_sanitizeIdentifier('comment') . ' ( ' .
|
||||
"dataid CHAR(16) NOT NULL$main_key, " .
|
||||
'pasteid CHAR(16), ' .
|
||||
'parentid CHAR(16), ' .
|
||||
"data $dataType, " .
|
||||
"nickname $dataType, " .
|
||||
"vizhash $dataType, " .
|
||||
"postdate INT$after_key );"
|
||||
);
|
||||
self::$_db->exec(
|
||||
'CREATE INDEX IF NOT EXISTS comment_parent ON ' .
|
||||
self::_sanitizeIdentifier('comment') . '(pasteid);'
|
||||
'CREATE TABLE "' . self::_sanitizeIdentifier('comment') . '" ( ' .
|
||||
"\"dataid\" CHAR(16) NOT NULL$main_key, " .
|
||||
'"pasteid" CHAR(16), ' .
|
||||
'"parentid" CHAR(16), ' .
|
||||
"\"data\" $dataType, " .
|
||||
"\"nickname\" $dataType, " .
|
||||
"\"vizhash\" $dataType, " .
|
||||
"\"postdate\" INT$after_key )"
|
||||
);
|
||||
if (self::$_type === 'oci') {
|
||||
self::$_db->exec(
|
||||
'declare
|
||||
already_exists exception;
|
||||
columns_indexed exception;
|
||||
pragma exception_init( already_exists, -955 );
|
||||
pragma exception_init(columns_indexed, -1408);
|
||||
begin
|
||||
execute immediate \'create index "comment_parent" on "' . self::_sanitizeIdentifier('comment') . '" ("pasteid")\';
|
||||
exception
|
||||
when already_exists or columns_indexed then
|
||||
NULL;
|
||||
end;'
|
||||
);
|
||||
} else {
|
||||
self::$_db->exec(
|
||||
'CREATE INDEX IF NOT EXISTS "comment_parent" ON "' .
|
||||
self::_sanitizeIdentifier('comment') . '" ("pasteid")'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -682,17 +853,37 @@ class Database extends AbstractData
|
||||
private static function _createConfigTable()
|
||||
{
|
||||
list($main_key, $after_key) = self::_getPrimaryKeyClauses('id');
|
||||
$charType = self::$_type === 'oci' ? 'VARCHAR2(16)' : 'CHAR(16)';
|
||||
$textType = self::_getMetaType();
|
||||
self::$_db->exec(
|
||||
'CREATE TABLE ' . self::_sanitizeIdentifier('config') .
|
||||
" ( id CHAR(16) NOT NULL$main_key, value TEXT$after_key );"
|
||||
'CREATE TABLE "' . self::_sanitizeIdentifier('config') .
|
||||
"\" ( \"id\" $charType NOT NULL$main_key, \"value\" $textType$after_key )"
|
||||
);
|
||||
self::_exec(
|
||||
'INSERT INTO ' . self::_sanitizeIdentifier('config') .
|
||||
' VALUES(?,?)',
|
||||
'INSERT INTO "' . self::_sanitizeIdentifier('config') .
|
||||
'" VALUES(?,?)',
|
||||
array('VERSION', Controller::VERSION)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* sanitizes CLOB values used with OCI
|
||||
*
|
||||
* From: https://stackoverflow.com/questions/36200534/pdo-oci-into-a-clob-field
|
||||
*
|
||||
* @access public
|
||||
* @static
|
||||
* @param int|string|resource $value
|
||||
* @return int|string
|
||||
*/
|
||||
public static function _sanitizeClob($value)
|
||||
{
|
||||
if (is_resource($value)) {
|
||||
$value = stream_get_contents($value);
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* sanitizes identifiers
|
||||
*
|
||||
@@ -721,43 +912,46 @@ class Database extends AbstractData
|
||||
case '0.21':
|
||||
// create the meta column if necessary (pre 0.21 change)
|
||||
try {
|
||||
self::$_db->exec('SELECT meta FROM ' . self::_sanitizeIdentifier('paste') . ' LIMIT 1;');
|
||||
self::$_db->exec(
|
||||
'SELECT "meta" FROM "' . self::_sanitizeIdentifier('paste') . '" ' .
|
||||
(self::$_type === 'oci' ? 'FETCH NEXT 1 ROWS ONLY' : 'LIMIT 1')
|
||||
);
|
||||
} catch (PDOException $e) {
|
||||
self::$_db->exec('ALTER TABLE ' . self::_sanitizeIdentifier('paste') . ' ADD COLUMN meta TEXT;');
|
||||
self::$_db->exec('ALTER TABLE "' . self::_sanitizeIdentifier('paste') . '" ADD COLUMN "meta" TEXT');
|
||||
}
|
||||
// SQLite only allows one ALTER statement at a time...
|
||||
self::$_db->exec(
|
||||
'ALTER TABLE ' . self::_sanitizeIdentifier('paste') .
|
||||
" ADD COLUMN attachment $attachmentType;"
|
||||
'ALTER TABLE "' . self::_sanitizeIdentifier('paste') .
|
||||
"\" ADD COLUMN \"attachment\" $attachmentType"
|
||||
);
|
||||
self::$_db->exec(
|
||||
'ALTER TABLE ' . self::_sanitizeIdentifier('paste') . " ADD COLUMN attachmentname $dataType;"
|
||||
'ALTER TABLE "' . self::_sanitizeIdentifier('paste') . "\" ADD COLUMN \"attachmentname\" $dataType"
|
||||
);
|
||||
// SQLite doesn't support MODIFY, but it allows TEXT of similar
|
||||
// size as BLOB, so there is no need to change it there
|
||||
if (self::$_type !== 'sqlite') {
|
||||
self::$_db->exec(
|
||||
'ALTER TABLE ' . self::_sanitizeIdentifier('paste') .
|
||||
" ADD PRIMARY KEY (dataid), MODIFY COLUMN data $dataType;"
|
||||
'ALTER TABLE "' . self::_sanitizeIdentifier('paste') .
|
||||
"\" ADD PRIMARY KEY (\"dataid\"), MODIFY COLUMN \"data\" $dataType"
|
||||
);
|
||||
self::$_db->exec(
|
||||
'ALTER TABLE ' . self::_sanitizeIdentifier('comment') .
|
||||
" ADD PRIMARY KEY (dataid), MODIFY COLUMN data $dataType, " .
|
||||
"MODIFY COLUMN nickname $dataType, MODIFY COLUMN vizhash $dataType;"
|
||||
'ALTER TABLE "' . self::_sanitizeIdentifier('comment') .
|
||||
"\" ADD PRIMARY KEY (\"dataid\"), MODIFY COLUMN \"data\" $dataType, " .
|
||||
"MODIFY COLUMN \"nickname\" $dataType, MODIFY COLUMN \"vizhash\" $dataType"
|
||||
);
|
||||
} else {
|
||||
self::$_db->exec(
|
||||
'CREATE UNIQUE INDEX IF NOT EXISTS paste_dataid ON ' .
|
||||
self::_sanitizeIdentifier('paste') . '(dataid);'
|
||||
'CREATE UNIQUE INDEX IF NOT EXISTS "paste_dataid" ON "' .
|
||||
self::_sanitizeIdentifier('paste') . '" ("dataid")'
|
||||
);
|
||||
self::$_db->exec(
|
||||
'CREATE UNIQUE INDEX IF NOT EXISTS comment_dataid ON ' .
|
||||
self::_sanitizeIdentifier('comment') . '(dataid);'
|
||||
'CREATE UNIQUE INDEX IF NOT EXISTS "comment_dataid" ON "' .
|
||||
self::_sanitizeIdentifier('comment') . '" ("dataid")'
|
||||
);
|
||||
}
|
||||
self::$_db->exec(
|
||||
'CREATE INDEX IF NOT EXISTS comment_parent ON ' .
|
||||
self::_sanitizeIdentifier('comment') . '(pasteid);'
|
||||
'CREATE INDEX IF NOT EXISTS "comment_parent" ON "' .
|
||||
self::_sanitizeIdentifier('comment') . '" ("pasteid")'
|
||||
);
|
||||
// no break, continue with updates for 0.22 and later
|
||||
case '1.3':
|
||||
@@ -766,15 +960,15 @@ class Database extends AbstractData
|
||||
// to change it there
|
||||
if (self::$_type !== 'sqlite' && self::$_type !== 'pgsql') {
|
||||
self::$_db->exec(
|
||||
'ALTER TABLE ' . self::_sanitizeIdentifier('paste') .
|
||||
" MODIFY COLUMN data $attachmentType;"
|
||||
'ALTER TABLE "' . self::_sanitizeIdentifier('paste') .
|
||||
"\" MODIFY COLUMN \"data\" $attachmentType"
|
||||
);
|
||||
}
|
||||
// no break, continue with updates for all newer versions
|
||||
default:
|
||||
self::_exec(
|
||||
'UPDATE ' . self::_sanitizeIdentifier('config') .
|
||||
' SET value = ? WHERE id = ?',
|
||||
'UPDATE "' . self::_sanitizeIdentifier('config') .
|
||||
'" SET "value" = ? WHERE "id" = ?',
|
||||
array(Controller::VERSION, 'VERSION')
|
||||
);
|
||||
}
|
||||
|
||||
+227
-20
@@ -7,12 +7,13 @@
|
||||
* @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.3.5
|
||||
* @version 1.4.0
|
||||
*/
|
||||
|
||||
namespace PrivateBin\Data;
|
||||
|
||||
use PrivateBin\Persistence\DataStore;
|
||||
use Exception;
|
||||
use PrivateBin\Json;
|
||||
|
||||
/**
|
||||
* Filesystem
|
||||
@@ -21,6 +22,29 @@ use PrivateBin\Persistence\DataStore;
|
||||
*/
|
||||
class Filesystem extends AbstractData
|
||||
{
|
||||
/**
|
||||
* first line in paste or comment files, to protect their contents from browsing exposed data directories
|
||||
*
|
||||
* @const string
|
||||
*/
|
||||
const PROTECTION_LINE = '<?php http_response_code(403); /*';
|
||||
|
||||
/**
|
||||
* line in generated .htaccess files, to protect exposed directories from being browsable on apache web servers
|
||||
*
|
||||
* @const string
|
||||
*/
|
||||
const HTACCESS_LINE = 'Require all denied';
|
||||
|
||||
/**
|
||||
* path in which to persist something
|
||||
*
|
||||
* @access private
|
||||
* @static
|
||||
* @var string
|
||||
*/
|
||||
private static $_path = 'data';
|
||||
|
||||
/**
|
||||
* get instance of singleton
|
||||
*
|
||||
@@ -40,7 +64,7 @@ class Filesystem extends AbstractData
|
||||
is_array($options) &&
|
||||
array_key_exists('dir', $options)
|
||||
) {
|
||||
DataStore::setPath($options['dir']);
|
||||
self::$_path = $options['dir'];
|
||||
}
|
||||
return self::$_instance;
|
||||
}
|
||||
@@ -63,7 +87,7 @@ class Filesystem extends AbstractData
|
||||
if (!is_dir($storagedir)) {
|
||||
mkdir($storagedir, 0700, true);
|
||||
}
|
||||
return DataStore::store($file, $paste);
|
||||
return self::_store($file, $paste);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -75,12 +99,13 @@ class Filesystem extends AbstractData
|
||||
*/
|
||||
public function read($pasteid)
|
||||
{
|
||||
if (!$this->exists($pasteid)) {
|
||||
if (
|
||||
!$this->exists($pasteid) ||
|
||||
!$paste = self::_get(self::_dataid2path($pasteid) . $pasteid . '.php')
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
return self::upgradePreV1Format(
|
||||
DataStore::get(self::_dataid2path($pasteid) . $pasteid . '.php')
|
||||
);
|
||||
return self::upgradePreV1Format($paste);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -127,7 +152,7 @@ class Filesystem extends AbstractData
|
||||
$pastePath = $basePath . '.php';
|
||||
// convert to PHP protected files if needed
|
||||
if (is_readable($basePath)) {
|
||||
DataStore::prependRename($basePath, $pastePath);
|
||||
self::_prependRename($basePath, $pastePath);
|
||||
|
||||
// convert comments, too
|
||||
$discdir = self::_dataid2discussionpath($pasteid);
|
||||
@@ -136,7 +161,7 @@ class Filesystem extends AbstractData
|
||||
while (false !== ($filename = $dir->read())) {
|
||||
if (substr($filename, -4) !== '.php' && strlen($filename) >= 16) {
|
||||
$commentFilename = $discdir . $filename . '.php';
|
||||
DataStore::prependRename($discdir . $filename, $commentFilename);
|
||||
self::_prependRename($discdir . $filename, $commentFilename);
|
||||
}
|
||||
}
|
||||
$dir->close();
|
||||
@@ -165,7 +190,7 @@ class Filesystem extends AbstractData
|
||||
if (!is_dir($storagedir)) {
|
||||
mkdir($storagedir, 0700, true);
|
||||
}
|
||||
return DataStore::store($file, $comment);
|
||||
return self::_store($file, $comment);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -187,7 +212,7 @@ class Filesystem extends AbstractData
|
||||
// - commentid is the comment identifier itself.
|
||||
// - parentid is the comment this comment replies to (It can be pasteid)
|
||||
if (is_file($discdir . $filename)) {
|
||||
$comment = DataStore::get($discdir . $filename);
|
||||
$comment = self::_get($discdir . $filename);
|
||||
$items = explode('.', $filename);
|
||||
// Add some meta information not contained in file.
|
||||
$comment['id'] = $items[1];
|
||||
@@ -223,6 +248,97 @@ class Filesystem extends AbstractData
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Save a value.
|
||||
*
|
||||
* @access public
|
||||
* @param string $value
|
||||
* @param string $namespace
|
||||
* @param string $key
|
||||
* @return bool
|
||||
*/
|
||||
public function setValue($value, $namespace, $key = '')
|
||||
{
|
||||
switch ($namespace) {
|
||||
case 'purge_limiter':
|
||||
return self::_storeString(
|
||||
self::$_path . DIRECTORY_SEPARATOR . 'purge_limiter.php',
|
||||
'<?php' . PHP_EOL . '$GLOBALS[\'purge_limiter\'] = ' . $value . ';'
|
||||
);
|
||||
case 'salt':
|
||||
return self::_storeString(
|
||||
self::$_path . DIRECTORY_SEPARATOR . 'salt.php',
|
||||
'<?php # |' . $value . '|'
|
||||
);
|
||||
case 'traffic_limiter':
|
||||
self::$_last_cache[$key] = $value;
|
||||
return self::_storeString(
|
||||
self::$_path . DIRECTORY_SEPARATOR . 'traffic_limiter.php',
|
||||
'<?php' . PHP_EOL . '$GLOBALS[\'traffic_limiter\'] = ' . var_export(self::$_last_cache, true) . ';'
|
||||
);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load a value.
|
||||
*
|
||||
* @access public
|
||||
* @param string $namespace
|
||||
* @param string $key
|
||||
* @return string
|
||||
*/
|
||||
public function getValue($namespace, $key = '')
|
||||
{
|
||||
switch ($namespace) {
|
||||
case 'purge_limiter':
|
||||
$file = self::$_path . DIRECTORY_SEPARATOR . 'purge_limiter.php';
|
||||
if (is_readable($file)) {
|
||||
require $file;
|
||||
return $GLOBALS['purge_limiter'];
|
||||
}
|
||||
break;
|
||||
case 'salt':
|
||||
$file = self::$_path . DIRECTORY_SEPARATOR . 'salt.php';
|
||||
if (is_readable($file)) {
|
||||
$items = explode('|', file_get_contents($file));
|
||||
if (is_array($items) && count($items) == 3) {
|
||||
return $items[1];
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'traffic_limiter':
|
||||
$file = self::$_path . DIRECTORY_SEPARATOR . 'traffic_limiter.php';
|
||||
if (is_readable($file)) {
|
||||
require $file;
|
||||
self::$_last_cache = $GLOBALS['traffic_limiter'];
|
||||
if (array_key_exists($key, self::$_last_cache)) {
|
||||
return self::$_last_cache[$key];
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* get the data
|
||||
*
|
||||
* @access public
|
||||
* @static
|
||||
* @param string $filename
|
||||
* @return array|false $data
|
||||
*/
|
||||
private static function _get($filename)
|
||||
{
|
||||
return Json::decode(
|
||||
substr(
|
||||
file_get_contents($filename),
|
||||
strlen(self::PROTECTION_LINE . PHP_EOL)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns up to batch size number of paste ids that have expired
|
||||
*
|
||||
@@ -233,9 +349,8 @@ class Filesystem extends AbstractData
|
||||
protected function _getExpiredPastes($batchsize)
|
||||
{
|
||||
$pastes = array();
|
||||
$mainpath = DataStore::getPath();
|
||||
$firstLevel = array_filter(
|
||||
scandir($mainpath),
|
||||
scandir(self::$_path),
|
||||
'self::_isFirstLevelDir'
|
||||
);
|
||||
if (count($firstLevel) > 0) {
|
||||
@@ -243,7 +358,7 @@ class Filesystem extends AbstractData
|
||||
for ($i = 0, $max = $batchsize * 10; $i < $max; ++$i) {
|
||||
$firstKey = array_rand($firstLevel);
|
||||
$secondLevel = array_filter(
|
||||
scandir($mainpath . DIRECTORY_SEPARATOR . $firstLevel[$firstKey]),
|
||||
scandir(self::$_path . DIRECTORY_SEPARATOR . $firstLevel[$firstKey]),
|
||||
'self::_isSecondLevelDir'
|
||||
);
|
||||
|
||||
@@ -254,7 +369,7 @@ class Filesystem extends AbstractData
|
||||
}
|
||||
|
||||
$secondKey = array_rand($secondLevel);
|
||||
$path = $mainpath . DIRECTORY_SEPARATOR .
|
||||
$path = self::$_path . DIRECTORY_SEPARATOR .
|
||||
$firstLevel[$firstKey] . DIRECTORY_SEPARATOR .
|
||||
$secondLevel[$secondKey];
|
||||
if (!is_dir($path)) {
|
||||
@@ -314,10 +429,9 @@ class Filesystem extends AbstractData
|
||||
*/
|
||||
private static function _dataid2path($dataid)
|
||||
{
|
||||
return DataStore::getPath(
|
||||
return self::$_path . DIRECTORY_SEPARATOR .
|
||||
substr($dataid, 0, 2) . DIRECTORY_SEPARATOR .
|
||||
substr($dataid, 2, 2) . DIRECTORY_SEPARATOR
|
||||
);
|
||||
substr($dataid, 2, 2) . DIRECTORY_SEPARATOR;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -347,7 +461,7 @@ class Filesystem extends AbstractData
|
||||
private static function _isFirstLevelDir($element)
|
||||
{
|
||||
return self::_isSecondLevelDir($element) &&
|
||||
is_dir(DataStore::getPath($element));
|
||||
is_dir(self::$_path . DIRECTORY_SEPARATOR . $element);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -362,4 +476,97 @@ class Filesystem extends AbstractData
|
||||
{
|
||||
return (bool) preg_match('/^[a-f0-9]{2}$/', $element);
|
||||
}
|
||||
|
||||
/**
|
||||
* store the data
|
||||
*
|
||||
* @access public
|
||||
* @static
|
||||
* @param string $filename
|
||||
* @param array $data
|
||||
* @return bool
|
||||
*/
|
||||
private static function _store($filename, array $data)
|
||||
{
|
||||
try {
|
||||
return self::_storeString(
|
||||
$filename,
|
||||
self::PROTECTION_LINE . PHP_EOL . Json::encode($data)
|
||||
);
|
||||
} catch (Exception $e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* store a string
|
||||
*
|
||||
* @access public
|
||||
* @static
|
||||
* @param string $filename
|
||||
* @param string $data
|
||||
* @return bool
|
||||
*/
|
||||
private static function _storeString($filename, $data)
|
||||
{
|
||||
// Create storage directory if it does not exist.
|
||||
if (!is_dir(self::$_path)) {
|
||||
if (!@mkdir(self::$_path, 0700)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
$file = self::$_path . DIRECTORY_SEPARATOR . '.htaccess';
|
||||
if (!is_file($file)) {
|
||||
$writtenBytes = 0;
|
||||
if ($fileCreated = @touch($file)) {
|
||||
$writtenBytes = @file_put_contents(
|
||||
$file,
|
||||
self::HTACCESS_LINE . PHP_EOL,
|
||||
LOCK_EX
|
||||
);
|
||||
}
|
||||
if (
|
||||
$fileCreated === false ||
|
||||
$writtenBytes === false ||
|
||||
$writtenBytes < strlen(self::HTACCESS_LINE . PHP_EOL)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
$fileCreated = true;
|
||||
$writtenBytes = 0;
|
||||
if (!is_file($filename)) {
|
||||
$fileCreated = @touch($filename);
|
||||
}
|
||||
if ($fileCreated) {
|
||||
$writtenBytes = @file_put_contents($filename, $data, LOCK_EX);
|
||||
}
|
||||
if ($fileCreated === false || $writtenBytes === false || $writtenBytes < strlen($data)) {
|
||||
return false;
|
||||
}
|
||||
@chmod($filename, 0640); // protect file from access by other users on the host
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* rename a file, prepending the protection line at the beginning
|
||||
*
|
||||
* @access public
|
||||
* @static
|
||||
* @param string $srcFile
|
||||
* @param string $destFile
|
||||
* @return void
|
||||
*/
|
||||
private static function _prependRename($srcFile, $destFile)
|
||||
{
|
||||
// don't overwrite already converted file
|
||||
if (!is_readable($destFile)) {
|
||||
$handle = fopen($srcFile, 'r', false, stream_context_create());
|
||||
file_put_contents($destFile, self::PROTECTION_LINE . PHP_EOL);
|
||||
file_put_contents($destFile, $handle, FILE_APPEND);
|
||||
fclose($handle);
|
||||
}
|
||||
unlink($srcFile);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,346 @@
|
||||
<?php
|
||||
|
||||
namespace PrivateBin\Data;
|
||||
|
||||
use Exception;
|
||||
use Google\Cloud\Core\Exception\NotFoundException;
|
||||
use Google\Cloud\Storage\Bucket;
|
||||
use Google\Cloud\Storage\StorageClient;
|
||||
use PrivateBin\Json;
|
||||
|
||||
class GoogleCloudStorage extends AbstractData
|
||||
{
|
||||
/**
|
||||
* GCS client
|
||||
*
|
||||
* @access private
|
||||
* @static
|
||||
* @var StorageClient
|
||||
*/
|
||||
private static $_client = null;
|
||||
|
||||
/**
|
||||
* GCS bucket
|
||||
*
|
||||
* @access private
|
||||
* @static
|
||||
* @var Bucket
|
||||
*/
|
||||
private static $_bucket = null;
|
||||
|
||||
/**
|
||||
* object prefix
|
||||
*
|
||||
* @access private
|
||||
* @static
|
||||
* @var string
|
||||
*/
|
||||
private static $_prefix = 'pastes';
|
||||
|
||||
/**
|
||||
* returns a Google Cloud Storage data backend.
|
||||
*
|
||||
* @access public
|
||||
* @static
|
||||
* @param array $options
|
||||
* @return GoogleCloudStorage
|
||||
*/
|
||||
public static function getInstance(array $options)
|
||||
{
|
||||
// if needed initialize the singleton
|
||||
if (!(self::$_instance instanceof self)) {
|
||||
self::$_instance = new self;
|
||||
}
|
||||
|
||||
$bucket = null;
|
||||
if (getenv('PRIVATEBIN_GCS_BUCKET')) {
|
||||
$bucket = getenv('PRIVATEBIN_GCS_BUCKET');
|
||||
}
|
||||
if (is_array($options) && array_key_exists('bucket', $options)) {
|
||||
$bucket = $options['bucket'];
|
||||
}
|
||||
if (is_array($options) && array_key_exists('prefix', $options)) {
|
||||
self::$_prefix = $options['prefix'];
|
||||
}
|
||||
|
||||
if (empty(self::$_client)) {
|
||||
self::$_client = class_exists('StorageClientStub', false) ?
|
||||
new \StorageClientStub(array()) :
|
||||
new StorageClient(array('suppressKeyFileNotice' => true));
|
||||
}
|
||||
self::$_bucket = self::$_client->bucket($bucket);
|
||||
|
||||
return self::$_instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* returns the google storage object key for $pasteid in self::$_bucket.
|
||||
*
|
||||
* @access private
|
||||
* @param $pasteid string to get the key for
|
||||
* @return string
|
||||
*/
|
||||
private function _getKey($pasteid)
|
||||
{
|
||||
if (self::$_prefix != '') {
|
||||
return self::$_prefix . '/' . $pasteid;
|
||||
}
|
||||
return $pasteid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Uploads the payload in the self::$_bucket under the specified key.
|
||||
* The entire payload is stored as a JSON document. The metadata is replicated
|
||||
* as the GCS object's metadata except for the fields attachment, attachmentname
|
||||
* and salt.
|
||||
*
|
||||
* @param $key string to store the payload under
|
||||
* @param $payload array to store
|
||||
* @return bool true if successful, otherwise false.
|
||||
*/
|
||||
private function _upload($key, $payload)
|
||||
{
|
||||
$metadata = array_key_exists('meta', $payload) ? $payload['meta'] : array();
|
||||
unset($metadata['attachment'], $metadata['attachmentname'], $metadata['salt']);
|
||||
foreach ($metadata as $k => $v) {
|
||||
$metadata[$k] = strval($v);
|
||||
}
|
||||
try {
|
||||
self::$_bucket->upload(Json::encode($payload), array(
|
||||
'name' => $key,
|
||||
'chunkSize' => 262144,
|
||||
'predefinedAcl' => 'private',
|
||||
'metadata' => array(
|
||||
'content-type' => 'application/json',
|
||||
'metadata' => $metadata,
|
||||
),
|
||||
));
|
||||
} catch (Exception $e) {
|
||||
error_log('failed to upload ' . $key . ' to ' . self::$_bucket->name() . ', ' .
|
||||
trim(preg_replace('/\s\s+/', ' ', $e->getMessage())));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function create($pasteid, array $paste)
|
||||
{
|
||||
if ($this->exists($pasteid)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->_upload($this->_getKey($pasteid), $paste);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function read($pasteid)
|
||||
{
|
||||
try {
|
||||
$o = self::$_bucket->object($this->_getKey($pasteid));
|
||||
$data = $o->downloadAsString();
|
||||
return Json::decode($data);
|
||||
} catch (NotFoundException $e) {
|
||||
return false;
|
||||
} catch (Exception $e) {
|
||||
error_log('failed to read ' . $pasteid . ' from ' . self::$_bucket->name() . ', ' .
|
||||
trim(preg_replace('/\s\s+/', ' ', $e->getMessage())));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function delete($pasteid)
|
||||
{
|
||||
$name = $this->_getKey($pasteid);
|
||||
|
||||
try {
|
||||
foreach (self::$_bucket->objects(array('prefix' => $name . '/discussion/')) as $comment) {
|
||||
try {
|
||||
self::$_bucket->object($comment->name())->delete();
|
||||
} catch (NotFoundException $e) {
|
||||
// ignore if already deleted.
|
||||
}
|
||||
}
|
||||
} catch (NotFoundException $e) {
|
||||
// there are no discussions associated with the paste
|
||||
}
|
||||
|
||||
try {
|
||||
self::$_bucket->object($name)->delete();
|
||||
} catch (NotFoundException $e) {
|
||||
// ignore if already deleted
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function exists($pasteid)
|
||||
{
|
||||
$o = self::$_bucket->object($this->_getKey($pasteid));
|
||||
return $o->exists();
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function createComment($pasteid, $parentid, $commentid, array $comment)
|
||||
{
|
||||
if ($this->existsComment($pasteid, $parentid, $commentid)) {
|
||||
return false;
|
||||
}
|
||||
$key = $this->_getKey($pasteid) . '/discussion/' . $parentid . '/' . $commentid;
|
||||
return $this->_upload($key, $comment);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function readComments($pasteid)
|
||||
{
|
||||
$comments = array();
|
||||
$prefix = $this->_getKey($pasteid) . '/discussion/';
|
||||
try {
|
||||
foreach (self::$_bucket->objects(array('prefix' => $prefix)) as $key) {
|
||||
$comment = JSON::decode(self::$_bucket->object($key->name())->downloadAsString());
|
||||
$comment['id'] = basename($key->name());
|
||||
$slot = $this->getOpenSlot($comments, (int) $comment['meta']['created']);
|
||||
$comments[$slot] = $comment;
|
||||
}
|
||||
} catch (NotFoundException $e) {
|
||||
// no comments found
|
||||
}
|
||||
return $comments;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function existsComment($pasteid, $parentid, $commentid)
|
||||
{
|
||||
$name = $this->_getKey($pasteid) . '/discussion/' . $parentid . '/' . $commentid;
|
||||
$o = self::$_bucket->object($name);
|
||||
return $o->exists();
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function purgeValues($namespace, $time)
|
||||
{
|
||||
$path = 'config/' . $namespace;
|
||||
try {
|
||||
foreach (self::$_bucket->objects(array('prefix' => $path)) as $object) {
|
||||
$name = $object->name();
|
||||
if (strlen($name) > strlen($path) && substr($name, strlen($path), 1) !== '/') {
|
||||
continue;
|
||||
}
|
||||
$info = $object->info();
|
||||
if (key_exists('metadata', $info) && key_exists('value', $info['metadata'])) {
|
||||
$value = $info['metadata']['value'];
|
||||
if (is_numeric($value) && intval($value) < $time) {
|
||||
try {
|
||||
$object->delete();
|
||||
} catch (NotFoundException $e) {
|
||||
// deleted by another instance.
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (NotFoundException $e) {
|
||||
// no objects in the bucket yet
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* For GoogleCloudStorage, the value will also be stored in the metadata for the
|
||||
* namespaces traffic_limiter and purge_limiter.
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function setValue($value, $namespace, $key = '')
|
||||
{
|
||||
if ($key === '') {
|
||||
$key = 'config/' . $namespace;
|
||||
} else {
|
||||
$key = 'config/' . $namespace . '/' . $key;
|
||||
}
|
||||
|
||||
$metadata = array('namespace' => $namespace);
|
||||
if ($namespace != 'salt') {
|
||||
$metadata['value'] = strval($value);
|
||||
}
|
||||
try {
|
||||
self::$_bucket->upload($value, array(
|
||||
'name' => $key,
|
||||
'chunkSize' => 262144,
|
||||
'predefinedAcl' => 'private',
|
||||
'metadata' => array(
|
||||
'content-type' => 'application/json',
|
||||
'metadata' => $metadata,
|
||||
),
|
||||
));
|
||||
} catch (Exception $e) {
|
||||
error_log('failed to set key ' . $key . ' to ' . self::$_bucket->name() . ', ' .
|
||||
trim(preg_replace('/\s\s+/', ' ', $e->getMessage())));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function getValue($namespace, $key = '')
|
||||
{
|
||||
if ($key === '') {
|
||||
$key = 'config/' . $namespace;
|
||||
} else {
|
||||
$key = 'config/' . $namespace . '/' . $key;
|
||||
}
|
||||
try {
|
||||
$o = self::$_bucket->object($key);
|
||||
return $o->downloadAsString();
|
||||
} catch (NotFoundException $e) {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
protected function _getExpiredPastes($batchsize)
|
||||
{
|
||||
$expired = array();
|
||||
|
||||
$now = time();
|
||||
$prefix = self::$_prefix;
|
||||
if ($prefix != '') {
|
||||
$prefix .= '/';
|
||||
}
|
||||
try {
|
||||
foreach (self::$_bucket->objects(array('prefix' => $prefix)) as $object) {
|
||||
$metadata = $object->info()['metadata'];
|
||||
if ($metadata != null && array_key_exists('expire_date', $metadata)) {
|
||||
$expire_at = intval($metadata['expire_date']);
|
||||
if ($expire_at != 0 && $expire_at < $now) {
|
||||
array_push($expired, basename($object->name()));
|
||||
}
|
||||
}
|
||||
|
||||
if (count($expired) > $batchsize) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (NotFoundException $e) {
|
||||
// no objects in the bucket yet
|
||||
}
|
||||
return $expired;
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -7,7 +7,7 @@
|
||||
* @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.3.5
|
||||
* @version 1.4.0
|
||||
*/
|
||||
|
||||
namespace PrivateBin;
|
||||
|
||||
+6
-1
@@ -7,7 +7,7 @@
|
||||
* @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.3.5
|
||||
* @version 1.4.0
|
||||
*/
|
||||
|
||||
namespace PrivateBin;
|
||||
@@ -52,6 +52,11 @@ class FormatV2
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure adata is an array.
|
||||
if (!is_array($message['adata'])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$cipherParams = $isComment ? $message['adata'] : $message['adata'][0];
|
||||
|
||||
// Make sure some fields are base64 data:
|
||||
|
||||
+6
-4
@@ -7,7 +7,7 @@
|
||||
* @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.3.5
|
||||
* @version 1.4.0
|
||||
*/
|
||||
|
||||
namespace PrivateBin;
|
||||
@@ -195,7 +195,7 @@ class I18n
|
||||
if (count(self::$_availableLanguages) == 0) {
|
||||
$i18n = dir(self::_getPath());
|
||||
while (false !== ($file = $i18n->read())) {
|
||||
if (preg_match('/^([a-z]{2}).json$/', $file, $match) === 1) {
|
||||
if (preg_match('/^([a-z]{2,3}).json$/', $file, $match) === 1) {
|
||||
self::$_availableLanguages[] = $match[1];
|
||||
}
|
||||
}
|
||||
@@ -305,7 +305,7 @@ class I18n
|
||||
/**
|
||||
* determines the plural form to use based on current language and given number
|
||||
*
|
||||
* From: http://localization-guide.readthedocs.org/en/latest/l10n/pluralforms.html
|
||||
* From: https://docs.translatehouse.org/projects/localization-guide/en/latest/l10n/pluralforms.html
|
||||
*
|
||||
* @access protected
|
||||
* @static
|
||||
@@ -317,6 +317,7 @@ class I18n
|
||||
switch (self::$_language) {
|
||||
case 'cs':
|
||||
return $n == 1 ? 0 : ($n >= 2 && $n <= 4 ? 1 : 2);
|
||||
case 'co':
|
||||
case 'fr':
|
||||
case 'oc':
|
||||
case 'zh':
|
||||
@@ -324,6 +325,7 @@ class I18n
|
||||
case 'he':
|
||||
return $n === 1 ? 0 : ($n === 2 ? 1 : (($n < 0 || $n > 10) && ($n % 10 === 0) ? 2 : 3));
|
||||
case 'id':
|
||||
case 'jbo':
|
||||
return 0;
|
||||
case 'lt':
|
||||
return $n % 10 === 1 && $n % 100 !== 11 ? 0 : (($n % 10 >= 2 && $n % 100 < 10 || $n % 100 >= 20) ? 1 : 2);
|
||||
@@ -334,7 +336,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));
|
||||
// bg, ca, de, en, es, hu, it, nl, no, pt
|
||||
// bg, ca, de, en, es, et, fi, hu, it, nl, no, pt
|
||||
default:
|
||||
return $n != 1 ? 1 : 0;
|
||||
}
|
||||
|
||||
+4
-4
@@ -7,7 +7,7 @@
|
||||
* @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.3.5
|
||||
* @version 1.4.0
|
||||
*/
|
||||
|
||||
namespace PrivateBin;
|
||||
@@ -44,13 +44,13 @@ class Json
|
||||
* @static
|
||||
* @param string $input
|
||||
* @throws Exception
|
||||
* @return array
|
||||
* @return mixed
|
||||
*/
|
||||
public static function decode($input)
|
||||
{
|
||||
$array = json_decode($input, true);
|
||||
$output = json_decode($input, true);
|
||||
self::_detectError();
|
||||
return $array;
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+5
-4
@@ -7,7 +7,7 @@
|
||||
* @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.3.5
|
||||
* @version 1.4.0
|
||||
*/
|
||||
|
||||
namespace PrivateBin;
|
||||
@@ -54,7 +54,7 @@ class Model
|
||||
*/
|
||||
public function getPaste($pasteId = null)
|
||||
{
|
||||
$paste = new Paste($this->_conf, $this->_getStore());
|
||||
$paste = new Paste($this->_conf, $this->getStore());
|
||||
if ($pasteId !== null) {
|
||||
$paste->setId($pasteId);
|
||||
}
|
||||
@@ -67,8 +67,9 @@ class Model
|
||||
public function purge()
|
||||
{
|
||||
PurgeLimiter::setConfiguration($this->_conf);
|
||||
PurgeLimiter::setStore($this->getStore());
|
||||
if (PurgeLimiter::canPurge()) {
|
||||
$this->_getStore()->purge($this->_conf->getKey('batchsize', 'purge'));
|
||||
$this->getStore()->purge($this->_conf->getKey('batchsize', 'purge'));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,7 +78,7 @@ class Model
|
||||
*
|
||||
* @return Data\AbstractData
|
||||
*/
|
||||
private function _getStore()
|
||||
public function getStore()
|
||||
{
|
||||
if ($this->_store === null) {
|
||||
$this->_store = forward_static_call(
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
* @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.3.5
|
||||
* @version 1.4.0
|
||||
*/
|
||||
|
||||
namespace PrivateBin\Model;
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
* @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.3.5
|
||||
* @version 1.4.0
|
||||
*/
|
||||
|
||||
namespace PrivateBin\Model;
|
||||
|
||||
+2
-2
@@ -7,7 +7,7 @@
|
||||
* @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.3.5
|
||||
* @version 1.4.0
|
||||
*/
|
||||
|
||||
namespace PrivateBin\Model;
|
||||
@@ -93,7 +93,7 @@ class Paste extends AbstractModel
|
||||
}
|
||||
|
||||
$this->_data['meta']['created'] = time();
|
||||
$this->_data['meta']['salt'] = serversalt::generate();
|
||||
$this->_data['meta']['salt'] = ServerSalt::generate();
|
||||
|
||||
// store paste
|
||||
if (
|
||||
|
||||
@@ -7,12 +7,12 @@
|
||||
* @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.3.5
|
||||
* @version 1.4.0
|
||||
*/
|
||||
|
||||
namespace PrivateBin\Persistence;
|
||||
|
||||
use Exception;
|
||||
use PrivateBin\Data\AbstractData;
|
||||
|
||||
/**
|
||||
* AbstractPersistence
|
||||
@@ -22,104 +22,23 @@ use Exception;
|
||||
abstract class AbstractPersistence
|
||||
{
|
||||
/**
|
||||
* path in which to persist something
|
||||
* data storage to use to persist something
|
||||
*
|
||||
* @access private
|
||||
* @static
|
||||
* @var string
|
||||
* @var AbstractData
|
||||
*/
|
||||
private static $_path = 'data';
|
||||
protected static $_store;
|
||||
|
||||
/**
|
||||
* set the path
|
||||
*
|
||||
* @access public
|
||||
* @static
|
||||
* @param string $path
|
||||
* @param AbstractData $store
|
||||
*/
|
||||
public static function setPath($path)
|
||||
public static function setStore(AbstractData $store)
|
||||
{
|
||||
self::$_path = $path;
|
||||
}
|
||||
|
||||
/**
|
||||
* get the path
|
||||
*
|
||||
* @access public
|
||||
* @static
|
||||
* @param string $filename
|
||||
* @return string
|
||||
*/
|
||||
public static function getPath($filename = null)
|
||||
{
|
||||
if (strlen($filename)) {
|
||||
return self::$_path . DIRECTORY_SEPARATOR . $filename;
|
||||
} else {
|
||||
return self::$_path;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* checks if the file exists
|
||||
*
|
||||
* @access protected
|
||||
* @static
|
||||
* @param string $filename
|
||||
* @return bool
|
||||
*/
|
||||
protected static function _exists($filename)
|
||||
{
|
||||
self::_initialize();
|
||||
return is_file(self::$_path . DIRECTORY_SEPARATOR . $filename);
|
||||
}
|
||||
|
||||
/**
|
||||
* prepares path for storage
|
||||
*
|
||||
* @access protected
|
||||
* @static
|
||||
* @throws Exception
|
||||
*/
|
||||
protected static function _initialize()
|
||||
{
|
||||
// Create storage directory if it does not exist.
|
||||
if (!is_dir(self::$_path)) {
|
||||
if (!@mkdir(self::$_path, 0700)) {
|
||||
throw new Exception('unable to create directory ' . self::$_path, 10);
|
||||
}
|
||||
}
|
||||
$file = self::$_path . DIRECTORY_SEPARATOR . '.htaccess';
|
||||
if (!is_file($file)) {
|
||||
$writtenBytes = @file_put_contents(
|
||||
$file,
|
||||
'Require all denied' . PHP_EOL,
|
||||
LOCK_EX
|
||||
);
|
||||
if ($writtenBytes === false || $writtenBytes < 19) {
|
||||
throw new Exception('unable to write to file ' . $file, 11);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* store the data
|
||||
*
|
||||
* @access protected
|
||||
* @static
|
||||
* @param string $filename
|
||||
* @param string $data
|
||||
* @throws Exception
|
||||
* @return string
|
||||
*/
|
||||
protected static function _store($filename, $data)
|
||||
{
|
||||
self::_initialize();
|
||||
$file = self::$_path . DIRECTORY_SEPARATOR . $filename;
|
||||
$writtenBytes = @file_put_contents($file, $data, LOCK_EX);
|
||||
if ($writtenBytes === false || $writtenBytes < strlen($data)) {
|
||||
throw new Exception('unable to write to file ' . $file, 13);
|
||||
}
|
||||
@chmod($file, 0640); // protect file access
|
||||
return $file;
|
||||
self::$_store = $store;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,97 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* PrivateBin
|
||||
*
|
||||
* a zero-knowledge paste bin
|
||||
*
|
||||
* @link https://github.com/PrivateBin/PrivateBin
|
||||
* @copyright 2012 Sébastien SAUVAGE (sebsauvage.net)
|
||||
* @license https://www.opensource.org/licenses/zlib-license.php The zlib/libpng License
|
||||
* @version 1.3.5
|
||||
*/
|
||||
|
||||
namespace PrivateBin\Persistence;
|
||||
|
||||
use Exception;
|
||||
use PrivateBin\Json;
|
||||
|
||||
/**
|
||||
* DataStore
|
||||
*
|
||||
* Handles data storage for Data\Filesystem.
|
||||
*/
|
||||
class DataStore extends AbstractPersistence
|
||||
{
|
||||
/**
|
||||
* first line in file, to protect its contents
|
||||
*
|
||||
* @const string
|
||||
*/
|
||||
const PROTECTION_LINE = '<?php http_response_code(403); /*';
|
||||
|
||||
/**
|
||||
* 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,
|
||||
self::PROTECTION_LINE . PHP_EOL . Json::encode($data)
|
||||
);
|
||||
return true;
|
||||
} catch (Exception $e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* get the data
|
||||
*
|
||||
* @access public
|
||||
* @static
|
||||
* @param string $filename
|
||||
* @return array|false $data
|
||||
*/
|
||||
public static function get($filename)
|
||||
{
|
||||
return Json::decode(
|
||||
substr(
|
||||
file_get_contents($filename),
|
||||
strlen(self::PROTECTION_LINE . PHP_EOL)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* rename a file, prepending the protection line at the beginning
|
||||
*
|
||||
* @access public
|
||||
* @static
|
||||
* @param string $srcFile
|
||||
* @param string $destFile
|
||||
* @param string $prefix (optional)
|
||||
* @return void
|
||||
*/
|
||||
public static function prependRename($srcFile, $destFile, $prefix = '')
|
||||
{
|
||||
// don't overwrite already converted file
|
||||
if (!is_readable($destFile)) {
|
||||
$handle = fopen($srcFile, 'r', false, stream_context_create());
|
||||
file_put_contents($destFile, $prefix . self::PROTECTION_LINE . PHP_EOL);
|
||||
file_put_contents($destFile, $handle, FILE_APPEND);
|
||||
fclose($handle);
|
||||
}
|
||||
unlink($srcFile);
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,7 @@
|
||||
* @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.3.5
|
||||
* @version 1.4.0
|
||||
*/
|
||||
|
||||
namespace PrivateBin\Persistence;
|
||||
@@ -52,7 +52,6 @@ class PurgeLimiter extends AbstractPersistence
|
||||
public static function setConfiguration(Configuration $conf)
|
||||
{
|
||||
self::setLimit($conf->getKey('limit', 'purge'));
|
||||
self::setPath($conf->getKey('dir', 'purge'));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -60,7 +59,6 @@ class PurgeLimiter extends AbstractPersistence
|
||||
*
|
||||
* @access public
|
||||
* @static
|
||||
* @throws \Exception
|
||||
* @return bool
|
||||
*/
|
||||
public static function canPurge()
|
||||
@@ -71,17 +69,14 @@ class PurgeLimiter extends AbstractPersistence
|
||||
}
|
||||
|
||||
$now = time();
|
||||
$file = 'purge_limiter.php';
|
||||
if (self::_exists($file)) {
|
||||
require self::getPath($file);
|
||||
$pl = $GLOBALS['purge_limiter'];
|
||||
if ($pl + self::$_limit >= $now) {
|
||||
return false;
|
||||
}
|
||||
$pl = (int) self::$_store->getValue('purge_limiter');
|
||||
if ($pl + self::$_limit >= $now) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$content = '<?php' . PHP_EOL . '$GLOBALS[\'purge_limiter\'] = ' . $now . ';';
|
||||
self::_store($file, $content);
|
||||
return true;
|
||||
$hasStored = self::$_store->setValue((string) $now, 'purge_limiter');
|
||||
if (!$hasStored) {
|
||||
error_log('failed to store the purge limiter, skipping purge cycle to avoid getting stuck in a purge loop');
|
||||
}
|
||||
return $hasStored;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,12 +7,12 @@
|
||||
* @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.3.5
|
||||
* @version 1.4.0
|
||||
*/
|
||||
|
||||
namespace PrivateBin\Persistence;
|
||||
|
||||
use Exception;
|
||||
use PrivateBin\Data\AbstractData;
|
||||
|
||||
/**
|
||||
* ServerSalt
|
||||
@@ -26,15 +26,6 @@ use Exception;
|
||||
*/
|
||||
class ServerSalt extends AbstractPersistence
|
||||
{
|
||||
/**
|
||||
* file where salt is saved to
|
||||
*
|
||||
* @access private
|
||||
* @static
|
||||
* @var string
|
||||
*/
|
||||
private static $_file = 'salt.php';
|
||||
|
||||
/**
|
||||
* generated salt
|
||||
*
|
||||
@@ -53,8 +44,7 @@ class ServerSalt extends AbstractPersistence
|
||||
*/
|
||||
public static function generate()
|
||||
{
|
||||
$randomSalt = bin2hex(random_bytes(256));
|
||||
return $randomSalt;
|
||||
return bin2hex(random_bytes(256));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -62,7 +52,6 @@ class ServerSalt extends AbstractPersistence
|
||||
*
|
||||
* @access public
|
||||
* @static
|
||||
* @throws Exception
|
||||
* @return string
|
||||
*/
|
||||
public static function get()
|
||||
@@ -71,20 +60,14 @@ class ServerSalt extends AbstractPersistence
|
||||
return self::$_salt;
|
||||
}
|
||||
|
||||
if (self::_exists(self::$_file)) {
|
||||
if (is_readable(self::getPath(self::$_file))) {
|
||||
$items = explode('|', file_get_contents(self::getPath(self::$_file)));
|
||||
}
|
||||
if (!isset($items) || !is_array($items) || count($items) != 3) {
|
||||
throw new Exception('unable to read file ' . self::getPath(self::$_file), 20);
|
||||
}
|
||||
self::$_salt = $items[1];
|
||||
$salt = self::$_store->getValue('salt');
|
||||
if ($salt) {
|
||||
self::$_salt = $salt;
|
||||
} else {
|
||||
self::$_salt = self::generate();
|
||||
self::_store(
|
||||
self::$_file,
|
||||
'<?php # |' . self::$_salt . '|'
|
||||
);
|
||||
if (!self::$_store->setValue(self::$_salt, 'salt')) {
|
||||
error_log('failed to store the server salt, delete tokens, traffic limiter and user icons won\'t work');
|
||||
}
|
||||
}
|
||||
return self::$_salt;
|
||||
}
|
||||
@@ -94,11 +77,11 @@ class ServerSalt extends AbstractPersistence
|
||||
*
|
||||
* @access public
|
||||
* @static
|
||||
* @param string $path
|
||||
* @param AbstractData $store
|
||||
*/
|
||||
public static function setPath($path)
|
||||
public static function setStore(AbstractData $store)
|
||||
{
|
||||
self::$_salt = '';
|
||||
parent::setPath($path);
|
||||
parent::setStore($store);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* PrivateBin
|
||||
*
|
||||
@@ -7,12 +8,16 @@
|
||||
* @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.3.5
|
||||
* @version 1.4.0
|
||||
*/
|
||||
|
||||
namespace PrivateBin\Persistence;
|
||||
|
||||
use Exception;
|
||||
use IPLib\Factory;
|
||||
use IPLib\ParseStringFlag;
|
||||
use PrivateBin\Configuration;
|
||||
use PrivateBin\I18n;
|
||||
|
||||
/**
|
||||
* TrafficLimiter
|
||||
@@ -22,13 +27,22 @@ use PrivateBin\Configuration;
|
||||
class TrafficLimiter extends AbstractPersistence
|
||||
{
|
||||
/**
|
||||
* time limit in seconds, defaults to 10s
|
||||
* listed IPs are the only ones allowed to create, defaults to null
|
||||
*
|
||||
* @access private
|
||||
* @static
|
||||
* @var int
|
||||
* @var string|null
|
||||
*/
|
||||
private static $_limit = 10;
|
||||
private static $_creators = null;
|
||||
|
||||
/**
|
||||
* listed IPs are exempted from limits, defaults to null
|
||||
*
|
||||
* @access private
|
||||
* @static
|
||||
* @var string|null
|
||||
*/
|
||||
private static $_exempted = null;
|
||||
|
||||
/**
|
||||
* key to fetch IP address
|
||||
@@ -40,16 +54,13 @@ class TrafficLimiter extends AbstractPersistence
|
||||
private static $_ipKey = 'REMOTE_ADDR';
|
||||
|
||||
/**
|
||||
* set the time limit in seconds
|
||||
* time limit in seconds, defaults to 10s
|
||||
*
|
||||
* @access public
|
||||
* @access private
|
||||
* @static
|
||||
* @param int $limit
|
||||
* @var int
|
||||
*/
|
||||
public static function setLimit($limit)
|
||||
{
|
||||
self::$_limit = $limit;
|
||||
}
|
||||
private static $_limit = 10;
|
||||
|
||||
/**
|
||||
* set configuration options of the traffic limiter
|
||||
@@ -60,9 +71,11 @@ class TrafficLimiter extends AbstractPersistence
|
||||
*/
|
||||
public static function setConfiguration(Configuration $conf)
|
||||
{
|
||||
self::setCreators($conf->getKey('creators', 'traffic'));
|
||||
self::setExempted($conf->getKey('exempted', 'traffic'));
|
||||
self::setLimit($conf->getKey('limit', 'traffic'));
|
||||
self::setPath($conf->getKey('dir', 'traffic'));
|
||||
if (($option = $conf->getKey('header', 'traffic')) !== null) {
|
||||
|
||||
if (($option = $conf->getKey('header', 'traffic')) !== '') {
|
||||
$httpHeader = 'HTTP_' . $option;
|
||||
if (array_key_exists($httpHeader, $_SERVER) && !empty($_SERVER[$httpHeader])) {
|
||||
self::$_ipKey = $httpHeader;
|
||||
@@ -70,6 +83,42 @@ class TrafficLimiter extends AbstractPersistence
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* set a list of creator IP(-ranges) as string
|
||||
*
|
||||
* @access public
|
||||
* @static
|
||||
* @param string $creators
|
||||
*/
|
||||
public static function setCreators($creators)
|
||||
{
|
||||
self::$_creators = $creators;
|
||||
}
|
||||
|
||||
/**
|
||||
* set a list of exempted IP(-ranges) as string
|
||||
*
|
||||
* @access public
|
||||
* @static
|
||||
* @param string $exempted
|
||||
*/
|
||||
public static function setExempted($exempted)
|
||||
{
|
||||
self::$_exempted = $exempted;
|
||||
}
|
||||
|
||||
/**
|
||||
* set the time limit in seconds
|
||||
*
|
||||
* @access public
|
||||
* @static
|
||||
* @param int $limit
|
||||
*/
|
||||
public static function setLimit($limit)
|
||||
{
|
||||
self::$_limit = $limit;
|
||||
}
|
||||
|
||||
/**
|
||||
* get a HMAC of the current visitors IP address
|
||||
*
|
||||
@@ -84,51 +133,93 @@ class TrafficLimiter extends AbstractPersistence
|
||||
}
|
||||
|
||||
/**
|
||||
* traffic limiter
|
||||
* validate $_ipKey against configured ipranges. If matched we will ignore the ip
|
||||
*
|
||||
* Make sure the IP address makes at most 1 request every 10 seconds.
|
||||
* @access private
|
||||
* @static
|
||||
* @param string $ipRange
|
||||
* @return bool
|
||||
*/
|
||||
private static function matchIp($ipRange = null)
|
||||
{
|
||||
if (is_string($ipRange)) {
|
||||
$ipRange = trim($ipRange);
|
||||
}
|
||||
$address = Factory::parseAddressString($_SERVER[self::$_ipKey]);
|
||||
$range = Factory::parseRangeString(
|
||||
$ipRange,
|
||||
ParseStringFlag::IPV4_MAYBE_NON_DECIMAL | ParseStringFlag::IPV4SUBNET_MAYBE_COMPACT | ParseStringFlag::IPV4ADDRESS_MAYBE_NON_QUAD_DOTTED
|
||||
);
|
||||
|
||||
// address could not be parsed, we might not be in IP space and try a string comparison instead
|
||||
if (is_null($address)) {
|
||||
return $_SERVER[self::$_ipKey] === $ipRange;
|
||||
}
|
||||
// range could not be parsed, possibly an invalid ip range given in config
|
||||
if (is_null($range)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $address->matches($range);
|
||||
}
|
||||
|
||||
/**
|
||||
* make sure the IP address is allowed to perfom a request
|
||||
*
|
||||
* @access public
|
||||
* @static
|
||||
* @throws \Exception
|
||||
* @return bool
|
||||
* @throws Exception
|
||||
* @return true
|
||||
*/
|
||||
public static function canPass()
|
||||
{
|
||||
// if creators are defined, the traffic limiter will only allow creation
|
||||
// for these, with no limits, and skip any other rules
|
||||
if (!empty(self::$_creators)) {
|
||||
$creatorIps = explode(',', self::$_creators);
|
||||
foreach ($creatorIps as $ipRange) {
|
||||
if (self::matchIp($ipRange) === true) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
throw new Exception(I18n::_('Your IP is not authorized to create pastes.'));
|
||||
}
|
||||
|
||||
// disable limits if set to less then 1
|
||||
if (self::$_limit < 1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$file = 'traffic_limiter.php';
|
||||
if (self::_exists($file)) {
|
||||
require self::getPath($file);
|
||||
$tl = $GLOBALS['traffic_limiter'];
|
||||
} else {
|
||||
$tl = array();
|
||||
}
|
||||
|
||||
// purge file of expired hashes to keep it small
|
||||
$now = time();
|
||||
foreach ($tl as $key => $time) {
|
||||
if ($time + self::$_limit < $now) {
|
||||
unset($tl[$key]);
|
||||
// check if $_ipKey is exempted from ratelimiting
|
||||
if (!empty(self::$_exempted)) {
|
||||
$exIp_array = explode(',', self::$_exempted);
|
||||
foreach ($exIp_array as $ipRange) {
|
||||
if (self::matchIp($ipRange) === true) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// this hash is used as an array key, hence a shorter algo is used
|
||||
// used as array key, which are limited in length, hence using algo with shorter range
|
||||
$hash = self::getHash('sha256');
|
||||
if (array_key_exists($hash, $tl) && ($tl[$hash] + self::$_limit >= $now)) {
|
||||
$now = time();
|
||||
$tl = (int) self::$_store->getValue('traffic_limiter', $hash);
|
||||
self::$_store->purgeValues('traffic_limiter', $now - self::$_limit);
|
||||
if ($tl > 0 && ($tl + self::$_limit >= $now)) {
|
||||
$result = false;
|
||||
} else {
|
||||
$tl[$hash] = time();
|
||||
$result = true;
|
||||
$tl = time();
|
||||
$result = true;
|
||||
}
|
||||
self::_store(
|
||||
$file,
|
||||
'<?php' . PHP_EOL .
|
||||
'$GLOBALS[\'traffic_limiter\'] = ' . var_export($tl, true) . ';'
|
||||
);
|
||||
return $result;
|
||||
if (!self::$_store->setValue((string) $tl, 'traffic_limiter', $hash)) {
|
||||
error_log('failed to store the traffic limiter, it probably contains outdated information');
|
||||
}
|
||||
if ($result) {
|
||||
return true;
|
||||
}
|
||||
throw new Exception(I18n::_(
|
||||
'Please wait %d seconds between each post.',
|
||||
self::$_limit
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
+8
-11
@@ -7,7 +7,7 @@
|
||||
* @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.3.5
|
||||
* @version 1.4.0
|
||||
*/
|
||||
|
||||
namespace PrivateBin;
|
||||
@@ -108,7 +108,9 @@ class Request
|
||||
case 'DELETE':
|
||||
case 'PUT':
|
||||
case 'POST':
|
||||
$this->_params = Json::decode(
|
||||
// it might be a creation or a deletion, the latter is detected below
|
||||
$this->_operation = 'create';
|
||||
$this->_params = Json::decode(
|
||||
file_get_contents(self::$_inputStream)
|
||||
);
|
||||
break;
|
||||
@@ -125,15 +127,10 @@ class Request
|
||||
}
|
||||
|
||||
// prepare operation, depending on current parameters
|
||||
if (
|
||||
array_key_exists('ct', $this->_params) &&
|
||||
!empty($this->_params['ct'])
|
||||
) {
|
||||
$this->_operation = 'create';
|
||||
} elseif (array_key_exists('pasteid', $this->_params) && !empty($this->_params['pasteid'])) {
|
||||
if (array_key_exists('pasteid', $this->_params) && !empty($this->_params['pasteid'])) {
|
||||
if (array_key_exists('deletetoken', $this->_params) && !empty($this->_params['deletetoken'])) {
|
||||
$this->_operation = 'delete';
|
||||
} else {
|
||||
} elseif ($this->_operation != 'create') {
|
||||
$this->_operation = 'read';
|
||||
}
|
||||
} elseif (array_key_exists('jsonld', $this->_params) && !empty($this->_params['jsonld'])) {
|
||||
@@ -172,7 +169,7 @@ class Request
|
||||
$data['meta'] = $meta;
|
||||
}
|
||||
foreach ($required_keys as $key) {
|
||||
$data[$key] = $this->getParam($key);
|
||||
$data[$key] = $this->getParam($key, $key == 'v' ? 1 : '');
|
||||
}
|
||||
// forcing a cast to int or float
|
||||
$data['v'] = $data['v'] + 0;
|
||||
@@ -288,7 +285,7 @@ class Request
|
||||
}
|
||||
krsort($mediaTypes);
|
||||
foreach ($mediaTypes as $acceptedQuality => $acceptedValues) {
|
||||
if ($acceptedQuality === 0.0) {
|
||||
if ($acceptedQuality === '0.0') {
|
||||
continue;
|
||||
}
|
||||
foreach ($acceptedValues as $acceptedValue) {
|
||||
|
||||
+1
-1
@@ -7,7 +7,7 @@
|
||||
* @link https://github.com/PrivateBin/PrivateBin
|
||||
* @copyright 2012 Sébastien SAUVAGE (sebsauvage.net)
|
||||
* @license http://www.opensource.org/licenses/zlib-license.php The zlib/libpng License
|
||||
* @version 1.3.5
|
||||
* @version 1.4.0
|
||||
*/
|
||||
|
||||
namespace PrivateBin;
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
* @link http://sebsauvage.net/wiki/doku.php?id=php:vizhash_gd
|
||||
* @copyright 2012 Sébastien SAUVAGE (sebsauvage.net)
|
||||
* @license https://www.opensource.org/licenses/zlib-license.php The zlib/libpng License
|
||||
* @version 0.0.5 beta PrivateBin 1.3.5
|
||||
* @version 0.0.5 beta PrivateBin 1.4.0
|
||||
*/
|
||||
|
||||
namespace PrivateBin;
|
||||
|
||||
+11
-7
@@ -7,6 +7,7 @@ $isPage = substr($template, -5) === '-page';
|
||||
<html lang="<?php echo I18n::_('en'); ?>">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta http-equiv="Content-Security-Policy" content="<?php echo I18n::encode($CSPHEADER); ?>">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="robots" content="noindex" />
|
||||
@@ -41,7 +42,7 @@ if ($SYNTAXHIGHLIGHTING) :
|
||||
endif;
|
||||
?>
|
||||
<noscript><link type="text/css" rel="stylesheet" href="css/noscript.css" /></noscript>
|
||||
<script type="text/javascript" data-cfasync="false" src="js/jquery-3.4.1.js" integrity="sha512-bnIvzh6FU75ZKxp0GXLH9bewza/OIw6dLVh9ICg0gogclmYGguQJWl8U30WpbsGTqbIiAwxTsbe76DErLq5EDQ==" crossorigin="anonymous"></script>
|
||||
<script type="text/javascript" data-cfasync="false" src="js/jquery-3.6.0.js" integrity="sha512-894YE6QWD5I59HgZOGReFYm4dnWc1Qt5NtvYSaNcOP+u1T9qYdvdihz0PPSiiqn/+/3e7Jo4EaG7TubfWGUrMQ==" crossorigin="anonymous"></script>
|
||||
<?php
|
||||
if ($QRCODE) :
|
||||
?>
|
||||
@@ -54,10 +55,10 @@ if ($ZEROBINCOMPATIBILITY) :
|
||||
<?php
|
||||
endif;
|
||||
?>
|
||||
<script type="text/javascript" data-cfasync="false" src="js/zlib-1.2.11.js" integrity="sha512-Yey/0yoaVmSbqMEyyff3DIu8kCPwpHvHf7tY1AuZ1lrX9NPCMg87PwzngMi+VNbe4ilCApmePeuKT869RTcyCQ==" crossorigin="anonymous"></script>
|
||||
<script type="text/javascript" data-cfasync="false" src="js/base-x-3.0.7.js" integrity="sha512-/Bi1AJIP0TtxEB+Jh6Hk809H1G7vn4iJV80qagslf0+Hm0UjUi1s3qNrn1kZULjzUYuaf6ck0ndLGJ7MxWLmgQ==" crossorigin="anonymous"></script>
|
||||
<script type="text/javascript" data-cfasync="false" src="js/zlib-1.2.12.js" integrity="sha512-Ewve1dyEW/Vf97OY91/aWqMx9NaaUK5d8Z6JB1RR5gFXtMhse/Ya7D/5CE/UrQTwOWqmkvn97JjP4YDUrmq/yA==" crossorigin="anonymous"></script>
|
||||
<script type="text/javascript" data-cfasync="false" src="js/base-x-4.0.0.js" integrity="sha512-nNPg5IGCwwrveZ8cA/yMGr5HiRS5Ps2H+s0J/mKTPjCPWUgFGGw7M5nqdnPD3VsRwCVysUh3Y8OWjeSKGkEQJQ==" crossorigin="anonymous"></script>
|
||||
<script type="text/javascript" data-cfasync="false" src="js/rawinflate-0.3.js" integrity="sha512-g8uelGgJW9A/Z1tB6Izxab++oj5kdD7B4qC7DHwZkB6DGMXKyzx7v5mvap2HXueI2IIn08YlRYM56jwWdm2ucQ==" crossorigin="anonymous"></script>
|
||||
<script type="text/javascript" data-cfasync="false" src="js/bootstrap-3.3.7.js" integrity="sha512-iztkobsvnjKfAtTNdHkGVjAYTrrtlC7mGp/54c40wowO7LhURYl3gVzzcEqGl/qKXQltJ2HwMrdLcNUdo+N/RQ==" crossorigin="anonymous"></script>
|
||||
<script type="text/javascript" data-cfasync="false" src="js/bootstrap-3.4.1.js" integrity="sha512-oBTprMeNEKCnqfuqKd6sbvFzmFQtlXS3e0C/RGFV0hD6QzhHV+ODfaQbAlmY6/q0ubbwlAM/nCJjkrgA3waLzg==" crossorigin="anonymous"></script>
|
||||
<?php
|
||||
if ($SYNTAXHIGHLIGHTING) :
|
||||
?>
|
||||
@@ -66,13 +67,13 @@ if ($SYNTAXHIGHLIGHTING) :
|
||||
endif;
|
||||
if ($MARKDOWN) :
|
||||
?>
|
||||
<script type="text/javascript" data-cfasync="false" src="js/showdown-1.9.1.js" integrity="sha512-nRri7kqh3iRLdHbhtjfe8w9eAQPmt+ubH5U88UZyKbz6O9Q0q4haaXF0krOUclKmRJou/kKZYulgBHvHXPqOvg==" crossorigin="anonymous"></script>
|
||||
<script type="text/javascript" data-cfasync="false" src="js/showdown-2.0.3.js" integrity="sha512-vcfjvW3UKHD/4vlQx804cqWK88jFmjsWRsZ8/u5YEcyHB1IituxrXDU7TvdqsFVsMnxpE/UIEo25/SYW+puWHw==" crossorigin="anonymous"></script>
|
||||
<?php
|
||||
endif;
|
||||
?>
|
||||
<script type="text/javascript" data-cfasync="false" src="js/purify-2.2.7.js" integrity="sha512-7Ka1I/nJuR2CL8wzIS5PJS4HgEMd0HJ6kfAl6fFhwFBB27rhztFbe0tS+Ex+Qg+5n4nZIT4lty4k4Di3+X9T4A==" crossorigin="anonymous"></script>
|
||||
<script type="text/javascript" data-cfasync="false" src="js/purify-2.3.6.js" integrity="sha512-N1GGPjbqLbwK821ZN7C925WuTwU4aDxz2CEEOXQ6/s6m6MBwVj8fh5fugiE2hzsm0xud3q7jpjZQ4ILnpMREYQ==" crossorigin="anonymous"></script>
|
||||
<script type="text/javascript" data-cfasync="false" src="js/legacy.js?<?php echo rawurlencode($VERSION); ?>" integrity="sha512-LYos+qXHIRqFf5ZPNphvtTB0cgzHUizu2wwcOwcwz/VIpRv9lpcBgPYz4uq6jx0INwCAj6Fbnl5HoKiLufS2jg==" crossorigin="anonymous"></script>
|
||||
<script type="text/javascript" data-cfasync="false" src="js/privatebin.js?<?php echo rawurlencode($VERSION); ?>" integrity="sha512-wuKnPu9+bTYhJ0HRhUmw0UxWYP5mbQehFNspkD9N4mTlxLkjRZXPnMt/nfT2/U62rRDUw1HL3SvveKJe2v4EBw==" crossorigin="anonymous"></script>
|
||||
<script type="text/javascript" data-cfasync="false" src="js/privatebin.js?<?php echo rawurlencode($VERSION); ?>" integrity="sha512-/F0+9bIbFUC8mKQzrcAjIs2Jg92w1DMcczT2Y/KqHVkFEXH1ZSrqtUX7QjLH6RgVR0YhTxhmWkZ2c8scGCwpkQ==" crossorigin="anonymous"></script>
|
||||
<!-- icon -->
|
||||
<link rel="apple-touch-icon" href="<?php echo I18n::encode($BASEPATH); ?>img/apple-touch-icon.png" sizes="180x180" />
|
||||
<link rel="icon" type="image/png" href="img/favicon-32x32.png" sizes="32x32" />
|
||||
@@ -212,6 +213,9 @@ endif;
|
||||
<button id="rawtextbutton" type="button" class="hidden btn btn-<?php echo $isDark ? 'warning' : 'default'; ?> navbar-btn">
|
||||
<span class="glyphicon glyphicon-text-background" aria-hidden="true"></span> <?php echo I18n::_('Raw text'), PHP_EOL; ?>
|
||||
</button>
|
||||
<button id="downloadtextbutton" type="button" class="hidden btn btn-<?php echo $isDark ? 'warning' : 'default'; ?> navbar-btn">
|
||||
<span class="glyphicon glyphicon glyphicon-download-alt" aria-hidden="true"></span> <?php echo I18n::_('Save paste'), PHP_EOL; ?>
|
||||
</button>
|
||||
<button id="emaillink" type="button" class="hidden btn btn-<?php echo $isDark ? 'warning' : 'default'; ?> navbar-btn">
|
||||
<span class="glyphicon glyphicon-envelope" aria-hidden="true"></span> <?php echo I18n::_('Email'), PHP_EOL; ?>
|
||||
</button>
|
||||
|
||||
+8
-6
@@ -4,6 +4,7 @@ use PrivateBin\I18n;
|
||||
<html lang="<?php echo I18n::_('en'); ?>">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta http-equiv="Content-Security-Policy" content="<?php echo I18n::encode($CSPHEADER); ?>">
|
||||
<meta name="robots" content="noindex" />
|
||||
<meta name="google" content="notranslate">
|
||||
<title><?php echo I18n::_($NAME); ?></title>
|
||||
@@ -20,7 +21,7 @@ if ($SYNTAXHIGHLIGHTING):
|
||||
endif;
|
||||
endif;
|
||||
?>
|
||||
<script type="text/javascript" data-cfasync="false" src="js/jquery-3.4.1.js" integrity="sha512-bnIvzh6FU75ZKxp0GXLH9bewza/OIw6dLVh9ICg0gogclmYGguQJWl8U30WpbsGTqbIiAwxTsbe76DErLq5EDQ==" crossorigin="anonymous"></script>
|
||||
<script type="text/javascript" data-cfasync="false" src="js/jquery-3.6.0.js" integrity="sha512-894YE6QWD5I59HgZOGReFYm4dnWc1Qt5NtvYSaNcOP+u1T9qYdvdihz0PPSiiqn/+/3e7Jo4EaG7TubfWGUrMQ==" crossorigin="anonymous"></script>
|
||||
<?php
|
||||
if ($QRCODE):
|
||||
?>
|
||||
@@ -33,8 +34,8 @@ if ($ZEROBINCOMPATIBILITY):
|
||||
<?php
|
||||
endif;
|
||||
?>
|
||||
<script type="text/javascript" data-cfasync="false" src="js/zlib-1.2.11.js" integrity="sha512-Yey/0yoaVmSbqMEyyff3DIu8kCPwpHvHf7tY1AuZ1lrX9NPCMg87PwzngMi+VNbe4ilCApmePeuKT869RTcyCQ==" crossorigin="anonymous"></script>
|
||||
<script type="text/javascript" data-cfasync="false" src="js/base-x-3.0.7.js" integrity="sha512-/Bi1AJIP0TtxEB+Jh6Hk809H1G7vn4iJV80qagslf0+Hm0UjUi1s3qNrn1kZULjzUYuaf6ck0ndLGJ7MxWLmgQ==" crossorigin="anonymous"></script>
|
||||
<script type="text/javascript" data-cfasync="false" src="js/zlib-1.2.12.js" integrity="sha512-Ewve1dyEW/Vf97OY91/aWqMx9NaaUK5d8Z6JB1RR5gFXtMhse/Ya7D/5CE/UrQTwOWqmkvn97JjP4YDUrmq/yA==" crossorigin="anonymous"></script>
|
||||
<script type="text/javascript" data-cfasync="false" src="js/base-x-4.0.0.js" integrity="sha512-nNPg5IGCwwrveZ8cA/yMGr5HiRS5Ps2H+s0J/mKTPjCPWUgFGGw7M5nqdnPD3VsRwCVysUh3Y8OWjeSKGkEQJQ==" crossorigin="anonymous"></script>
|
||||
<script type="text/javascript" data-cfasync="false" src="js/rawinflate-0.3.js" integrity="sha512-g8uelGgJW9A/Z1tB6Izxab++oj5kdD7B4qC7DHwZkB6DGMXKyzx7v5mvap2HXueI2IIn08YlRYM56jwWdm2ucQ==" crossorigin="anonymous"></script>
|
||||
<?php
|
||||
if ($SYNTAXHIGHLIGHTING):
|
||||
@@ -44,13 +45,13 @@ if ($SYNTAXHIGHLIGHTING):
|
||||
endif;
|
||||
if ($MARKDOWN):
|
||||
?>
|
||||
<script type="text/javascript" data-cfasync="false" src="js/showdown-1.9.1.js" integrity="sha512-nRri7kqh3iRLdHbhtjfe8w9eAQPmt+ubH5U88UZyKbz6O9Q0q4haaXF0krOUclKmRJou/kKZYulgBHvHXPqOvg==" crossorigin="anonymous"></script>
|
||||
<script type="text/javascript" data-cfasync="false" src="js/showdown-2.0.3.js" integrity="sha512-vcfjvW3UKHD/4vlQx804cqWK88jFmjsWRsZ8/u5YEcyHB1IituxrXDU7TvdqsFVsMnxpE/UIEo25/SYW+puWHw==" crossorigin="anonymous"></script>
|
||||
<?php
|
||||
endif;
|
||||
?>
|
||||
<script type="text/javascript" data-cfasync="false" src="js/purify-2.2.7.js" integrity="sha512-7Ka1I/nJuR2CL8wzIS5PJS4HgEMd0HJ6kfAl6fFhwFBB27rhztFbe0tS+Ex+Qg+5n4nZIT4lty4k4Di3+X9T4A==" crossorigin="anonymous"></script>
|
||||
<script type="text/javascript" data-cfasync="false" src="js/purify-2.3.6.js" integrity="sha512-N1GGPjbqLbwK821ZN7C925WuTwU4aDxz2CEEOXQ6/s6m6MBwVj8fh5fugiE2hzsm0xud3q7jpjZQ4ILnpMREYQ==" crossorigin="anonymous"></script>
|
||||
<script type="text/javascript" data-cfasync="false" src="js/legacy.js?<?php echo rawurlencode($VERSION); ?>" integrity="sha512-LYos+qXHIRqFf5ZPNphvtTB0cgzHUizu2wwcOwcwz/VIpRv9lpcBgPYz4uq6jx0INwCAj6Fbnl5HoKiLufS2jg==" crossorigin="anonymous"></script>
|
||||
<script type="text/javascript" data-cfasync="false" src="js/privatebin.js?<?php echo rawurlencode($VERSION); ?>" integrity="sha512-wuKnPu9+bTYhJ0HRhUmw0UxWYP5mbQehFNspkD9N4mTlxLkjRZXPnMt/nfT2/U62rRDUw1HL3SvveKJe2v4EBw==" crossorigin="anonymous"></script>
|
||||
<script type="text/javascript" data-cfasync="false" src="js/privatebin.js?<?php echo rawurlencode($VERSION); ?>" integrity="sha512-/F0+9bIbFUC8mKQzrcAjIs2Jg92w1DMcczT2Y/KqHVkFEXH1ZSrqtUX7QjLH6RgVR0YhTxhmWkZ2c8scGCwpkQ==" crossorigin="anonymous"></script>
|
||||
<!-- icon -->
|
||||
<link rel="apple-touch-icon" href="img/apple-touch-icon.png?<?php echo rawurlencode($VERSION); ?>" sizes="180x180" />
|
||||
<link rel="icon" type="image/png" href="img/favicon-32x32.png?<?php echo rawurlencode($VERSION); ?>" sizes="32x32" />
|
||||
@@ -127,6 +128,7 @@ endif;
|
||||
<button id="sendbutton" class="hidden"><img src="img/icon_send.png" width="18" height="15" alt="" /><?php echo I18n::_('Send'); ?></button>
|
||||
<button id="clonebutton" class="hidden"><img src="img/icon_clone.png" width="15" height="17" alt="" /><?php echo I18n::_('Clone'); ?></button>
|
||||
<button id="rawtextbutton" class="hidden"><img src="img/icon_raw.png" width="15" height="15" alt="" /><?php echo I18n::_('Raw text'); ?></button>
|
||||
<button id="downloadtextbutton" class="hidden"><?php echo I18n::_('Save paste'), PHP_EOL; ?></button>
|
||||
<button id="emaillink" class="hidden"><img src="img/icon_email.png" width="15" height="15" alt="" /><?php echo I18n::_('Email'); ?></button>
|
||||
<?php
|
||||
if ($QRCODE):
|
||||
|
||||
+591
-1
@@ -1,5 +1,11 @@
|
||||
<?php
|
||||
|
||||
use Google\Cloud\Core\Exception\BadRequestException;
|
||||
use Google\Cloud\Core\Exception\NotFoundException;
|
||||
use Google\Cloud\Storage\Bucket;
|
||||
use Google\Cloud\Storage\Connection\ConnectionInterface;
|
||||
use Google\Cloud\Storage\StorageClient;
|
||||
use Google\Cloud\Storage\StorageObject;
|
||||
use PrivateBin\Persistence\ServerSalt;
|
||||
|
||||
error_reporting(E_ALL | E_STRICT);
|
||||
@@ -21,6 +27,586 @@ if (!defined('CONF_SAMPLE')) {
|
||||
require PATH . 'vendor/autoload.php';
|
||||
Helper::updateSubresourceIntegrity();
|
||||
|
||||
/**
|
||||
* Class StorageClientStub provides a limited stub for performing the unit test
|
||||
*/
|
||||
class StorageClientStub extends StorageClient
|
||||
{
|
||||
private $_config = null;
|
||||
private $_connection = null;
|
||||
private $_buckets = array();
|
||||
|
||||
public function __construct(array $config = array())
|
||||
{
|
||||
$this->_config = $config;
|
||||
$this->_connection = new ConnectionInterfaceStub();
|
||||
}
|
||||
|
||||
public function bucket($name, $userProject = false)
|
||||
{
|
||||
if (!key_exists($name, $this->_buckets)) {
|
||||
$b = new BucketStub($this->_connection, $name, array(), $this);
|
||||
$this->_buckets[$name] = $b;
|
||||
}
|
||||
return $this->_buckets[$name];
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \Google\Cloud\Core\Exception\NotFoundException
|
||||
*/
|
||||
public function deleteBucket($name)
|
||||
{
|
||||
if (key_exists($name, $this->_buckets)) {
|
||||
unset($this->_buckets[$name]);
|
||||
} else {
|
||||
throw new NotFoundException();
|
||||
}
|
||||
}
|
||||
|
||||
public function buckets(array $options = array())
|
||||
{
|
||||
throw new BadMethodCallException('not supported by this stub');
|
||||
}
|
||||
|
||||
public function registerStreamWrapper($protocol = null)
|
||||
{
|
||||
throw new BadMethodCallException('not supported by this stub');
|
||||
}
|
||||
|
||||
public function unregisterStreamWrapper($protocol = null)
|
||||
{
|
||||
throw new BadMethodCallException('not supported by this stub');
|
||||
}
|
||||
|
||||
public function signedUrlUploader($uri, $data, array $options = array())
|
||||
{
|
||||
throw new BadMethodCallException('not supported by this stub');
|
||||
}
|
||||
|
||||
public function timestamp(\DateTimeInterface $timestamp, $nanoSeconds = null)
|
||||
{
|
||||
throw new BadMethodCallException('not supported by this stub');
|
||||
}
|
||||
|
||||
public function getServiceAccount(array $options = array())
|
||||
{
|
||||
throw new BadMethodCallException('not supported by this stub');
|
||||
}
|
||||
|
||||
public function hmacKeys(array $options = array())
|
||||
{
|
||||
throw new BadMethodCallException('not supported by this stub');
|
||||
}
|
||||
|
||||
public function hmacKey($accessId, $projectId = null, array $metadata = array())
|
||||
{
|
||||
throw new BadMethodCallException('not supported by this stub');
|
||||
}
|
||||
|
||||
public function createHmacKey($serviceAccountEmail, array $options = array())
|
||||
{
|
||||
throw new BadMethodCallException('not supported by this stub');
|
||||
}
|
||||
|
||||
public function createBucket($name, array $options = array())
|
||||
{
|
||||
if (key_exists($name, $this->_buckets)) {
|
||||
throw new BadRequestException('already exists');
|
||||
}
|
||||
$b = new BucketStub($this->_connection, $name, array(), $this);
|
||||
$this->_buckets[$name] = $b;
|
||||
return $b;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Class BucketStub stubs a GCS bucket.
|
||||
*/
|
||||
class BucketStub extends Bucket
|
||||
{
|
||||
public $_objects;
|
||||
private $_name;
|
||||
private $_info;
|
||||
private $_connection;
|
||||
private $_client;
|
||||
|
||||
public function __construct(ConnectionInterface $connection, $name, array $info = array(), $client = null)
|
||||
{
|
||||
$this->_name = $name;
|
||||
$this->_info = $info;
|
||||
$this->_connection = $connection;
|
||||
$this->_objects = array();
|
||||
$this->_client = $client;
|
||||
}
|
||||
|
||||
public function acl()
|
||||
{
|
||||
throw new BadMethodCallException('not supported by this stub');
|
||||
}
|
||||
|
||||
public function defaultAcl()
|
||||
{
|
||||
throw new BadMethodCallException('not supported by this stub');
|
||||
}
|
||||
|
||||
public function exists()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function upload($data, array $options = array())
|
||||
{
|
||||
if (!is_string($data) || !key_exists('name', $options)) {
|
||||
throw new BadMethodCallException('not supported by this stub');
|
||||
}
|
||||
|
||||
$name = $options['name'];
|
||||
$generation = '1';
|
||||
$o = new StorageObjectStub($this->_connection, $name, $this, $generation, $options);
|
||||
$this->_objects[$options['name']] = $o;
|
||||
$o->setData($data);
|
||||
}
|
||||
|
||||
public function uploadAsync($data, array $options = array())
|
||||
{
|
||||
throw new BadMethodCallException('not supported by this stub');
|
||||
}
|
||||
|
||||
public function getResumableUploader($data, array $options = array())
|
||||
{
|
||||
throw new BadMethodCallException('not supported by this stub');
|
||||
}
|
||||
|
||||
public function getStreamableUploader($data, array $options = array())
|
||||
{
|
||||
throw new BadMethodCallException('not supported by this stub');
|
||||
}
|
||||
|
||||
public function object($name, array $options = array())
|
||||
{
|
||||
if (key_exists($name, $this->_objects)) {
|
||||
return $this->_objects[$name];
|
||||
} else {
|
||||
return new StorageObjectStub($this->_connection, $name, $this, null, $options);
|
||||
}
|
||||
}
|
||||
|
||||
public function objects(array $options = array())
|
||||
{
|
||||
$prefix = key_exists('prefix', $options) ? $options['prefix'] : '';
|
||||
|
||||
return new CallbackFilterIterator(
|
||||
new ArrayIterator($this->_objects),
|
||||
function ($current, $key, $iterator) use ($prefix) {
|
||||
return substr($key, 0, strlen($prefix)) == $prefix;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
public function createNotification($topic, array $options = array())
|
||||
{
|
||||
throw new BadMethodCallException('not supported by this stub');
|
||||
}
|
||||
|
||||
public function notification($id)
|
||||
{
|
||||
throw new BadMethodCallException('not supported by this stub');
|
||||
}
|
||||
|
||||
public function notifications(array $options = array())
|
||||
{
|
||||
throw new BadMethodCallException('not supported by this stub');
|
||||
}
|
||||
|
||||
public function delete(array $options = array())
|
||||
{
|
||||
$this->_client->deleteBucket($this->_name);
|
||||
}
|
||||
|
||||
public function update(array $options = array())
|
||||
{
|
||||
throw new BadMethodCallException('not supported by this stub');
|
||||
}
|
||||
|
||||
public function compose(array $sourceObjects, $name, array $options = array())
|
||||
{
|
||||
throw new BadMethodCallException('not supported by this stub');
|
||||
}
|
||||
|
||||
public function info(array $options = array())
|
||||
{
|
||||
throw new BadMethodCallException('not supported by this stub');
|
||||
}
|
||||
|
||||
public function reload(array $options = array())
|
||||
{
|
||||
throw new BadMethodCallException('not supported by this stub');
|
||||
}
|
||||
|
||||
public function name()
|
||||
{
|
||||
return $this->_name;
|
||||
}
|
||||
|
||||
public static function lifecycle(array $lifecycle = array())
|
||||
{
|
||||
throw new BadMethodCallException('not supported by this stub');
|
||||
}
|
||||
|
||||
public function currentLifecycle(array $options = array())
|
||||
{
|
||||
throw new BadMethodCallException('not supported by this stub');
|
||||
}
|
||||
|
||||
public function isWritable($file = null)
|
||||
{
|
||||
throw new BadMethodCallException('not supported by this stub');
|
||||
}
|
||||
|
||||
public function iam()
|
||||
{
|
||||
throw new BadMethodCallException('not supported by this stub');
|
||||
}
|
||||
|
||||
public function lockRetentionPolicy(array $options = array())
|
||||
{
|
||||
throw new BadMethodCallException('not supported by this stub');
|
||||
}
|
||||
|
||||
public function signedUrl($expires, array $options = array())
|
||||
{
|
||||
throw new BadMethodCallException('not supported by this stub');
|
||||
}
|
||||
|
||||
public function generateSignedPostPolicyV4($objectName, $expires, array $options = array())
|
||||
{
|
||||
throw new BadMethodCallException('not supported by this stub');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Class StorageObjectStub stubs a GCS storage object.
|
||||
*/
|
||||
class StorageObjectStub extends StorageObject
|
||||
{
|
||||
private $_name;
|
||||
private $_data;
|
||||
private $_info;
|
||||
private $_bucket;
|
||||
private $_generation;
|
||||
private $_exists = false;
|
||||
private $_connection;
|
||||
|
||||
public function __construct(ConnectionInterface $connection, $name, $bucket, $generation = null, array $info = array(), $encryptionKey = null, $encryptionKeySHA256 = null)
|
||||
{
|
||||
$this->_name = $name;
|
||||
$this->_bucket = $bucket;
|
||||
$this->_generation = $generation;
|
||||
$this->_info = $info;
|
||||
$this->_connection = $connection;
|
||||
$timeCreated = new DateTime();
|
||||
$this->_info['metadata']['timeCreated'] = $timeCreated->format('Y-m-d\TH:i:s.u\Z');
|
||||
}
|
||||
|
||||
public function acl()
|
||||
{
|
||||
throw new BadMethodCallException('not supported by this stub');
|
||||
}
|
||||
|
||||
public function exists(array $options = array())
|
||||
{
|
||||
return key_exists($this->_name, $this->_bucket->_objects);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws NotFoundException
|
||||
*/
|
||||
public function delete(array $options = array())
|
||||
{
|
||||
if (key_exists($this->_name, $this->_bucket->_objects)) {
|
||||
unset($this->_bucket->_objects[$this->_name]);
|
||||
} else {
|
||||
throw new NotFoundException('key ' . $this->_name . ' not found.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws NotFoundException
|
||||
*/
|
||||
public function update(array $metadata, array $options = array())
|
||||
{
|
||||
if (!$this->_exists) {
|
||||
throw new NotFoundException('key ' . $this->_name . ' not found.');
|
||||
}
|
||||
$this->_info = $metadata;
|
||||
}
|
||||
|
||||
public function copy($destination, array $options = array())
|
||||
{
|
||||
throw new BadMethodCallException('not supported by this stub');
|
||||
}
|
||||
|
||||
public function rewrite($destination, array $options = array())
|
||||
{
|
||||
throw new BadMethodCallException('not supported by this stub');
|
||||
}
|
||||
|
||||
public function rename($name, array $options = array())
|
||||
{
|
||||
throw new BadMethodCallException('not supported by this stub');
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws NotFoundException
|
||||
*/
|
||||
public function downloadAsString(array $options = array())
|
||||
{
|
||||
if (!$this->_exists) {
|
||||
throw new NotFoundException('key ' . $this->_name . ' not found.');
|
||||
}
|
||||
return $this->_data;
|
||||
}
|
||||
|
||||
public function downloadToFile($path, array $options = array())
|
||||
{
|
||||
throw new BadMethodCallException('not supported by this stub');
|
||||
}
|
||||
|
||||
public function downloadAsStream(array $options = array())
|
||||
{
|
||||
throw new BadMethodCallException('not supported by this stub');
|
||||
}
|
||||
|
||||
public function downloadAsStreamAsync(array $options = array())
|
||||
{
|
||||
throw new BadMethodCallException('not supported by this stub');
|
||||
}
|
||||
|
||||
public function signedUrl($expires, array $options = array())
|
||||
{
|
||||
throw new BadMethodCallException('not supported by this stub');
|
||||
}
|
||||
|
||||
public function signedUploadUrl($expires, array $options = array())
|
||||
{
|
||||
throw new BadMethodCallException('not supported by this stub');
|
||||
}
|
||||
|
||||
public function beginSignedUploadSession(array $options = array())
|
||||
{
|
||||
throw new BadMethodCallException('not supported by this stub');
|
||||
}
|
||||
|
||||
public function info(array $options = array())
|
||||
{
|
||||
return key_exists('metadata',$this->_info) ? $this->_info['metadata'] : array();
|
||||
}
|
||||
|
||||
public function reload(array $options = array())
|
||||
{
|
||||
throw new BadMethodCallException('not supported by this stub');
|
||||
}
|
||||
|
||||
public function name()
|
||||
{
|
||||
return $this->_name;
|
||||
}
|
||||
|
||||
public function identity()
|
||||
{
|
||||
throw new BadMethodCallException('not supported by this stub');
|
||||
}
|
||||
|
||||
public function gcsUri()
|
||||
{
|
||||
return sprintf(
|
||||
'gs://%s/%s',
|
||||
$this->_bucket->name(),
|
||||
$this->_name
|
||||
);
|
||||
}
|
||||
|
||||
public function setData($data)
|
||||
{
|
||||
$this->_data = $data;
|
||||
$this->_exists = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Class ConnectionInterfaceStub required for the stubs.
|
||||
*/
|
||||
class ConnectionInterfaceStub implements ConnectionInterface
|
||||
{
|
||||
public function deleteAcl(array $args = array())
|
||||
{
|
||||
throw new BadMethodCallException('not supported by this stub');
|
||||
}
|
||||
|
||||
public function getAcl(array $args = array())
|
||||
{
|
||||
throw new BadMethodCallException('not supported by this stub');
|
||||
}
|
||||
|
||||
public function listAcl(array $args = array())
|
||||
{
|
||||
throw new BadMethodCallException('not supported by this stub');
|
||||
}
|
||||
|
||||
public function insertAcl(array $args = array())
|
||||
{
|
||||
throw new BadMethodCallException('not supported by this stub');
|
||||
}
|
||||
|
||||
public function patchAcl(array $args = array())
|
||||
{
|
||||
throw new BadMethodCallException('not supported by this stub');
|
||||
}
|
||||
|
||||
public function deleteBucket(array $args = array())
|
||||
{
|
||||
throw new BadMethodCallException('not supported by this stub');
|
||||
}
|
||||
|
||||
public function getBucket(array $args = array())
|
||||
{
|
||||
throw new BadMethodCallException('not supported by this stub');
|
||||
}
|
||||
|
||||
public function listBuckets(array $args = array())
|
||||
{
|
||||
throw new BadMethodCallException('not supported by this stub');
|
||||
}
|
||||
|
||||
public function insertBucket(array $args = array())
|
||||
{
|
||||
throw new BadMethodCallException('not supported by this stub');
|
||||
}
|
||||
|
||||
public function getBucketIamPolicy(array $args)
|
||||
{
|
||||
throw new BadMethodCallException('not supported by this stub');
|
||||
}
|
||||
|
||||
public function setBucketIamPolicy(array $args)
|
||||
{
|
||||
throw new BadMethodCallException('not supported by this stub');
|
||||
}
|
||||
|
||||
public function testBucketIamPermissions(array $args)
|
||||
{
|
||||
throw new BadMethodCallException('not supported by this stub');
|
||||
}
|
||||
|
||||
public function patchBucket(array $args = array())
|
||||
{
|
||||
throw new BadMethodCallException('not supported by this stub');
|
||||
}
|
||||
|
||||
public function deleteObject(array $args = array())
|
||||
{
|
||||
throw new BadMethodCallException('not supported by this stub');
|
||||
}
|
||||
|
||||
public function copyObject(array $args = array())
|
||||
{
|
||||
throw new BadMethodCallException('not supported by this stub');
|
||||
}
|
||||
|
||||
public function rewriteObject(array $args = array())
|
||||
{
|
||||
throw new BadMethodCallException('not supported by this stub');
|
||||
}
|
||||
|
||||
public function composeObject(array $args = array())
|
||||
{
|
||||
throw new BadMethodCallException('not supported by this stub');
|
||||
}
|
||||
|
||||
public function getObject(array $args = array())
|
||||
{
|
||||
throw new BadMethodCallException('not supported by this stub');
|
||||
}
|
||||
|
||||
public function listObjects(array $args = array())
|
||||
{
|
||||
throw new BadMethodCallException('not supported by this stub');
|
||||
}
|
||||
|
||||
public function patchObject(array $args = array())
|
||||
{
|
||||
throw new BadMethodCallException('not supported by this stub');
|
||||
}
|
||||
|
||||
public function downloadObject(array $args = array())
|
||||
{
|
||||
throw new BadMethodCallException('not supported by this stub');
|
||||
}
|
||||
|
||||
public function insertObject(array $args = array())
|
||||
{
|
||||
throw new BadMethodCallException('not supported by this stub');
|
||||
}
|
||||
|
||||
public function getNotification(array $args = array())
|
||||
{
|
||||
throw new BadMethodCallException('not supported by this stub');
|
||||
}
|
||||
|
||||
public function deleteNotification(array $args = array())
|
||||
{
|
||||
throw new BadMethodCallException('not supported by this stub');
|
||||
}
|
||||
|
||||
public function insertNotification(array $args = array())
|
||||
{
|
||||
throw new BadMethodCallException('not supported by this stub');
|
||||
}
|
||||
|
||||
public function listNotifications(array $args = array())
|
||||
{
|
||||
throw new BadMethodCallException('not supported by this stub');
|
||||
}
|
||||
|
||||
public function getServiceAccount(array $args = array())
|
||||
{
|
||||
throw new BadMethodCallException('not supported by this stub');
|
||||
}
|
||||
|
||||
public function lockRetentionPolicy(array $args = array())
|
||||
{
|
||||
throw new BadMethodCallException('not supported by this stub');
|
||||
}
|
||||
|
||||
public function createHmacKey(array $args = array())
|
||||
{
|
||||
throw new BadMethodCallException('not supported by this stub');
|
||||
}
|
||||
|
||||
public function deleteHmacKey(array $args = array())
|
||||
{
|
||||
throw new BadMethodCallException('not supported by this stub');
|
||||
}
|
||||
|
||||
public function getHmacKey(array $args = array())
|
||||
{
|
||||
throw new BadMethodCallException('not supported by this stub');
|
||||
}
|
||||
|
||||
public function updateHmacKey(array $args = array())
|
||||
{
|
||||
throw new BadMethodCallException('not supported by this stub');
|
||||
}
|
||||
|
||||
public function listHmacKeys(array $args = array())
|
||||
{
|
||||
throw new BadMethodCallException('not supported by this stub');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Class Helper provides unit tests pastes and comments of various formats
|
||||
*/
|
||||
class Helper
|
||||
{
|
||||
/**
|
||||
@@ -155,7 +741,11 @@ class Helper
|
||||
public static function getPastePost($version = 2, array $meta = array())
|
||||
{
|
||||
$example = self::getPaste($version, $meta);
|
||||
$example['meta'] = array('expire' => $example['meta']['expire']);
|
||||
if ($version == 2) {
|
||||
$example['meta'] = array('expire' => $example['meta']['expire']);
|
||||
} else {
|
||||
unset($example['meta']['postdate']);
|
||||
}
|
||||
return $example;
|
||||
}
|
||||
|
||||
|
||||
@@ -17,8 +17,6 @@ class ConfigurationTest extends PHPUnit_Framework_TestCase
|
||||
$this->_minimalConfig = '[main]' . PHP_EOL . '[model]' . PHP_EOL . '[model_options]';
|
||||
$this->_options = Configuration::getDefaults();
|
||||
$this->_options['model_options']['dir'] = PATH . $this->_options['model_options']['dir'];
|
||||
$this->_options['traffic']['dir'] = PATH . $this->_options['traffic']['dir'];
|
||||
$this->_options['purge']['dir'] = PATH . $this->_options['purge']['dir'];
|
||||
$this->_path = sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'privatebin_cfg';
|
||||
if (!is_dir($this->_path)) {
|
||||
mkdir($this->_path);
|
||||
@@ -147,44 +145,6 @@ class ConfigurationTest extends PHPUnit_Framework_TestCase
|
||||
$this->assertEquals('Database', $conf->getKey('class', 'model'), 'old db class gets renamed');
|
||||
}
|
||||
|
||||
public function testHandleConfigFileRename()
|
||||
{
|
||||
$options = $this->_options;
|
||||
Helper::createIniFile(PATH . 'cfg' . DIRECTORY_SEPARATOR . 'conf.ini.sample', $options);
|
||||
|
||||
$options['main']['opendiscussion'] = true;
|
||||
$options['main']['fileupload'] = true;
|
||||
$options['main']['template'] = 'darkstrap';
|
||||
Helper::createIniFile(PATH . 'cfg' . DIRECTORY_SEPARATOR . 'conf.ini', $options);
|
||||
|
||||
$conf = new Configuration;
|
||||
$this->assertFileExists(CONF, 'old configuration file gets converted');
|
||||
$this->assertFileNotExists(PATH . 'cfg' . DIRECTORY_SEPARATOR . 'conf.ini', 'old configuration file gets removed');
|
||||
$this->assertFileNotExists(PATH . 'cfg' . DIRECTORY_SEPARATOR . 'conf.ini.sample', 'old configuration sample file gets removed');
|
||||
$this->assertTrue(
|
||||
$conf->getKey('opendiscussion') &&
|
||||
$conf->getKey('fileupload') &&
|
||||
$conf->getKey('template') === 'darkstrap',
|
||||
'configuration values get converted'
|
||||
);
|
||||
}
|
||||
|
||||
public function testRenameIniSample()
|
||||
{
|
||||
$iniSample = PATH . 'cfg' . DIRECTORY_SEPARATOR . 'conf.ini.sample';
|
||||
|
||||
Helper::createIniFile(PATH . 'cfg' . DIRECTORY_SEPARATOR . 'conf.ini', $this->_options);
|
||||
if (is_file(CONF)) {
|
||||
unlink(CONF);
|
||||
}
|
||||
rename(CONF_SAMPLE, $iniSample);
|
||||
new Configuration;
|
||||
$this->assertFileNotExists($iniSample, 'old sample file gets removed');
|
||||
$this->assertFileExists(CONF_SAMPLE, 'new sample file gets created');
|
||||
$this->assertFileExists(CONF, 'old configuration file gets converted');
|
||||
$this->assertFileNotExists(PATH . 'cfg' . DIRECTORY_SEPARATOR . 'conf.ini', 'old configuration file gets removed');
|
||||
}
|
||||
|
||||
public function testConfigPath()
|
||||
{
|
||||
// setup
|
||||
@@ -204,29 +164,4 @@ class ConfigurationTest extends PHPUnit_Framework_TestCase
|
||||
}
|
||||
putenv('CONFIG_PATH');
|
||||
}
|
||||
|
||||
public function testConfigPathIni()
|
||||
{
|
||||
// setup
|
||||
$configFile = $this->_path . DIRECTORY_SEPARATOR . 'conf.ini';
|
||||
$configMigrated = $this->_path . DIRECTORY_SEPARATOR . 'conf.php';
|
||||
$options = $this->_options;
|
||||
$options['main']['name'] = 'OtherBin';
|
||||
Helper::createIniFile($configFile, $options);
|
||||
$this->assertFileNotExists(CONF, 'configuration in the default location is non existing');
|
||||
|
||||
// test
|
||||
putenv('CONFIG_PATH=' . $this->_path);
|
||||
$conf = new Configuration;
|
||||
$this->assertEquals('OtherBin', $conf->getKey('name'), 'changing config path is supported for ini files as well');
|
||||
$this->assertFileExists($configMigrated, 'old configuration file gets converted');
|
||||
$this->assertFileNotExists($configFile, 'old configuration file gets removed');
|
||||
$this->assertFileNotExists(CONF, 'configuration is not created in the default location');
|
||||
|
||||
// cleanup environment
|
||||
if (is_file($configFile)) {
|
||||
unlink($configFile);
|
||||
}
|
||||
putenv('CONFIG_PATH');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -428,8 +428,6 @@ class ConfigurationCombinationsTest extends PHPUnit_Framework_TestCase
|
||||
Helper::confBackup();
|
||||
$this->_path = sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'privatebin_data';
|
||||
$this->_model = Filesystem::getInstance(array('dir' => $this->_path));
|
||||
ServerSalt::setPath($this->_path);
|
||||
TrafficLimiter::setPath($this->_path);
|
||||
$this->reset();
|
||||
}
|
||||
|
||||
@@ -449,8 +447,6 @@ class ConfigurationCombinationsTest extends PHPUnit_Framework_TestCase
|
||||
if ($this->_model->exists(Helper::getPasteId()))
|
||||
$this->_model->delete(Helper::getPasteId());
|
||||
$configuration['model_options']['dir'] = $this->_path;
|
||||
$configuration['traffic']['dir'] = $this->_path;
|
||||
$configuration['purge']['dir'] = $this->_path;
|
||||
Helper::createIniFile(CONF, $configuration);
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user