125 Commits

Author SHA1 Message Date
rugk
6731bc1214 Merge branch 'experiment-showdown-2' of https://github.com/PrivateBin/PrivateBin into experiment-showdown-2 2022-02-20 16:00:05 +01:00
rugk
94ef6392b0 Unminify/beautify minified showdown version for CodeQL
As in https://github.com/PrivateBin/PrivateBin/pull/892
To get to know what is bad...

USed DuckDuckGo's beautifier
2022-02-20 15:59:47 +01:00
rugk
438638b72b Unminify/beautify minified showdown version for CodeQL
As in https://github.com/PrivateBin/PrivateBin/pull/892
To get to know what is bad...
2022-02-20 15:59:18 +01:00
El RIDO
35698f0f99 test to determine what bugs CodeQL 2022-02-20 15:28:16 +01:00
El RIDO
0e3a7196f9 set frame-ancestors to none
disables embedding the site in any frames, which can bypass some of the security mechanisms reg. cross site scripting
2022-02-20 15:21:47 +01:00
El RIDO
7277d2bb43 update all libraries 2022-02-18 07:36:09 +01:00
El RIDO
c8c6a67530 Merge pull request #887 from PrivateBin/crowdin-translation
New Crowdin updates
2022-02-18 06:50:44 +01:00
El RIDO
9443900f66 Merge pull request #886 from PrivateBin/scrutinizer-i18n-test
allow for Lojban (jbo) to be the "any" language pick
2022-02-18 06:17:50 +01:00
PrivateBin Translator Bot
76bc8590a6 New translations en.json (Catalan) 2022-02-18 01:37:48 +01:00
PrivateBin Translator Bot
6cc47e6073 New translations en.json (Catalan) 2022-02-18 00:42:37 +01:00
El RIDO
2d7f5e9a9f allow for Lojban (jbo) to be the "any" language pick
The available language list is generated by reading the i18n directory
descriptor one entry at a time, so if the jbo.json happens to be the first
file written to the directory it will be on top of the list and get picked.

This is an edge case, most users browsers won't be set to that, but we need
to cover this allowed and valid use case in the language detection.
2022-02-17 20:44:49 +01:00
El RIDO
6307c01cc6 Merge pull request #882 from PrivateBin/php-warning
php warning in templates, fixes #875
2022-02-17 20:37:47 +01:00
El RIDO
a200f8875c php warning in templates, fixes #875 2022-02-15 19:02:44 +01:00
El RIDO
75eede9870 Merge branch 'crowdin-translation' 2022-02-13 09:03:47 +01:00
El RIDO
e822673e90 Merge branch 'master' into crowdin-translation 2022-02-13 09:01:27 +01:00
El RIDO
9df6754dfa Merge pull request #881 from PrivateBin/jbobau
Lojban translation
2022-02-13 08:58:29 +01:00
PrivateBin Translator Bot
f5f07b9288 New translations en.json (Lojban) 2022-02-12 18:24:14 +01:00
El RIDO
75a86e2594 Merge pull request #879 from AckKid/patch-1
Create Procfile
2022-02-12 17:29:22 +01:00
El RIDO
4758fd683a Merge branch 'bc-bjoern-jquery' 2022-02-12 17:17:17 +01:00
El RIDO
186dd82653 Apply StyleCI fix
that class name we used was not quite correct, but PHP tolerated the typo
2022-02-12 16:41:25 +01:00
El RIDO
8faf0501f4 improve Lojban support
- Crowdin has to use the 3 letter language code, since Lojban has no 2 letter code. Added support for this in the PHP backend and renamed the translation file.
- Lojban has no plural cases, updated the plural-formulas accordingly.
- Credited the change and documented it.
- Updated the SRI hashes.
2022-02-12 16:17:09 +01:00
Bjoern Becker
832f000576 update jquery 2022-02-11 12:22:16 +01:00
PrivateBin Translator Bot
6da1fa04c3 New translations en.json (Lojban) 2022-02-11 06:47:01 +01:00
PrivateBin Translator Bot
0c0be618e0 New translations en.json (Lojban) 2022-02-11 05:48:20 +01:00
PrivateBin Translator Bot
8b0ab8ee49 New translations en.json (Lojban) 2022-02-11 01:30:42 +01:00
AckKid
49d0b35dc8 Create Procfile 2022-02-10 18:53:39 -05:00
PrivateBin Translator Bot
86da334e8f New translations en.json (Lojban) 2022-02-11 00:18:31 +01:00
PrivateBin Translator Bot
b21d5afa2d New translations en.json (Lojban) 2022-02-10 22:21:16 +01:00
PrivateBin Translator Bot
5b5f1e3aa5 New translations en.json (Lojban) 2022-02-10 21:23:20 +01:00
PrivateBin Translator Bot
34923addd7 New translations en.json (Lojban) 2022-02-10 20:16:16 +01:00
foxsouns - SEE ME @ GITLAB
15374f99ae name change, little better 2022-02-10 10:33:11 -08:00
foxsouns - SEE ME @ GITLAB
1d6bcb1f57 Update jb.json 2022-02-10 09:48:16 -08:00
foxsouns - SEE ME @ GITLAB
77dd0b027f roughly 2% of random lojban stuff 2022-02-10 00:30:14 -08:00
foxsouns - SEE ME @ GITLAB
902e7cf480 add lojban 2022-02-09 23:27:54 -08:00
foxsouns - SEE ME @ GITLAB
08d8922aad change name to the more sensical "patxu" 2022-02-09 22:50:19 -08:00
foxsouns - SEE ME @ GITLAB
79abba5124 Update jb.json
baby steps
2022-02-09 22:44:22 -08:00
foxsouns - SEE ME @ GITLAB
cadfb09193 copy en.json into jb.json 2022-02-09 22:39:09 -08:00
foxsouns - SEE ME @ GITLAB
401cd32d07 add jb (lojban) into supported languages list 2022-02-09 22:30:53 -08:00
PrivateBin Translator Bot
f3b14225ba New translations en.json (Finnish) 2022-01-31 15:52:29 +01:00
PrivateBin Translator Bot
3b472fdd41 New translations en.json (Finnish) 2022-01-31 14:40:40 +01:00
PrivateBin Translator Bot
bb3af09c6c New translations en.json (Finnish) 2022-01-31 13:37:59 +01:00
PrivateBin Translator Bot
57b03d438e New translations en.json (Finnish) 2022-01-31 12:35:57 +01:00
PrivateBin Translator Bot
1a7ac272eb New translations en.json (Finnish) 2022-01-31 10:59:17 +01:00
PrivateBin Translator Bot
1b2b183d7f New translations en.json (Finnish) 2022-01-31 10:02:10 +01:00
El RIDO
1f872167c1 Merge branch 'austinhuang0131-master' 2022-01-31 06:40:13 +01:00
El RIDO
29ffd25c18 apply suggestion of @r4sas 2022-01-30 21:42:24 +01:00
PrivateBin Translator Bot
d43dfdefdf New translations en.json (Finnish) 2022-01-30 21:21:23 +01:00
El RIDO
1d20eee169 readability 2022-01-26 05:28:29 +01:00
El RIDO
53c0e4976b document what the U type stands for 2022-01-26 05:26:47 +01:00
El RIDO
0333777a37 remove duplicate CLOB sanitation 2022-01-25 05:59:22 +01:00
El RIDO
f4438a0103 inserting CLOB absolutely requires a length argument
Co-authored-by: Austin Huang <im@austinhuang.me>
2022-01-24 21:44:20 +01:00
El RIDO
55db9426b9 Throws ORA-00942: table or view does not exist otherwise
Co-authored-by: Austin Huang <im@austinhuang.me>
2022-01-24 21:43:48 +01:00
El RIDO
535f038daa handle LIMIT in oci
Co-authored-by: Austin Huang <im@austinhuang.me>
2022-01-24 21:43:31 +01:00
El RIDO
0c4852c099 this fixes the comment display issue
Co-authored-by: Austin Huang <im@austinhuang.me>
2022-01-24 21:40:10 +01:00
El RIDO
b8e8755fb1 Basically it wants a non-empty catch statement
Co-authored-by: Austin Huang <im@austinhuang.me>
2022-01-24 21:36:18 +01:00
El RIDO
0b6af67b99 removed obsolete comment 2022-01-24 17:50:24 +01:00
El RIDO
56c54dd880 prefer switch statements for complex logic, all comparing the same variable 2022-01-24 17:48:27 +01:00
El RIDO
a8e1c33b54 stick to single convention of binding parameters 2022-01-24 17:26:09 +01:00
El RIDO
0cc2b67753 bindValue doesn't need the length 2022-01-23 21:45:22 +01:00
El RIDO
4f051fe5a5 revert regression 2022-01-23 21:31:40 +01:00
El RIDO
8d63921924 workaround bug in OCI PDO driver 2022-01-23 21:24:28 +01:00
El RIDO
0be55e05bf use quoted identifiers, tell MySQL to expect ANSI SQL 2022-01-23 20:59:02 +01:00
El RIDO
b133c2e233 sanitize both single rows and multiple ones 2022-01-23 07:32:28 +01:00
El RIDO
b54308a77e don't mangle non-arrays 2022-01-23 07:19:35 +01:00
El RIDO
47deaeb7ca use the correct function 2022-01-23 07:11:36 +01:00
El RIDO
83336b2949 documented changes for Postgres and Oracle 2022-01-23 07:11:06 +01:00
El RIDO
35ef64ff79 remove duplication, kudos @rugk 2022-01-22 22:11:49 +01:00
El RIDO
c725b4f0fe handle 'IF NOT EXISTS' differently in OCI 2022-01-22 21:29:39 +01:00
El RIDO
585d5db983 apply StyleCI recommendation 2022-01-22 08:47:34 +01:00
El RIDO
2182cdd44f generalize OCI handling of queries and results 2022-01-22 08:45:12 +01:00
Austin Huang
041ef7f7a5 Support OCI (Satisfy the CI) 2022-01-20 13:33:23 -05:00
Austin Huang
6a489d35ab Support OCI (Create table) 2022-01-20 09:15:10 -05:00
Austin Huang
ee99952d90 Support OCI (Read/Write) 2022-01-17 20:06:26 -05:00
El RIDO
4f09873f94 Merge branch 'crowdin-translation' 2022-01-10 21:28:26 +01:00
El RIDO
ca999b15ed limit files of StyleCI scan 2022-01-10 21:26:28 +01:00
El RIDO
eef4f8be3f exclude configuration files from StyleCI scan, causing 'Unterminated comment' false positive 2022-01-10 21:21:12 +01:00
El RIDO
ff19d052b7 disable StyleCI rule causing 'Unterminated comment' false positive 2022-01-10 21:11:59 +01:00
PrivateBin Translator Bot
e53090a937 New translations en.json (Catalan) 2022-01-06 18:06:35 +01:00
PrivateBin Translator Bot
74e1f18ae0 New translations en.json (Catalan) 2022-01-06 17:10:34 +01:00
PrivateBin Translator Bot
02fcc0ba53 New translations en.json (Catalan) 2022-01-06 13:18:29 +01:00
El RIDO
e36a94cfdd Merge branch 'php8-ci-yetagain' 2021-10-30 17:23:23 +02:00
El RIDO
b80b318e38 spaces 2021-10-30 17:23:09 +02:00
rugk
1fff4bf4d7 Also set author for merge commit
Follow-up of 41898282+github-actions[bot]@users.noreply.github.com again

In contrast to your suggestion, @elrido, I did use GitHubs bot account again. The mails won't spam anyone, and it's actually intended for such stuff.
Also, we get a proper avatar on GitHub's commit messages etc., and of course we know it is actually GitHubs (servers) that do this change.
2021-10-30 16:53:42 +02:00
El RIDO
20d39347c6 Merge pull request #852 from PrivateBin/php8-cifix
Set GitHub Bot as author for PHP8 merge commits
2021-10-24 11:04:40 +02:00
rugk
aa6e2f7631 Set GitHub Bot as author for PHP8 merge commits 2021-10-23 15:04:54 +02:00
El RIDO
3666db3b9a Merge pull request #850 from LMS235/patch-1
Update de.json
2021-10-20 12:40:26 +02:00
El RIDO
5ba8266eb8 Merge pull request #848 from PrivateBin/php8-cifix2
Fix PHP refresh pipeline merge
2021-10-14 12:40:37 +02:00
Florian
86c5dc9db9 Update de.json
Link is not translated as "Verknüpfung", that is a separate term.
2021-10-14 09:04:29 +02:00
rugk
af852927a9 Fix PHP refresh pipeline merge
See https://github.com/PrivateBin/PrivateBin/pull/847#issuecomment-942580850

Now merging the origin as master is not yet pulled.
2021-10-13 20:07:45 +02:00
El RIDO
0fb104b102 Merge pull request #847 from PrivateBin/patch-pipeline
Fix PHP8 pipeline
2021-10-13 19:33:06 +02:00
rugk
f6421c9c7c Fix PHP8 pipeline
As per https://github.com/PrivateBin/PrivateBin/pull/843#issuecomment-939526915

Co-Authored-By: El RIDO <elrido@gmx.net>
2021-10-11 17:45:42 +02:00
El RIDO
2d1552a345 Merge pull request #846 from PrivateBin/crowdin-translation
New Crowdin updates
2021-10-08 20:14:46 +02:00
PrivateBin Translator Bot
8085604385 New translations en.json (Czech) 2021-10-08 19:40:15 +02:00
El RIDO
31cdfc7be6 Merge pull request #845 from PrivateBin/crowdin-translation
New Crowdin updates
2021-10-08 19:12:13 +02:00
PrivateBin Translator Bot
307443dac3 New translations en.json (Czech) 2021-10-08 18:28:00 +02:00
El RIDO
c5fd6028b5 Merge pull request #843 from PrivateBin/php8-ci
Add CI for automatic PHP8 branch updates
2021-10-07 21:00:09 +02:00
rugk
c7cd450f9b Remove useless boilerplate comments 2021-10-06 20:19:03 +02:00
rugk
a988be7431 Add CI for automatic PHP8 updates
Adds a simple CI for pushing the master branches changes to the php8 branch.

Useful/discussed for https://github.com/PrivateBin/PrivateBin/issues/707
2021-10-06 20:13:09 +02:00
El RIDO
981304848f Merge pull request #840 from PrivateBin/ci-cache
Cache PHP modules, composer and NodeJS stuff in CI
2021-10-04 10:03:31 +02:00
El RIDO
51a590c3c7 Merge pull request #841 from PrivateBin/ci-problem-matchers
Setup CI problem matchers for PHP
2021-10-02 20:15:22 +02:00
rugk
f4e68fcc04 style: better YAML comments 2021-10-02 01:12:08 +02:00
rugk
f43a41c117 Update tests.yml 2021-10-02 01:07:57 +02:00
rugk
ab11fbeb47 Fix syntax error
Apparently in envs the OS etc. syntax is not supported, so we need to use it like this.
2021-10-02 01:01:24 +02:00
rugk
5f4fe52eab Use package-json instead of package-lock.json
for cache
2021-10-02 00:56:44 +02:00
rugk
b80732f8e2 Add caching for NodeJS 2021-10-02 00:55:08 +02:00
rugk
a372ee92e9 Fix wrong cache key 2021-10-02 00:43:54 +02:00
rugk
e2ae0da4e1 Style cleanup adding newlines
Seems to be the unofficial GitHub Actions YAML style and arguably makes things a lot more readable if you have a lot of steps…
2021-10-02 00:41:54 +02:00
rugk
3f7bceb862 Also cache PHP extensions
See https://github.com/shivammathur/cache-extensions#workflow
2021-10-02 00:38:21 +02:00
rugk
507a10adc5 Use composer.json instead of composer.lock
In a cache
2021-10-02 00:32:57 +02:00
rugk
a8f7840d25 Only restore cache from current date then 2021-10-02 00:29:48 +02:00
rugk
3ba6483bf3 Try caching composer stuff
Especially the GCM stuff may be quite large, so caching may be a good idea.

I tried following https://github.com/shivammathur/setup-php#cache-composer-dependencies
2021-10-02 00:27:57 +02:00
El RIDO
fba8384ac3 Merge pull request #836 from PrivateBin/crowdin-translation
New Crowdin updates
2021-09-28 05:59:13 +02:00
PrivateBin Translator Bot
def58480b3 New translations en.json (Occitan) 2021-09-27 10:21:34 +02:00
El RIDO
cb5ba022ba Merge pull request #832 from PrivateBin/crowdin-translation
New Crowdin updates
2021-09-04 09:09:00 +02:00
PrivateBin Translator Bot
c5c3a0e743 New translations en.json (Turkish) 2021-09-03 18:58:00 +02:00
PrivateBin Translator Bot
3c068cd6c3 New translations en.json (Turkish) 2021-09-03 17:57:02 +02:00
El RIDO
82f1431440 Merge branch 'conf-path-handling' 2021-08-19 19:33:19 +02:00
El RIDO
df2f5931cd improve readability, kudos @rugk 2021-08-19 19:28:52 +02:00
El RIDO
ff3b668958 apply StyleCI recommendation 2021-08-19 11:04:31 +02:00
El RIDO
eb10d4d35e be more flexible with configuration paths
1. only consider CONFIG_PATH environment variable, if non-empty
2. fall back to search in PATH (defined in index.php), if CONFIG_PATH doesn't contain a readable configuration file
2021-08-19 10:21:21 +02:00
El RIDO
18972ae0fa luckily the PHP ini parser doesn't interpret this as an empty block, replacing the one defined above 2021-08-19 10:18:08 +02:00
El RIDO
e7fb9ac54c Merge pull request #818 from PrivateBin/crowdin-translation
New Crowdin updates
2021-08-19 10:14:28 +02:00
El RIDO
08816a1d71 Merge pull request #823 from PrivateBin/release-clarify
Clarify download instruction from GitHub releases
2021-08-05 20:17:19 +02:00
rugk
ea663f7491 Clarify download instruction from GitHub releases
Fixes https://github.com/PrivateBin/PrivateBin/issues/822
2021-08-05 19:28:43 +02:00
PrivateBin Translator Bot
5f28acf629 New translations en.json (Lithuanian) 2021-07-08 22:59:50 +02:00
56 changed files with 9401 additions and 500 deletions

37
.github/workflows/refresh-php8.yml vendored Normal file
View File

@@ -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'

View File

@@ -2,6 +2,7 @@ name: Tests
on: [push]
jobs:
Composer:
runs-on: ubuntu-latest
steps:
@@ -11,43 +12,109 @@ jobs:
run: composer validate
- name: Install dependencies
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

View File

@@ -29,3 +29,9 @@ disabled:
- short_array_syntax
- single_line_after_imports
- unalign_equals
finder:
path:
- "lib/"
- "tpl/"
- "tst/"

View File

@@ -1,19 +1,20 @@
# PrivateBin version history
* **1.4 (not yet released)**
* ADDED: Translation for Estonian
* ADDED: Translations for Estonian 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)
* CHANGED: Language selection cookie only transmitted over HTTPS (#472)
* CHANGED: Upgrading libraries to: random_compat 2.0.20
* CHANGED: Upgrading libraries to: base-x 4.0.0, DOMpurify 2.3.6, ip-lib 1.18.0, jQuery 3.6.0, random_compat 2.0.21 & Showdown 2.0.0
* 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)
* **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)

View File

@@ -29,6 +29,7 @@ Sébastien Sauvage - original idea and main developer
* 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
@@ -53,3 +54,4 @@ Sébastien Sauvage - original idea and main developer
* whenwesober - Indonesian
* retiolus - Catalan
* sarnane - Estonian
* foxsouns - Lojban

View File

@@ -1,7 +1,7 @@
# Installation
**TL;DR:** Download the
[latest release archive](https://github.com/PrivateBin/PrivateBin/releases/latest)
[latest release archive](https://github.com/PrivateBin/PrivateBin/releases/latest) (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
@@ -190,8 +190,14 @@ CREATE TABLE prefix_config (
INSERT INTO prefix_config VALUES('VERSION', '1.3.5');
```
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

1
Procfile Normal file
View File

@@ -0,0 +1 @@
web: vendor/bin/heroku-php-apache2

View File

@@ -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'; base-uri 'self'; form-action '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' resource:; 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
@@ -161,7 +161,7 @@ class = Filesystem
[model_options]
dir = PATH "data"
[model]
;[model]
; example of a Google Cloud Storage configuration
;class = GoogleCloudStorage
;[model_options]

View File

@@ -25,12 +25,12 @@
},
"require" : {
"php" : "^5.6.0 || ^7.0 || ^8.0",
"paragonie/random_compat" : "2.0.20",
"paragonie/random_compat" : "2.0.21",
"yzalis/identicon" : "2.0.0",
"mlocati/ip-lib" : "1.14.0"
"mlocati/ip-lib" : "1.18.0"
},
"suggest" : {
"google/cloud-storage" : "1.23.1"
"google/cloud-storage" : "1.26.1"
},
"require-dev" : {
"phpunit/phpunit" : "^4.6 || ^5.0"

84
composer.lock generated
View File

@@ -4,20 +4,20 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "217f0ba9bdac1014a332a8ba390be949",
"content-hash": "fa52d4988bfe17d4b27e3a4789a1ec49",
"packages": [
{
"name": "mlocati/ip-lib",
"version": "1.14.0",
"version": "1.18.0",
"source": {
"type": "git",
"url": "https://github.com/mlocati/ip-lib.git",
"reference": "882bc0e115970a536b13bcfa59f312783fce08c8"
"reference": "c77bd0b1f3e3956c7e9661e75cb1f54ed67d95d2"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/mlocati/ip-lib/zipball/882bc0e115970a536b13bcfa59f312783fce08c8",
"reference": "882bc0e115970a536b13bcfa59f312783fce08c8",
"url": "https://api.github.com/repos/mlocati/ip-lib/zipball/c77bd0b1f3e3956c7e9661e75cb1f54ed67d95d2",
"reference": "c77bd0b1f3e3956c7e9661e75cb1f54ed67d95d2",
"shasum": ""
},
"require": {
@@ -25,8 +25,7 @@
},
"require-dev": {
"ext-pdo_sqlite": "*",
"phpunit/dbunit": "^1.4 || ^2 || ^3 || ^4",
"phpunit/phpunit": "^4.8 || ^5.7 || ^6.5"
"phpunit/phpunit": "^4.8 || ^5.7 || ^6.5 || ^7.5 || ^8.5 || ^9.5"
},
"type": "library",
"autoload": {
@@ -72,27 +71,27 @@
"type": "other"
}
],
"time": "2020-12-31T11:30:02+00:00"
"time": "2022-01-13T18:05:33+00:00"
},
{
"name": "paragonie/random_compat",
"version": "v2.0.20",
"version": "v2.0.21",
"source": {
"type": "git",
"url": "https://github.com/paragonie/random_compat.git",
"reference": "0f1f60250fccffeaf5dda91eea1c018aed1adc2a"
"reference": "96c132c7f2f7bc3230723b66e89f8f150b29d5ae"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/paragonie/random_compat/zipball/0f1f60250fccffeaf5dda91eea1c018aed1adc2a",
"reference": "0f1f60250fccffeaf5dda91eea1c018aed1adc2a",
"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."
@@ -121,7 +120,7 @@
"pseudorandom",
"random"
],
"time": "2021-04-17T09:33:01+00:00"
"time": "2022-02-16T17:07:03+00:00"
},
{
"name": "yzalis/identicon",
@@ -270,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": [
@@ -348,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": {
@@ -368,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": {
@@ -396,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": {
@@ -417,7 +417,8 @@
"phpdocumentor/reflection-common": "^2.0"
},
"require-dev": {
"ext-tokenizer": "*"
"ext-tokenizer": "*",
"psalm/phar": "^4.8"
},
"type": "library",
"extra": {
@@ -441,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",
@@ -1419,21 +1420,24 @@
},
{
"name": "symfony/polyfill-ctype",
"version": "v1.23.0",
"version": "v1.24.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-ctype.git",
"reference": "46cd95797e9df938fdd2b03693b5fca5e64b01ce"
"reference": "30885182c981ab175d4d034db0f6f469898070ab"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/46cd95797e9df938fdd2b03693b5fca5e64b01ce",
"reference": "46cd95797e9df938fdd2b03693b5fca5e64b01ce",
"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"
},
@@ -1491,20 +1495,20 @@
"type": "tidelift"
}
],
"time": "2021-02-19T12:13:01+00:00"
"time": "2021-10-20T20:35:02+00:00"
},
{
"name": "symfony/yaml",
"version": "v4.4.24",
"version": "v4.4.37",
"source": {
"type": "git",
"url": "https://github.com/symfony/yaml.git",
"reference": "8b6d1b97521e2f125039b3fcb4747584c6dfa0ef"
"reference": "d7f637cc0f0cc14beb0984f2bb50da560b271311"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/yaml/zipball/8b6d1b97521e2f125039b3fcb4747584c6dfa0ef",
"reference": "8b6d1b97521e2f125039b3fcb4747584c6dfa0ef",
"url": "https://api.github.com/repos/symfony/yaml/zipball/d7f637cc0f0cc14beb0984f2bb50da560b271311",
"reference": "d7f637cc0f0cc14beb0984f2bb50da560b271311",
"shasum": ""
},
"require": {
@@ -1559,7 +1563,7 @@
"type": "tidelift"
}
],
"time": "2021-05-16T09:52:47+00:00"
"time": "2022-01-24T20:11:01+00:00"
},
{
"name": "webmozart/assert",

View File

@@ -8,10 +8,10 @@
"%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.": [
"Espereu %d segon entre cada entrada. (singular)",
"Espereu %d segons entre cada entrada. (1r plural)",
"Espereu %d segons entre cada entrada. (2n plural)",
"Please wait %d seconds between each post. (3er 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.": "L'enganxat està limitat a %s de dades encriptades.",
"Invalid data.": "Dades no vàlides.",
@@ -20,8 +20,8 @@
"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.",
"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",
@@ -35,48 +35,48 @@
"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",

View File

@@ -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,45 +145,45 @@
"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.",
"Save paste": "Save paste"
"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"
}

View File

@@ -175,15 +175,15 @@
"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.",
"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"
}

189
i18n/fi.json Normal file
View File

@@ -0,0 +1,189 @@
{
"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"
}

189
i18n/jbo.json Normal file
View File

@@ -0,0 +1,189 @@
{
"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"
}

View File

@@ -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"],

View File

@@ -185,5 +185,5 @@
"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 trumpinimo įrankis gali atskleisti URL adrese jūsų iššifravimo raktą.",
"Save paste": "Save paste"
"Save paste": "Įrašyti įdėjimą"
}

View File

@@ -184,6 +184,6 @@
"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 qualquun mai li permet tanben daccedir a la nòta.",
"URL shortener may expose your decrypt key in URL.": "URL shortener may expose your decrypt key in URL.",
"Save paste": "Save paste"
"URL shortener may expose your decrypt key in URL.": "Los espleches dacorchiment dURL pòdon expausar la clau de deschiframent dins lURL.",
"Save paste": "Enregistrar lo tèxt"
}

View File

@@ -26,14 +26,14 @@
"%s requires a modern browser to work.": "%s requires a modern browser to work.",
"New": "Yeni",
"Send": "Gönder",
"Clone": "Clone",
"Clone": "Kopyala",
"Raw text": "Raw text",
"Expires": "Expires",
"Expires": "Süre Sonu",
"Burn after reading": "Burn after reading",
"Open discussion": "Open discussion",
"Open discussion": "ı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)",
@@ -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,33 +155,33 @@
"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.",

View File

@@ -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++]

View File

@@ -10,15 +10,15 @@ 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;
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.0');
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('./privatebin');

2
js/jquery-3.4.1.js vendored

File diff suppressed because one or more lines are too long

2
js/jquery-3.6.0.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@@ -601,7 +601,7 @@ jQuery.PrivateBin = (function($, RawDeflate) {
* @prop {string[]}
* @readonly
*/
const supportedLanguages = ['bg', 'ca', 'cs', 'de', 'es', 'et', 'fr', 'he', 'hu', 'id', 'it', 'lt', 'no', 'nl', 'pl', 'pt', 'oc', 'ru', 'sl', 'uk', 'zh'];
const supportedLanguages = ['bg', 'ca', 'cs', 'de', 'es', 'et', 'fr', 'he', 'hu', 'id', 'it', 'jbo', 'lt', 'no', 'nl', 'pl', 'pt', 'oc', 'ru', 'sl', 'uk', 'zh'];
/**
* built in language
@@ -785,6 +785,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);

File diff suppressed because one or more lines are too long

2
js/purify-2.3.6.js Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

2447
js/showdown-2.0.0.js Normal file

File diff suppressed because it is too large Load Diff

5157
js/showdown.js Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -44,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>.',
@@ -54,7 +54,7 @@ class Configuration
'urlshortener' => '',
'qrcode' => true,
'icon' => 'identicon',
'cspheader' => 'default-src \'none\'; base-uri \'self\'; form-action \'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\' resource:; 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',
@@ -101,10 +101,15 @@ class Configuration
*/
public function __construct()
{
$basePaths = array();
$config = array();
$basePath = (getenv('CONFIG_PATH') !== false ? getenv('CONFIG_PATH') : PATH . 'cfg') . DIRECTORY_SEPARATOR;
$configFile = $basePath . 'conf.php';
$configPath = getenv('CONFIG_PATH');
if ($configPath !== false && !empty($configPath)) {
$basePaths[] = $configPath;
}
$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) {
@@ -112,6 +117,8 @@ class Configuration
throw new Exception(I18n::_('PrivateBin requires configuration section [%s] to be present in configuration file.', $section), 2);
}
}
break;
}
}
$opts = '_options';

View File

@@ -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'],
@@ -200,8 +205,8 @@ class Database extends AbstractData
}
try {
return self::_exec(
'INSERT INTO ' . self::_sanitizeIdentifier('paste') .
' VALUES(?,?,?,?,?,?,?,?,?)',
'INSERT INTO "' . self::_sanitizeIdentifier('paste') .
'" VALUES(?,?,?,?,?,?,?,?,?)',
array(
$pasteid,
$isVersion1 ? $paste['data'] : Json::encode($paste),
@@ -235,8 +240,8 @@ class Database extends AbstractData
self::$_cache[$pasteid] = false;
try {
$paste = self::_select(
'SELECT * FROM ' . self::_sanitizeIdentifier('paste') .
' WHERE dataid = ?', array($pasteid), true
'SELECT * FROM "' . self::_sanitizeIdentifier('paste') .
'" WHERE "dataid" = ?', array($pasteid), true
);
} catch (Exception $e) {
$paste = false;
@@ -297,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)
@@ -357,8 +362,8 @@ class Database extends AbstractData
}
try {
return self::_exec(
'INSERT INTO ' . self::_sanitizeIdentifier('comment') .
' VALUES(?,?,?,?,?,?,?)',
'INSERT INTO "' . self::_sanitizeIdentifier('comment') .
'" VALUES(?,?,?,?,?,?,?)',
array(
$commentid,
$pasteid,
@@ -384,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']);
@@ -429,8 +434,8 @@ class Database extends AbstractData
{
try {
return (bool) self::_select(
'SELECT dataid FROM ' . self::_sanitizeIdentifier('comment') .
' WHERE pasteid = ? AND parentid = ? AND dataid = ?',
'SELECT "dataid" FROM "' . self::_sanitizeIdentifier('comment') .
'" WHERE "pasteid" = ? AND "parentid" = ? AND "dataid" = ?',
array($pasteid, $parentid, $commentid), true
);
} catch (Exception $e) {
@@ -458,8 +463,8 @@ class Database extends AbstractData
}
}
return self::_exec(
'UPDATE ' . self::_sanitizeIdentifier('config') .
' SET value = ? WHERE id = ?',
'UPDATE "' . self::_sanitizeIdentifier('config') .
'" SET "value" = ? WHERE "id" = ?',
array($value, strtoupper($namespace))
);
}
@@ -479,8 +484,8 @@ class Database extends AbstractData
if ($value === '') {
// initialize the row, so that setValue can rely on UPDATE queries
self::_exec(
'INSERT INTO ' . self::_sanitizeIdentifier('config') .
' VALUES(?,?)',
'INSERT INTO "' . self::_sanitizeIdentifier('config') .
'" VALUES(?,?)',
array($configKey, '')
);
@@ -517,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'];
}
@@ -542,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;
}
@@ -562,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;
}
@@ -598,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';
@@ -614,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(
@@ -652,8 +683,8 @@ class Database extends AbstractData
{
try {
$row = self::_select(
'SELECT value FROM ' . self::_sanitizeIdentifier('config') .
' WHERE id = ?', array($key), true
'SELECT "value" FROM "' . self::_sanitizeIdentifier('config') .
'" WHERE "id" = ?', array($key), true
);
} catch (PDOException $e) {
return '';
@@ -672,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 {
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);
}
@@ -683,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
@@ -691,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
@@ -705,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';
}
}
/**
@@ -719,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 )"
);
}
@@ -744,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 );"
'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(
'CREATE INDEX IF NOT EXISTS comment_parent ON ' .
self::_sanitizeIdentifier('comment') . '(pasteid);'
'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")'
);
}
}
/**
@@ -768,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
*
@@ -807,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':
@@ -852,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')
);
}

View File

@@ -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];
}
}
@@ -324,6 +324,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);

View File

@@ -41,7 +41,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) :
?>
@@ -55,7 +55,7 @@ if ($ZEROBINCOMPATIBILITY) :
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/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>
<?php
@@ -66,13 +66,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.0.js" integrity="sha512-UB9jpMTOJLSnVzePuqlSGT34G70wEGqtIWabMeAh+Drnj4/uQ8rFkFn1zkN9vkWp/7nA51U2LmP23H5MJvBXsw==" 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-lJwDAY69TQuYQZ7FjUFPfhgYeZ2L6y5bmGt1hR+d3kMm2sddivGr7ZDdLLSe/CBgn1JrsKMj3th9dPyXN3dLHw==" crossorigin="anonymous"></script>
<script type="text/javascript" data-cfasync="false" src="js/privatebin.js?<?php echo rawurlencode($VERSION); ?>" integrity="sha512-PTOcxIWIPWCnb5vC4fmQDMqYGerwsu3AndVyPxn9NlQffIWYMPf/p28Z9SIygXsmcYjmTRmUiW5y7df63mNTfg==" 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" />

View File

@@ -20,7 +20,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):
?>
@@ -34,7 +34,7 @@ if ($ZEROBINCOMPATIBILITY):
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/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 +44,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.0.js" integrity="sha512-UB9jpMTOJLSnVzePuqlSGT34G70wEGqtIWabMeAh+Drnj4/uQ8rFkFn1zkN9vkWp/7nA51U2LmP23H5MJvBXsw==" 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-lJwDAY69TQuYQZ7FjUFPfhgYeZ2L6y5bmGt1hR+d3kMm2sddivGr7ZDdLLSe/CBgn1JrsKMj3th9dPyXN3dLHw==" crossorigin="anonymous"></script>
<script type="text/javascript" data-cfasync="false" src="js/privatebin.js?<?php echo rawurlencode($VERSION); ?>" integrity="sha512-PTOcxIWIPWCnb5vC4fmQDMqYGerwsu3AndVyPxn9NlQffIWYMPf/p28Z9SIygXsmcYjmTRmUiW5y7df63mNTfg==" 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" />

View File

@@ -304,7 +304,7 @@ class StorageObjectStub extends StorageObject
$this->_generation = $generation;
$this->_info = $info;
$this->_connection = $connection;
$timeCreated = new Datetime();
$timeCreated = new DateTime();
$this->_info['metadata']['timeCreated'] = $timeCreated->format('Y-m-d\TH:i:s.u\Z');
}

View File

@@ -388,4 +388,16 @@ class DatabaseTest extends PHPUnit_Framework_TestCase
$this->assertEquals(Controller::VERSION, $result['value']);
Helper::rmDir($this->_path);
}
public function testOciClob()
{
$int = (int) random_bytes(1);
$string = random_bytes(10);
$clob = fopen('php://memory', 'r+');
fwrite($clob, $string);
rewind($clob);
$this->assertEquals($int, Database::_sanitizeClob($int));
$this->assertEquals($string, Database::_sanitizeClob($string));
$this->assertEquals($string, Database::_sanitizeClob($clob));
}
}

View File

@@ -145,7 +145,7 @@ class I18nTest extends PHPUnit_Framework_TestCase
{
$_SERVER['HTTP_ACCEPT_LANGUAGE'] = '*';
I18n::loadTranslations();
$this->assertTrue(strlen(I18n::_('en')) == 2, 'browser language any');
$this->assertTrue(strlen(I18n::_('en')) >= 2, 'browser language any');
}
public function testVariableInjection()

View File

@@ -12,6 +12,7 @@ return array(
'IPLib\\Address\\IPv6' => $vendorDir . '/mlocati/ip-lib/src/Address/IPv6.php',
'IPLib\\Address\\Type' => $vendorDir . '/mlocati/ip-lib/src/Address/Type.php',
'IPLib\\Factory' => $vendorDir . '/mlocati/ip-lib/src/Factory.php',
'IPLib\\ParseStringFlag' => $vendorDir . '/mlocati/ip-lib/src/ParseStringFlag.php',
'IPLib\\Range\\AbstractRange' => $vendorDir . '/mlocati/ip-lib/src/Range/AbstractRange.php',
'IPLib\\Range\\Pattern' => $vendorDir . '/mlocati/ip-lib/src/Range/Pattern.php',
'IPLib\\Range\\RangeInterface' => $vendorDir . '/mlocati/ip-lib/src/Range/RangeInterface.php',
@@ -19,7 +20,8 @@ return array(
'IPLib\\Range\\Subnet' => $vendorDir . '/mlocati/ip-lib/src/Range/Subnet.php',
'IPLib\\Range\\Type' => $vendorDir . '/mlocati/ip-lib/src/Range/Type.php',
'IPLib\\Service\\BinaryMath' => $vendorDir . '/mlocati/ip-lib/src/Service/BinaryMath.php',
'IPLib\\Service\\RangesFromBounradyCalculator' => $vendorDir . '/mlocati/ip-lib/src/Service/RangesFromBounradyCalculator.php',
'IPLib\\Service\\RangesFromBoundaryCalculator' => $vendorDir . '/mlocati/ip-lib/src/Service/RangesFromBoundaryCalculator.php',
'IPLib\\Service\\UnsignedIntegerMath' => $vendorDir . '/mlocati/ip-lib/src/Service/UnsignedIntegerMath.php',
'Identicon\\Generator\\BaseGenerator' => $vendorDir . '/yzalis/identicon/src/Identicon/Generator/BaseGenerator.php',
'Identicon\\Generator\\GdGenerator' => $vendorDir . '/yzalis/identicon/src/Identicon/Generator/GdGenerator.php',
'Identicon\\Generator\\GeneratorInterface' => $vendorDir . '/yzalis/identicon/src/Identicon/Generator/GeneratorInterface.php',
@@ -41,7 +43,6 @@ return array(
'PrivateBin\\Model\\Comment' => $baseDir . '/lib/Model/Comment.php',
'PrivateBin\\Model\\Paste' => $baseDir . '/lib/Model/Paste.php',
'PrivateBin\\Persistence\\AbstractPersistence' => $baseDir . '/lib/Persistence/AbstractPersistence.php',
'PrivateBin\\Persistence\\DataStore' => $baseDir . '/lib/Persistence/DataStore.php',
'PrivateBin\\Persistence\\PurgeLimiter' => $baseDir . '/lib/Persistence/PurgeLimiter.php',
'PrivateBin\\Persistence\\ServerSalt' => $baseDir . '/lib/Persistence/ServerSalt.php',
'PrivateBin\\Persistence\\TrafficLimiter' => $baseDir . '/lib/Persistence/TrafficLimiter.php',

View File

@@ -44,6 +44,7 @@ class ComposerStaticInitDontChange
'IPLib\\Address\\IPv6' => __DIR__ . '/..' . '/mlocati/ip-lib/src/Address/IPv6.php',
'IPLib\\Address\\Type' => __DIR__ . '/..' . '/mlocati/ip-lib/src/Address/Type.php',
'IPLib\\Factory' => __DIR__ . '/..' . '/mlocati/ip-lib/src/Factory.php',
'IPLib\\ParseStringFlag' => __DIR__ . '/..' . '/mlocati/ip-lib/src/ParseStringFlag.php',
'IPLib\\Range\\AbstractRange' => __DIR__ . '/..' . '/mlocati/ip-lib/src/Range/AbstractRange.php',
'IPLib\\Range\\Pattern' => __DIR__ . '/..' . '/mlocati/ip-lib/src/Range/Pattern.php',
'IPLib\\Range\\RangeInterface' => __DIR__ . '/..' . '/mlocati/ip-lib/src/Range/RangeInterface.php',
@@ -51,7 +52,8 @@ class ComposerStaticInitDontChange
'IPLib\\Range\\Subnet' => __DIR__ . '/..' . '/mlocati/ip-lib/src/Range/Subnet.php',
'IPLib\\Range\\Type' => __DIR__ . '/..' . '/mlocati/ip-lib/src/Range/Type.php',
'IPLib\\Service\\BinaryMath' => __DIR__ . '/..' . '/mlocati/ip-lib/src/Service/BinaryMath.php',
'IPLib\\Service\\RangesFromBounradyCalculator' => __DIR__ . '/..' . '/mlocati/ip-lib/src/Service/RangesFromBounradyCalculator.php',
'IPLib\\Service\\RangesFromBoundaryCalculator' => __DIR__ . '/..' . '/mlocati/ip-lib/src/Service/RangesFromBoundaryCalculator.php',
'IPLib\\Service\\UnsignedIntegerMath' => __DIR__ . '/..' . '/mlocati/ip-lib/src/Service/UnsignedIntegerMath.php',
'Identicon\\Generator\\BaseGenerator' => __DIR__ . '/..' . '/yzalis/identicon/src/Identicon/Generator/BaseGenerator.php',
'Identicon\\Generator\\GdGenerator' => __DIR__ . '/..' . '/yzalis/identicon/src/Identicon/Generator/GdGenerator.php',
'Identicon\\Generator\\GeneratorInterface' => __DIR__ . '/..' . '/yzalis/identicon/src/Identicon/Generator/GeneratorInterface.php',
@@ -73,7 +75,6 @@ class ComposerStaticInitDontChange
'PrivateBin\\Model\\Comment' => __DIR__ . '/../..' . '/lib/Model/Comment.php',
'PrivateBin\\Model\\Paste' => __DIR__ . '/../..' . '/lib/Model/Paste.php',
'PrivateBin\\Persistence\\AbstractPersistence' => __DIR__ . '/../..' . '/lib/Persistence/AbstractPersistence.php',
'PrivateBin\\Persistence\\DataStore' => __DIR__ . '/../..' . '/lib/Persistence/DataStore.php',
'PrivateBin\\Persistence\\PurgeLimiter' => __DIR__ . '/../..' . '/lib/Persistence/PurgeLimiter.php',
'PrivateBin\\Persistence\\ServerSalt' => __DIR__ . '/../..' . '/lib/Persistence/ServerSalt.php',
'PrivateBin\\Persistence\\TrafficLimiter' => __DIR__ . '/../..' . '/lib/Persistence/TrafficLimiter.php',

View File

@@ -21,6 +21,8 @@ interface AddressInterface
*
* @return int
*
* @since 1.14.0
*
* @example 32 for IPv4
* @example 128 for IPv6
*/
@@ -51,6 +53,8 @@ interface AddressInterface
*
* @return string
*
* @since 1.14.0
*
* @example For localhost: For IPv4 you'll get '01111111000000000000000000000001' (32 digits), for IPv6 '00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001' (128 digits)
*/
public function getBits();
@@ -66,6 +70,8 @@ interface AddressInterface
* Get the default RFC reserved range type.
*
* @return int One of the \IPLib\Range\Type::T_... constants
*
* @since 1.5.0
*/
public static function getDefaultReservedRangeType();
@@ -73,6 +79,8 @@ interface AddressInterface
* Get the RFC reserved ranges (except the ones of type getDefaultReservedRangeType).
*
* @return \IPLib\Address\AssignedRange[] ranges are sorted
*
* @since 1.5.0
*/
public static function getReservedRanges();
@@ -99,10 +107,28 @@ interface AddressInterface
*/
public function matches(RangeInterface $range);
/**
* Get the address at a certain distance from this address.
*
* @param int $n the distance of the address (can be negative)
*
* @return \IPLib\Address\AddressInterface|null return NULL if $n is not an integer or if the final address would be invalid
*
* @since 1.15.0
*
* @example passing 1 to the address 127.0.0.1 will result in 127.0.0.2
* @example passing -1 to the address 127.0.0.1 will result in 127.0.0.0
* @example passing -1 to the address 0.0.0.0 will result in NULL
*/
public function getAddressAtOffset($n);
/**
* Get the address right after this IP address (if available).
*
* @return \IPLib\Address\AddressInterface|null
*
* @see \IPLib\Address\AddressInterface::getAddressAtOffset()
* @since 1.4.0
*/
public function getNextAddress();
@@ -110,6 +136,9 @@ interface AddressInterface
* Get the address right before this IP address (if available).
*
* @return \IPLib\Address\AddressInterface|null
*
* @see \IPLib\Address\AddressInterface::getAddressAtOffset()
* @since 1.4.0
*/
public function getPreviousAddress();
@@ -118,6 +147,8 @@ interface AddressInterface
*
* @return string
*
* @since 1.12.0
*
* @example for IPv4 it returns something like x.x.x.x.in-addr.arpa
* @example for IPv6 it returns something like x.x.x.x..x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.ip6.arpa
*/

View File

@@ -6,6 +6,8 @@ use IPLib\Range\RangeInterface;
/**
* Represents an IP address range with an assigned range type.
*
* @since 1.5.0
*/
class AssignedRange
{

View File

@@ -2,6 +2,7 @@
namespace IPLib\Address;
use IPLib\ParseStringFlag;
use IPLib\Range\RangeInterface;
use IPLib\Range\Subnet;
use IPLib\Range\Type as RangeType;
@@ -46,7 +47,7 @@ class IPv4 implements AddressInterface
*
* @var array|null
*/
private static $reservedRanges = null;
private static $reservedRanges;
/**
* Initializes the instance.
@@ -82,52 +83,94 @@ class IPv4 implements AddressInterface
}
/**
* Parse a string and returns an IPv4 instance if the string is valid, or null otherwise.
* @deprecated since 1.17.0: use the parseString() method instead.
* For upgrading:
* - if $mayIncludePort is true, use the ParseStringFlag::MAY_INCLUDE_PORT flag
* - if $supportNonDecimalIPv4 is true, use the ParseStringFlag::IPV4_MAYBE_NON_DECIMAL flag
*
* @param string|mixed $address the address to parse
* @param bool $mayIncludePort set to false to avoid parsing addresses with ports
* @param bool $supportNonDecimalIPv4 set to true to support parsing non decimal (that is, octal and hexadecimal) IPv4 addresses
* @param bool $mayIncludePort
* @param bool $supportNonDecimalIPv4
*
* @return static|null
*
* @see \IPLib\Address\IPv4::parseString()
* @since 1.1.0 added the $mayIncludePort argument
* @since 1.10.0 added the $supportNonDecimalIPv4 argument
*/
public static function fromString($address, $mayIncludePort = true, $supportNonDecimalIPv4 = false)
{
if (!is_string($address) || !strpos($address, '.')) {
return static::parseString($address, 0 | ($mayIncludePort ? ParseStringFlag::MAY_INCLUDE_PORT : 0) | ($supportNonDecimalIPv4 ? ParseStringFlag::IPV4_MAYBE_NON_DECIMAL : 0));
}
/**
* Parse a string and returns an IPv4 instance if the string is valid, or null otherwise.
*
* @param string|mixed $address the address to parse
* @param int $flags A combination or zero or more flags
*
* @return static|null
*
* @see \IPLib\ParseStringFlag
* @since 1.17.0
*/
public static function parseString($address, $flags = 0)
{
if (!is_string($address)) {
return null;
}
$rxChunk = '0?[0-9]{1,3}';
if ($supportNonDecimalIPv4) {
$rxChunk = "(?:0[Xx]0*[0-9A-Fa-f]{1,2})|(?:{$rxChunk})";
$flags = (int) $flags;
$matches = null;
if ($flags & ParseStringFlag::ADDRESS_MAYBE_RDNS) {
if (preg_match('/^([12]?[0-9]{1,2}\.[12]?[0-9]{1,2}\.[12]?[0-9]{1,2}\.[12]?[0-9]{1,2})\.in-addr\.arpa\.?$/i', $address, $matches)) {
$address = implode('.', array_reverse(explode('.', $matches[1])));
$flags = $flags & ~(ParseStringFlag::IPV4_MAYBE_NON_DECIMAL | ParseStringFlag::IPV4ADDRESS_MAYBE_NON_QUAD_DOTTED);
}
$rx = "0*?({$rxChunk})\.0*?({$rxChunk})\.0*?({$rxChunk})\.0*?({$rxChunk})";
if ($mayIncludePort) {
}
if ($flags & ParseStringFlag::IPV4ADDRESS_MAYBE_NON_QUAD_DOTTED) {
if (strpos($address, '.') === 0) {
return null;
}
$lengthNonHex = '{1,11}';
$lengthHex = '{1,8}';
$chunk234Optional = true;
} else {
if (!strpos($address, '.')) {
return null;
}
$lengthNonHex = '{1,3}';
$lengthHex = '{1,2}';
$chunk234Optional = false;
}
$rxChunk1 = "0?[0-9]{$lengthNonHex}";
if ($flags & ParseStringFlag::IPV4_MAYBE_NON_DECIMAL) {
$rxChunk1 = "(?:0[Xx]0*[0-9A-Fa-f]{$lengthHex})|(?:{$rxChunk1})";
$onlyDecimal = false;
} else {
$onlyDecimal = true;
}
$rxChunk1 = "0*?({$rxChunk1})";
$rxChunk234 = "\.{$rxChunk1}";
if ($chunk234Optional) {
$rxChunk234 = "(?:{$rxChunk234})?";
}
$rx = "{$rxChunk1}{$rxChunk234}{$rxChunk234}{$rxChunk234}";
if ($flags & ParseStringFlag::MAY_INCLUDE_PORT) {
$rx .= '(?::\d+)?';
}
$matches = null;
if (!preg_match('/^' . $rx . '$/', $address, $matches)) {
return null;
}
$math = new \IPLib\Service\UnsignedIntegerMath();
$nums = array();
for ($i = 1; $i <= 4; $i++) {
$s = $matches[$i];
if ($supportNonDecimalIPv4) {
if (stripos($s, '0x') === 0) {
$n = hexdec(substr($s, 2));
} elseif ($s[0] === '0') {
if (!preg_match('/^[0-7]+$/', $s)) {
$maxChunkIndex = count($matches) - 1;
for ($i = 1; $i <= $maxChunkIndex; $i++) {
$numBytes = $i === $maxChunkIndex ? 5 - $i : 1;
$chunkBytes = $math->getBytes($matches[$i], $numBytes, $onlyDecimal);
if ($chunkBytes === null) {
return null;
}
$n = octdec(substr($s, 1));
} else {
$n = (int) $s;
}
} else {
$n = (int) $s;
}
if ($n < 0 || $n > 255) {
return null;
}
$nums[] = (string) $n;
$nums = array_merge($nums, $chunkBytes);
}
return new static(implode('.', $nums));
@@ -179,6 +222,8 @@ class IPv4 implements AddressInterface
*
* @return string
*
* @since 1.10.0
*
* @example if $long == false: if the decimal representation is '0.7.8.255': '0.7.010.0377'
* @example if $long == true: if the decimal representation is '0.7.8.255': '0000.0007.0010.0377'
*/
@@ -203,6 +248,8 @@ class IPv4 implements AddressInterface
*
* @return string
*
* @since 1.10.0
*
* @example if $long == false: if the decimal representation is '0.9.10.255': '0.9.0xa.0xff'
* @example if $long == true: if the decimal representation is '0.9.10.255': '0x00.0x09.0x0a.0xff'
*/
@@ -318,10 +365,10 @@ class IPv4 implements AddressInterface
$exceptions = array();
if (isset($data[1])) {
foreach ($data[1] as $exceptionRange => $exceptionType) {
$exceptions[] = new AssignedRange(Subnet::fromString($exceptionRange), $exceptionType);
$exceptions[] = new AssignedRange(Subnet::parseString($exceptionRange), $exceptionType);
}
}
$reservedRanges[] = new AssignedRange(Subnet::fromString($range), $data[0], $exceptions);
$reservedRanges[] = new AssignedRange(Subnet::parseString($range), $data[0], $exceptions);
}
self::$reservedRanges = $reservedRanges;
}
@@ -359,13 +406,15 @@ class IPv4 implements AddressInterface
{
$myBytes = $this->getBytes();
return IPv6::fromString('2002:' . sprintf('%02x', $myBytes[0]) . sprintf('%02x', $myBytes[1]) . ':' . sprintf('%02x', $myBytes[2]) . sprintf('%02x', $myBytes[3]) . '::');
return IPv6::parseString('2002:' . sprintf('%02x', $myBytes[0]) . sprintf('%02x', $myBytes[1]) . ':' . sprintf('%02x', $myBytes[2]) . sprintf('%02x', $myBytes[3]) . '::');
}
/**
* Create an IPv6 representation of this address (in IPv6 IPv4-mapped notation).
*
* @return \IPLib\Address\IPv6
*
* @since 1.11.0
*/
public function toIPv6IPv4Mapped()
{
@@ -400,6 +449,37 @@ class IPv4 implements AddressInterface
return $range->contains($this);
}
/**
* {@inheritdoc}
*
* @see \IPLib\Address\AddressInterface::getAddressAtOffset()
*/
public function getAddressAtOffset($n)
{
if (!is_int($n)) {
return null;
}
$boundary = 256;
$mod = $n;
$bytes = $this->getBytes();
for ($i = count($bytes) - 1; $i >= 0; $i--) {
$tmp = ($bytes[$i] + $mod) % $boundary;
$mod = (int) floor(($bytes[$i] + $mod) / $boundary);
if ($tmp < 0) {
$tmp += $boundary;
}
$bytes[$i] = $tmp;
}
if ($mod !== 0) {
return null;
}
return static::fromBytes($bytes);
}
/**
* {@inheritdoc}
*
@@ -407,22 +487,7 @@ class IPv4 implements AddressInterface
*/
public function getNextAddress()
{
$overflow = false;
$bytes = $this->getBytes();
for ($i = count($bytes) - 1; $i >= 0; $i--) {
if ($bytes[$i] === 255) {
if ($i === 0) {
$overflow = true;
break;
}
$bytes[$i] = 0;
} else {
$bytes[$i]++;
break;
}
}
return $overflow ? null : static::fromBytes($bytes);
return $this->getAddressAtOffset(1);
}
/**
@@ -432,22 +497,7 @@ class IPv4 implements AddressInterface
*/
public function getPreviousAddress()
{
$overflow = false;
$bytes = $this->getBytes();
for ($i = count($bytes) - 1; $i >= 0; $i--) {
if ($bytes[$i] === 0) {
if ($i === 0) {
$overflow = true;
break;
}
$bytes[$i] = 255;
} else {
$bytes[$i]--;
break;
}
}
return $overflow ? null : static::fromBytes($bytes);
return $this->getAddressAtOffset(-1);
}
/**

View File

@@ -2,6 +2,7 @@
namespace IPLib\Address;
use IPLib\ParseStringFlag;
use IPLib\Range\RangeInterface;
use IPLib\Range\Subnet;
use IPLib\Range\Type as RangeType;
@@ -55,7 +56,7 @@ class IPv6 implements AddressInterface
*
* @var array|null
*/
private static $reservedRanges = null;
private static $reservedRanges;
/**
* Initializes the instance.
@@ -92,32 +93,69 @@ class IPv6 implements AddressInterface
}
/**
* Parse a string and returns an IPv6 instance if the string is valid, or null otherwise.
* @deprecated since 1.17.0: use the parseString() method instead.
* For upgrading:
* - if $mayIncludePort is true, use the ParseStringFlag::MAY_INCLUDE_PORT flag
* - if $mayIncludeZoneID is true, use the ParseStringFlag::MAY_INCLUDE_ZONEID flag
*
* @param string|mixed $address the address to parse
* @param bool $mayIncludePort set to false to avoid parsing addresses with ports
* @param bool $mayIncludeZoneID set to false to avoid parsing addresses with zone IDs (see RFC 4007)
* @param string|mixed $address
* @param bool $mayIncludePort
* @param bool $mayIncludeZoneID
*
* @return static|null
*
* @see \IPLib\Address\IPv6::parseString()
* @since 1.1.0 added the $mayIncludePort argument
* @since 1.3.0 added the $mayIncludeZoneID argument
*/
public static function fromString($address, $mayIncludePort = true, $mayIncludeZoneID = true)
{
return static::parseString($address, 0 | ($mayIncludePort ? ParseStringFlag::MAY_INCLUDE_PORT : 0) | ($mayIncludeZoneID ? ParseStringFlag::MAY_INCLUDE_ZONEID : 0));
}
/**
* Parse a string and returns an IPv6 instance if the string is valid, or null otherwise.
*
* @param string|mixed $address the address to parse
* @param int $flags A combination or zero or more flags
*
* @return static|null
*
* @see \IPLib\ParseStringFlag
* @since 1.17.0
*/
public static function parseString($address, $flags = 0)
{
if (!is_string($address)) {
return null;
}
$matches = null;
$flags = (int) $flags;
if ($flags & ParseStringFlag::ADDRESS_MAYBE_RDNS) {
if (preg_match('/^([0-9a-f](?:\.[0-9a-f]){31})\.ip6\.arpa\.?/i', $address, $matches)) {
$nibbles = array_reverse(explode('.', $matches[1]));
$quibbles = array();
foreach (array_chunk($nibbles, 4) as $n) {
$quibbles[] = implode('', $n);
}
$address = implode(':', $quibbles);
}
}
$result = null;
if (is_string($address) && strpos($address, ':') !== false && strpos($address, ':::') === false) {
$matches = null;
if ($mayIncludePort && $address[0] === '[' && preg_match('/^\[(.+)]:\d+$/', $address, $matches)) {
if ($flags & ParseStringFlag::MAY_INCLUDE_PORT && $address[0] === '[' && preg_match('/^\[(.+)]:\d+$/', $address, $matches)) {
$address = $matches[1];
}
if ($mayIncludeZoneID) {
if ($flags & ParseStringFlag::MAY_INCLUDE_ZONEID) {
$percentagePos = strpos($address, '%');
if ($percentagePos > 0) {
$address = substr($address, 0, $percentagePos);
}
}
if (preg_match('/^((?:[0-9a-f]*:+)+)(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})$/i', $address, $matches)) {
$address6 = static::fromString($matches[1] . '0:0', false);
$address6 = static::parseString($matches[1] . '0:0');
if ($address6 !== null) {
$address4 = IPv4::fromString($matches[2], false);
$address4 = IPv4::parseString($matches[2]);
if ($address4 !== null) {
$bytes4 = $address4->getBytes();
$address6->longAddress = substr($address6->longAddress, 0, -9) . sprintf('%02x%02x:%02x%02x', $bytes4[0], $bytes4[1], $bytes4[2], $bytes4[3]);
@@ -401,10 +439,10 @@ class IPv6 implements AddressInterface
$exceptions = array();
if (isset($data[1])) {
foreach ($data[1] as $exceptionRange => $exceptionType) {
$exceptions[] = new AssignedRange(Subnet::fromString($exceptionRange), $exceptionType);
$exceptions[] = new AssignedRange(Subnet::parseString($exceptionRange), $exceptionType);
}
}
$reservedRanges[] = new AssignedRange(Subnet::fromString($range), $data[0], $exceptions);
$reservedRanges[] = new AssignedRange(Subnet::parseString($range), $data[0], $exceptions);
}
self::$reservedRanges = $reservedRanges;
}
@@ -471,6 +509,7 @@ class IPv6 implements AddressInterface
* @example '0000:0000:0000:0000:0000:0000:013.001.068.003' when $ipV6Long and $ipV4Long are true
*
* @see https://tools.ietf.org/html/rfc4291#section-2.2 point 3.
* @since 1.9.0
*/
public function toMixedIPv6IPv4String($ipV6Long = false, $ipV4Long = false)
{
@@ -503,6 +542,37 @@ class IPv6 implements AddressInterface
return $range->contains($this);
}
/**
* {@inheritdoc}
*
* @see \IPLib\Address\AddressInterface::getAddressAtOffset()
*/
public function getAddressAtOffset($n)
{
if (!is_int($n)) {
return null;
}
$boundary = 0x10000;
$mod = $n;
$words = $this->getWords();
for ($i = count($words) - 1; $i >= 0; $i--) {
$tmp = ($words[$i] + $mod) % $boundary;
$mod = (int) floor(($words[$i] + $mod) / $boundary);
if ($tmp < 0) {
$tmp += $boundary;
}
$words[$i] = $tmp;
}
if ($mod !== 0) {
return null;
}
return static::fromWords($words);
}
/**
* {@inheritdoc}
*
@@ -510,22 +580,7 @@ class IPv6 implements AddressInterface
*/
public function getNextAddress()
{
$overflow = false;
$words = $this->getWords();
for ($i = count($words) - 1; $i >= 0; $i--) {
if ($words[$i] === 0xffff) {
if ($i === 0) {
$overflow = true;
break;
}
$words[$i] = 0;
} else {
$words[$i]++;
break;
}
}
return $overflow ? null : static::fromWords($words);
return $this->getAddressAtOffset(1);
}
/**
@@ -535,22 +590,7 @@ class IPv6 implements AddressInterface
*/
public function getPreviousAddress()
{
$overflow = false;
$words = $this->getWords();
for ($i = count($words) - 1; $i >= 0; $i--) {
if ($words[$i] === 0) {
if ($i === 0) {
$overflow = true;
break;
}
$words[$i] = 0xffff;
} else {
$words[$i]--;
break;
}
}
return $overflow ? null : static::fromWords($words);
return $this->getAddressAtOffset(-1);
}
/**

View File

@@ -27,6 +27,8 @@ class Type
* @param int $type
*
* @return string
*
* @since 1.1.0
*/
public static function getName($type)
{

View File

@@ -4,7 +4,7 @@ namespace IPLib;
use IPLib\Address\AddressInterface;
use IPLib\Range\Subnet;
use IPLib\Service\RangesFromBounradyCalculator;
use IPLib\Service\RangesFromBoundaryCalculator;
/**
* Factory methods to build class instances.
@@ -12,23 +12,48 @@ use IPLib\Service\RangesFromBounradyCalculator;
class Factory
{
/**
* Parse an IP address string.
* @deprecated since 1.17.0: use the parseAddressString() method instead.
* For upgrading:
* - if $mayIncludePort is true, use the ParseStringFlag::MAY_INCLUDE_PORT flag
* - if $mayIncludeZoneID is true, use the ParseStringFlag::MAY_INCLUDE_ZONEID flag
* - if $supportNonDecimalIPv4 is true, use the ParseStringFlag::IPV4_MAYBE_NON_DECIMAL flag
*
* @param string $address the address to parse
* @param bool $mayIncludePort set to false to avoid parsing addresses with ports
* @param bool $mayIncludeZoneID set to false to avoid parsing IPv6 addresses with zone IDs (see RFC 4007)
* @param bool $supportNonDecimalIPv4 set to true to support parsing non decimal (that is, octal and hexadecimal) IPv4 addresses
* @param string|mixed $address
* @param bool $mayIncludePort
* @param bool $mayIncludeZoneID
* @param bool $supportNonDecimalIPv4
*
* @return \IPLib\Address\AddressInterface|null
*
* @see \IPLib\Factory::parseAddressString()
* @since 1.1.0 added the $mayIncludePort argument
* @since 1.3.0 added the $mayIncludeZoneID argument
* @since 1.10.0 added the $supportNonDecimalIPv4 argument
*/
public static function addressFromString($address, $mayIncludePort = true, $mayIncludeZoneID = true, $supportNonDecimalIPv4 = false)
{
return static::parseAddressString($address, 0 + ($mayIncludePort ? ParseStringFlag::MAY_INCLUDE_PORT : 0) + ($mayIncludeZoneID ? ParseStringFlag::MAY_INCLUDE_ZONEID : 0) + ($supportNonDecimalIPv4 ? ParseStringFlag::IPV4_MAYBE_NON_DECIMAL : 0));
}
/**
* Parse an IP address string.
*
* @param string|mixed $address the address to parse
* @param int $flags A combination or zero or more flags
*
* @return \IPLib\Address\AddressInterface|null
*
* @see \IPLib\ParseStringFlag
* @since 1.17.0
*/
public static function parseAddressString($address, $flags = 0)
{
$result = null;
if ($result === null) {
$result = Address\IPv4::fromString($address, $mayIncludePort, $supportNonDecimalIPv4);
$result = Address\IPv4::parseString($address, $flags);
}
if ($result === null) {
$result = Address\IPv6::fromString($address, $mayIncludePort, $mayIncludeZoneID);
$result = Address\IPv6::parseString($address, $flags);
}
return $result;
@@ -54,59 +79,125 @@ class Factory
return $result;
}
/**
* @deprecated since 1.17.0: use the parseRangeString() method instead.
* For upgrading:
* - if $supportNonDecimalIPv4 is true, use the ParseStringFlag::IPV4_MAYBE_NON_DECIMAL flag
*
* @param string|mixed $address
* @param bool $supportNonDecimalIPv4
*
* @return \IPLib\Address\AddressInterface|null
*
* @see \IPLib\Factory::parseRangeString()
* @since 1.10.0 added the $supportNonDecimalIPv4 argument
*/
public static function rangeFromString($address, $supportNonDecimalIPv4 = false)
{
return static::parseRangeString($address, $supportNonDecimalIPv4 ? ParseStringFlag::IPV4_MAYBE_NON_DECIMAL : 0);
}
/**
* Parse an IP range string.
*
* @param string $range
* @param bool $supportNonDecimalIPv4 set to true to support parsing non decimal (that is, octal and hexadecimal) IPv4 addresses
* @param int $flags A combination or zero or more flags
*
* @return \IPLib\Range\RangeInterface|null
*
* @see \IPLib\ParseStringFlag
* @since 1.17.0
*/
public static function rangeFromString($range, $supportNonDecimalIPv4 = false)
public static function parseRangeString($range, $flags = 0)
{
$result = null;
if ($result === null) {
$result = Range\Subnet::fromString($range, $supportNonDecimalIPv4);
$result = Range\Subnet::parseString($range, $flags);
}
if ($result === null) {
$result = Range\Pattern::fromString($range, $supportNonDecimalIPv4);
$result = Range\Pattern::parseString($range, $flags);
}
if ($result === null) {
$result = Range\Single::fromString($range, $supportNonDecimalIPv4);
$result = Range\Single::parseString($range, $flags);
}
return $result;
}
/**
* Create the smallest address range that comprises two addresses.
* @deprecated since 1.17.0: use the getRangeFromBoundaries() method instead.
* For upgrading:
* - if $supportNonDecimalIPv4 is true, use the ParseStringFlag::IPV4_MAYBE_NON_DECIMAL flag
*
* @param string|\IPLib\Address\AddressInterface $from
* @param string|\IPLib\Address\AddressInterface $to
* @param bool $supportNonDecimalIPv4 set to true to support parsing non decimal (that is, octal and hexadecimal) IPv4 addresses
* @param string|\IPLib\Address\AddressInterface|mixed $from
* @param string|\IPLib\Address\AddressInterface|mixed $to
* @param bool $supportNonDecimalIPv4
*
* @return \IPLib\Range\RangeInterface|null return NULL if $from and/or $to are invalid addresses, or if both are NULL or empty strings, or if they are addresses of different types
* @return \IPLib\Address\AddressInterface|null
*
* @see \IPLib\Factory::getRangeFromBoundaries()
* @since 1.2.0
* @since 1.10.0 added the $supportNonDecimalIPv4 argument
*/
public static function rangeFromBoundaries($from, $to, $supportNonDecimalIPv4 = false)
{
list($from, $to) = self::parseBoundaries($from, $to, $supportNonDecimalIPv4);
return static::getRangeFromBoundaries($from, $to, ParseStringFlag::MAY_INCLUDE_PORT | ParseStringFlag::MAY_INCLUDE_ZONEID | ($supportNonDecimalIPv4 ? ParseStringFlag::IPV4_MAYBE_NON_DECIMAL : 0));
}
/**
* Create the smallest address range that comprises two addresses.
*
* @param string|\IPLib\Address\AddressInterface|mixed $from
* @param string|\IPLib\Address\AddressInterface|mixed $to
* @param int $flags A combination or zero or more flags
*
* @return \IPLib\Range\RangeInterface|null return NULL if $from and/or $to are invalid addresses, or if both are NULL or empty strings, or if they are addresses of different types
*
* @see \IPLib\ParseStringFlag
* @since 1.17.0
*/
public static function getRangeFromBoundaries($from, $to, $flags = 0)
{
list($from, $to) = self::parseBoundaries($from, $to, $flags);
return $from === false || $to === false ? null : static::rangeFromBoundaryAddresses($from, $to);
}
/**
* @deprecated since 1.17.0: use the getRangesFromBoundaries() method instead.
* For upgrading:
* - if $supportNonDecimalIPv4 is true, use the ParseStringFlag::IPV4_MAYBE_NON_DECIMAL flag
*
* @param string|\IPLib\Address\AddressInterface|mixed $from
* @param string|\IPLib\Address\AddressInterface|mixed $to
* @param bool $supportNonDecimalIPv4
*
* @return \IPLib\Range\Subnet[]|null
*
* @see \IPLib\Factory::getRangesFromBoundaries()
* @since 1.14.0
*/
public static function rangesFromBoundaries($from, $to, $supportNonDecimalIPv4 = false)
{
return static::getRangesFromBoundaries($from, $to, ParseStringFlag::MAY_INCLUDE_PORT | ParseStringFlag::MAY_INCLUDE_ZONEID | ($supportNonDecimalIPv4 ? ParseStringFlag::IPV4_MAYBE_NON_DECIMAL : 0));
}
/**
* Create a list of Range instances that exactly describes all the addresses between the two provided addresses.
*
* @param string|\IPLib\Address\AddressInterface $from
* @param string|\IPLib\Address\AddressInterface $to
* @param bool $supportNonDecimalIPv4 set to true to support parsing non decimal (that is, octal and hexadecimal) IPv4 addresses
* @param int $flags A combination or zero or more flags
*
* @return \IPLib\Range\Subnet[]|null return NULL if $from and/or $to are invalid addresses, or if both are NULL or empty strings, or if they are addresses of different types
*
* @see \IPLib\ParseStringFlag
* @since 1.17.0
*/
public static function rangesFromBoundaries($from, $to, $supportNonDecimalIPv4 = false)
public static function getRangesFromBoundaries($from, $to, $flags = 0)
{
list($from, $to) = self::parseBoundaries($from, $to, $supportNonDecimalIPv4);
if (($from === false || $to === false) || ($from === null && $to === null)) {
list($from, $to) = self::parseBoundaries($from, $to, $flags);
if ($from === false || $to === false || ($from === null && $to === null)) {
return null;
}
if ($from === null || $to === null) {
@@ -118,7 +209,7 @@ class Factory
if ($to->getNumberOfBits() !== $numberOfBits) {
return null;
}
$calculator = new RangesFromBounradyCalculator($numberOfBits);
$calculator = new RangesFromBoundaryCalculator($numberOfBits);
return $calculator->getRanges($from, $to);
}
@@ -128,6 +219,8 @@ class Factory
* @param \IPLib\Address\AddressInterface $to
*
* @return \IPLib\Range\RangeInterface|null
*
* @since 1.2.0
*/
protected static function rangeFromBoundaryAddresses(AddressInterface $from = null, AddressInterface $to = null)
{
@@ -163,7 +256,7 @@ class Factory
break;
}
}
$result = static::rangeFromString($from->toString(true) . '/' . (string) $sameBits);
$result = static::parseRangeString($from->toString() . '/' . (string) $sameBits);
}
}
}
@@ -174,11 +267,11 @@ class Factory
/**
* @param string|\IPLib\Address\AddressInterface $from
* @param string|\IPLib\Address\AddressInterface $to
* @param bool $supportNonDecimalIPv4
* @param int $flags
*
* @return \IPLib\Address\AddressInterface[]|null[]|false[]
*/
private static function parseBoundaries($from, $to, $supportNonDecimalIPv4 = false)
private static function parseBoundaries($from, $to, $flags = 0)
{
$result = array();
foreach (array('from', 'to') as $param) {
@@ -188,7 +281,7 @@ class Factory
if ($value === '') {
$value = null;
} else {
$value = static::addressFromString($value, true, true, $supportNonDecimalIPv4);
$value = static::parseAddressString($value, $flags);
if ($value === null) {
$value = false;
}

View File

@@ -0,0 +1,79 @@
<?php
namespace IPLib;
/**
* Flags for the parseString() methods.
*
* @since 1.17.0
*/
class ParseStringFlag
{
/**
* Use this flag if the input string may include the port.
*
* @var int
*/
const MAY_INCLUDE_PORT = 1;
/**
* Use this flag if the input string may include a zone ID.
*
* @var int
*/
const MAY_INCLUDE_ZONEID = 2;
/**
* Use this flag if IPv4 addresses may be in decimal/octal/hexadecimal format.
* This notation is accepted by the implementation of inet_aton and inet_addr of the libc implementation of GNU, Windows and Mac (but not Musl), but not by inet_pton and ip2long.
*
* @var int
*
* @example 1.08.0x10.0 => 5.0.0.1
* @example 5.256 => 5.0.1.0
* @example 5.0.256 => 5.0.1.0
* @example 123456789 => 7.91.205.21
*/
const IPV4_MAYBE_NON_DECIMAL = 4;
/**
* Use this flag if IPv4 subnet ranges may be in compact form.
*
* @example 127/24 => 127.0.0.0/24
* @example 10/8 => 10.0.0.0/8
* @example 10/24 => 10.0.0.0/24
* @example 10.10.10/24 => 10.10.10.0/24
*
* @var int
*/
const IPV4SUBNET_MAYBE_COMPACT = 8;
/**
* Use this flag if IPv4 addresses may be in non quad-dotted notation.
* This notation is accepted by the implementation of inet_aton and inet_addr of the libc implementation of GNU, Windows and Mac (but not Musl), but not by inet_pton and ip2long.
*
* @var int
*
* @example 5.1 => 5.0.0.1
* @example 5.256 => 5.0.1.0
* @example 5.0.256 => 5.0.1.0
* @example 123456789 => 7.91.205.21
*
* @see https://man7.org/linux/man-pages/man3/inet_addr.3.html#DESCRIPTION
* @see https://www.freebsd.org/cgi/man.cgi?query=inet_net&sektion=3&apropos=0&manpath=FreeBSD+12.2-RELEASE+and+Ports#end
* @see http://git.musl-libc.org/cgit/musl/tree/src/network/inet_aton.c?h=v1.2.2
*/
const IPV4ADDRESS_MAYBE_NON_QUAD_DOTTED = 16;
/**
* Use this flag if you want to accept parsing IPv4/IPv6 addresses in Reverse DNS Lookup Address format.
*
* @var int
*
* @since 1.18.0
*
* @example 140.13.12.10.in-addr.arpa => 10.12.13.140
* @example b.a.9.8.7.6.5.0.4.0.0.0.3.0.0.0.2.0.0.0.1.0.0.0.0.0.0.0.1.2.3.4.ip6.arpa => 4321:0:1:2:3:4:567:89ab
*/
const ADDRESS_MAYBE_RDNS = 32;
}

View File

@@ -8,6 +8,9 @@ use IPLib\Address\IPv6;
use IPLib\Address\Type as AddressType;
use IPLib\Factory;
/**
* Base class for range classes.
*/
abstract class AbstractRange implements RangeInterface
{
/**
@@ -20,7 +23,7 @@ abstract class AbstractRange implements RangeInterface
if ($this->rangeType === null) {
$addressType = $this->getAddressType();
if ($addressType === AddressType::T_IPv6 && Subnet::get6to4()->containsRange($this)) {
$this->rangeType = Factory::rangeFromBoundaries($this->fromAddress->toIPv4(), $this->toAddress->toIPv4())->getRangeType();
$this->rangeType = Factory::getRangeFromBoundaries($this->fromAddress->toIPv4(), $this->toAddress->toIPv4())->getRangeType();
} else {
switch ($addressType) {
case AddressType::T_IPv4:
@@ -48,6 +51,33 @@ abstract class AbstractRange implements RangeInterface
return $this->rangeType === false ? null : $this->rangeType;
}
/**
* {@inheritdoc}
*
* @see \IPLib\Range\RangeInterface::getAddressAtOffset()
*/
public function getAddressAtOffset($n)
{
if (!is_int($n)) {
return null;
}
$address = null;
if ($n >= 0) {
$start = Factory::parseAddressString($this->getComparableStartString());
$address = $start->getAddressAtOffset($n);
} else {
$end = Factory::parseAddressString($this->getComparableEndString());
$address = $end->getAddressAtOffset($n + 1);
}
if ($address === null) {
return null;
}
return $this->contains($address) ? $address : null;
}
/**
* {@inheritdoc}
*

View File

@@ -6,6 +6,7 @@ use IPLib\Address\AddressInterface;
use IPLib\Address\IPv4;
use IPLib\Address\IPv6;
use IPLib\Address\Type as AddressType;
use IPLib\ParseStringFlag;
/**
* Represents an address range in pattern format (only ending asterisks are supported).
@@ -40,6 +41,8 @@ class Pattern extends AbstractRange
* The type of the range of this IP range.
*
* @var int|false|null false if this range crosses multiple range types, null if yet to be determined
*
* @since 1.5.0
*/
protected $rangeType;
@@ -67,24 +70,45 @@ class Pattern extends AbstractRange
return $this->toString();
}
/**
* @deprecated since 1.17.0: use the parseString() method instead.
* For upgrading:
* - if $supportNonDecimalIPv4 is true, use the ParseStringFlag::IPV4_MAYBE_NON_DECIMAL flag
*
* @param string|mixed $range
* @param bool $supportNonDecimalIPv4
*
* @return static|null
*
* @see \IPLib\Range\Pattern::parseString()
* @since 1.10.0 added the $supportNonDecimalIPv4 argument
*/
public static function fromString($range, $supportNonDecimalIPv4 = false)
{
return static::parseString($range, ParseStringFlag::MAY_INCLUDE_PORT | ParseStringFlag::MAY_INCLUDE_ZONEID | ($supportNonDecimalIPv4 ? ParseStringFlag::IPV4_MAYBE_NON_DECIMAL : 0));
}
/**
* Try get the range instance starting from its string representation.
*
* @param string|mixed $range
* @param bool $supportNonDecimalIPv4 set to true to support parsing non decimal (that is, octal and hexadecimal) IPv4 addresses
* @param int $flags A combination or zero or more flags
*
* @return static|null
*
* @since 1.17.0
* @see \IPLib\ParseStringFlag
*/
public static function fromString($range, $supportNonDecimalIPv4 = false)
public static function parseString($range, $flags = 0)
{
if (!is_string($range) || strpos($range, '*') === false) {
return null;
}
if ($range === '*.*.*.*') {
return new static(IPv4::fromString('0.0.0.0'), IPv4::fromString('255.255.255.255'), 4);
return new static(IPv4::parseString('0.0.0.0'), IPv4::parseString('255.255.255.255'), 4);
}
if ($range === '*:*:*:*:*:*:*:*') {
return new static(IPv6::fromString('::'), IPv6::fromString('ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff'), 8);
return new static(IPv6::parseString('::'), IPv6::parseString('ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff'), 8);
}
$matches = null;
if (strpos($range, '.') !== false && preg_match('/^[^*]+((?:\.\*)+)$/', $range, $matches)) {
@@ -96,7 +120,7 @@ class Pattern extends AbstractRange
$asterisksCount += $missingDots;
}
}
$fromAddress = IPv4::fromString(str_replace('*', '0', $range), true, $supportNonDecimalIPv4);
$fromAddress = IPv4::parseString(str_replace('*', '0', $range), $flags);
if ($fromAddress === null) {
return null;
}
@@ -108,7 +132,7 @@ class Pattern extends AbstractRange
}
if (strpos($range, ':') !== false && preg_match('/^[^*]+((?::\*)+)$/', $range, $matches)) {
$asterisksCount = strlen($matches[1]) >> 1;
$fromAddress = IPv6::fromString(str_replace('*', '0', $range));
$fromAddress = IPv6::parseString(str_replace('*', '0', $range));
if ($fromAddress === null) {
return null;
}
@@ -217,15 +241,11 @@ class Pattern extends AbstractRange
* {@inheritdoc}
*
* @see \IPLib\Range\RangeInterface::asSubnet()
* @since 1.8.0
*/
public function asSubnet()
{
switch ($this->getAddressType()) {
case AddressType::T_IPv4:
return new Subnet($this->getStartAddress(), $this->getEndAddress(), 8 * (4 - $this->asterisksCount));
case AddressType::T_IPv6:
return new Subnet($this->getStartAddress(), $this->getEndAddress(), 16 * (8 - $this->asterisksCount));
}
return new Subnet($this->getStartAddress(), $this->getEndAddress(), $this->getNetworkPrefix());
}
/**
@@ -272,4 +292,31 @@ class Pattern extends AbstractRange
{
return $this->asterisksCount === 0 ? array($this->getStartAddress()->getReverseDNSLookupName()) : $this->asSubnet()->getReverseDNSLookupName();
}
/**
* {@inheritdoc}
*
* @see \IPLib\Range\RangeInterface::getSize()
*/
public function getSize()
{
$fromAddress = $this->fromAddress;
$maxPrefix = $fromAddress::getNumberOfBits();
$prefix = $this->getNetworkPrefix();
return pow(2, ($maxPrefix - $prefix));
}
/**
* @return float|int
*/
private function getNetworkPrefix()
{
switch ($this->getAddressType()) {
case AddressType::T_IPv4:
return 8 * (4 - $this->asterisksCount);
case AddressType::T_IPv6:
return 16 * (8 - $this->asterisksCount);
}
}
}

View File

@@ -38,9 +38,26 @@ interface RangeInterface
* Get the type of range of the IP address.
*
* @return int One of the \IPLib\Range\Type::T_... constants
*
* @since 1.5.0
*/
public function getRangeType();
/**
* Get the address at a certain offset of this range.
*
* @param int $n the offset of the address (support negative offset)
*
* @return \IPLib\Address\AddressInterface|null return NULL if $n is not an integer or if the offset out of range
*
* @since 1.15.0
*
* @example passing 256 to the range 127.0.0.0/16 will result in 127.0.1.0
* @example passing -1 to the range 127.0.1.0/16 will result in 127.0.255.255
* @example passing 256 to the range 127.0.0.0/24 will result in NULL
*/
public function getAddressAtOffset($n);
/**
* Check if this range contains an IP address.
*
@@ -56,6 +73,8 @@ interface RangeInterface
* @param \IPLib\Range\RangeInterface $range
*
* @return bool
*
* @since 1.5.0
*/
public function containsRange(RangeInterface $range);
@@ -63,6 +82,8 @@ interface RangeInterface
* Get the initial address contained in this range.
*
* @return \IPLib\Address\AddressInterface
*
* @since 1.4.0
*/
public function getStartAddress();
@@ -70,6 +91,8 @@ interface RangeInterface
* Get the final address contained in this range.
*
* @return \IPLib\Address\AddressInterface
*
* @since 1.4.0
*/
public function getEndAddress();
@@ -91,6 +114,8 @@ interface RangeInterface
* Get the subnet mask representing this range (only for IPv4 ranges).
*
* @return \IPLib\Address\IPv4|null return NULL if the range is an IPv6 range, the subnet mask otherwise
*
* @since 1.8.0
*/
public function getSubnetMask();
@@ -98,6 +123,8 @@ interface RangeInterface
* Get the subnet/CIDR representation of this range.
*
* @return \IPLib\Range\Subnet
*
* @since 1.13.0
*/
public function asSubnet();
@@ -105,6 +132,8 @@ interface RangeInterface
* Get the pattern/asterisk representation (if applicable) of this range.
*
* @return \IPLib\Range\Pattern|null return NULL if this range can't be represented by a pattern notation
*
* @since 1.13.0
*/
public function asPattern();
@@ -113,8 +142,19 @@ interface RangeInterface
*
* @return string[]
*
* @since 1.13.0
*
* @example for IPv4 it returns something like array('x.x.x.x.in-addr.arpa', 'x.x.x.x.in-addr.arpa') (where the number of 'x.' ranges from 1 to 4)
* @example for IPv6 it returns something like array('x.x.x.x..x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.ip6.arpa', 'x.x.x.x..x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.ip6.arpa') (where the number of 'x.' ranges from 1 to 32)
*/
public function getReverseDNSLookupName();
/**
* Get the count of addresses this IP range contains.
*
* @return int|float Return float as for huge IPv6 networks, int is not enough
*
* @since 1.16.0
*/
public function getSize();
}

View File

@@ -6,6 +6,7 @@ use IPLib\Address\AddressInterface;
use IPLib\Address\IPv4;
use IPLib\Address\Type as AddressType;
use IPLib\Factory;
use IPLib\ParseStringFlag;
/**
* Represents a single address (eg a range that contains just one address).
@@ -41,17 +42,39 @@ class Single extends AbstractRange
}
/**
* Try get the range instance starting from its string representation.
* @deprecated since 1.17.0: use the parseString() method instead.
* For upgrading:
* - if $supportNonDecimalIPv4 is true, use the ParseStringFlag::IPV4_MAYBE_NON_DECIMAL flag
*
* @param string|mixed $range
* @param bool $supportNonDecimalIPv4 set to true to support parsing non decimal (that is, octal and hexadecimal) IPv4 addresses
* @param bool $supportNonDecimalIPv4
*
* @return static|null
*
* @see \IPLib\Range\Single::parseString()
* @since 1.10.0 added the $supportNonDecimalIPv4 argument
*/
public static function fromString($range, $supportNonDecimalIPv4 = false)
{
return static::parseString($range, ParseStringFlag::MAY_INCLUDE_PORT | ParseStringFlag::MAY_INCLUDE_ZONEID | ($supportNonDecimalIPv4 ? ParseStringFlag::IPV4_MAYBE_NON_DECIMAL : 0));
}
/**
* Try get the range instance starting from its string representation.
*
* @param string|mixed $range
* @param int $flags A combination or zero or more flags
*
* @return static|null
*
* @see \IPLib\ParseStringFlag
* @since 1.17.0
*/
public static function parseString($range, $flags = 0)
{
$result = null;
$address = Factory::addressFromString($range, true, true, $supportNonDecimalIPv4);
$flags = (int) $flags;
$address = Factory::parseAddressString($range, $flags);
if ($address !== null) {
$result = new static($address);
}
@@ -65,6 +88,8 @@ class Single extends AbstractRange
* @param \IPLib\Address\AddressInterface $address
*
* @return static
*
* @since 1.2.0
*/
public static function fromAddress(AddressInterface $address)
{
@@ -118,23 +143,6 @@ class Single extends AbstractRange
return $result;
}
/**
* {@inheritdoc}
*
* @see \IPLib\Range\RangeInterface::containsRange()
*/
public function containsRange(RangeInterface $range)
{
$result = false;
if ($range->getAddressType() === $this->getAddressType()) {
if ($range->toString(false) === $this->toString(false)) {
$result = true;
}
}
return $result;
}
/**
* {@inheritdoc}
*
@@ -223,4 +231,14 @@ class Single extends AbstractRange
{
return array($this->getStartAddress()->getReverseDNSLookupName());
}
/**
* {@inheritdoc}
*
* @see \IPLib\Range\RangeInterface::getSize()
*/
public function getSize()
{
return 1;
}
}

View File

@@ -4,9 +4,9 @@ namespace IPLib\Range;
use IPLib\Address\AddressInterface;
use IPLib\Address\IPv4;
use IPLib\Address\IPv6;
use IPLib\Address\Type as AddressType;
use IPLib\Factory;
use IPLib\ParseStringFlag;
/**
* Represents an address range in subnet format (eg CIDR).
@@ -41,6 +41,8 @@ class Subnet extends AbstractRange
* The type of the range of this IP range.
*
* @var int|null
*
* @since 1.5.0
*/
protected $rangeType;
@@ -77,15 +79,36 @@ class Subnet extends AbstractRange
return $this->toString();
}
/**
* @deprecated since 1.17.0: use the parseString() method instead.
* For upgrading:
* - if $supportNonDecimalIPv4 is true, use the ParseStringFlag::IPV4_MAYBE_NON_DECIMAL flag
*
* @param string|mixed $range
* @param bool $supportNonDecimalIPv4
*
* @return static|null
*
* @see \IPLib\Range\Subnet::parseString()
* @since 1.10.0 added the $supportNonDecimalIPv4 argument
*/
public static function fromString($range, $supportNonDecimalIPv4 = false)
{
return static::parseString($range, ParseStringFlag::MAY_INCLUDE_PORT | ParseStringFlag::MAY_INCLUDE_ZONEID | ($supportNonDecimalIPv4 ? ParseStringFlag::IPV4_MAYBE_NON_DECIMAL : 0));
}
/**
* Try get the range instance starting from its string representation.
*
* @param string|mixed $range
* @param bool $supportNonDecimalIPv4 set to true to support parsing non decimal (that is, octal and hexadecimal) IPv4 addresses
* @param int $flags A combination or zero or more flags
*
* @return static|null
*
* @see \IPLib\ParseStringFlag
* @since 1.17.0
*/
public static function fromString($range, $supportNonDecimalIPv4 = false)
public static function parseString($range, $flags = 0)
{
if (!is_string($range)) {
return null;
@@ -94,7 +117,14 @@ class Subnet extends AbstractRange
if (count($parts) !== 2) {
return null;
}
$address = Factory::addressFromString($parts[0], true, true, $supportNonDecimalIPv4);
$flags = (int) $flags;
if (strpos($parts[0], ':') === false && $flags & ParseStringFlag::IPV4SUBNET_MAYBE_COMPACT) {
$missingDots = 3 - substr_count($parts[0], '.');
if ($missingDots > 0) {
$parts[0] .= str_repeat('.0', $missingDots);
}
}
$address = Factory::parseAddressString($parts[0], $flags);
if ($address === null) {
return null;
}
@@ -197,9 +227,10 @@ class Subnet extends AbstractRange
}
/**
* Get the pattern (asterisk) representation (if applicable) of this range.
* {@inheritdoc}
*
* @return \IPLib\Range\Pattern|null return NULL if this range can't be represented by a pattern notation
* @see \IPLib\Range\RangeInterface::asPattern()
* @since 1.8.0
*/
public function asPattern()
{
@@ -217,11 +248,13 @@ class Subnet extends AbstractRange
* Get the 6to4 address IPv6 address range.
*
* @return self
*
* @since 1.5.0
*/
public static function get6to4()
{
if (self::$sixToFour === null) {
self::$sixToFour = self::fromString('2002::/16');
self::$sixToFour = self::parseString('2002::/16');
}
return self::$sixToFour;
@@ -231,6 +264,8 @@ class Subnet extends AbstractRange
* Get subnet prefix.
*
* @return int
*
* @since 1.7.0
*/
public function getNetworkPrefix()
{
@@ -302,4 +337,18 @@ class Subnet extends AbstractRange
return $result;
}
/**
* {@inheritdoc}
*
* @see \IPLib\Range\RangeInterface::getSize()
*/
public function getSize()
{
$fromAddress = $this->fromAddress;
$maxPrefix = $fromAddress::getNumberOfBits();
$prefix = $this->getNetworkPrefix();
return pow(2, ($maxPrefix - $prefix));
}
}

View File

@@ -102,6 +102,8 @@ class Type
* Carrier-grade NAT address.
*
* @var int
*
* @since 1.10.0
*/
const T_CGNAT = 14;

View File

@@ -4,6 +4,8 @@ namespace IPLib\Service;
/**
* Helper class to work with unsigned binary integers.
*
* @internal
*/
class BinaryMath
{

View File

@@ -7,12 +7,14 @@ use IPLib\Factory;
use IPLib\Range\Subnet;
/**
* Helper class to calculate the subnets describing all (and only all) the addresses between two bouundaries.
* Helper class to calculate the subnets describing all (and only all) the addresses between two boundaries.
*
* @internal
*/
class RangesFromBounradyCalculator
class RangesFromBoundaryCalculator
{
/**
* The BinaryMath instance to be used to perform bitwise poerations.
* The BinaryMath instance to be used to perform bitwise operations.
*
* @var \IPLib\Service\BinaryMath
*/
@@ -53,7 +55,7 @@ class RangesFromBounradyCalculator
}
/**
* Calculate the subnets describing all (and only all) the addresses between two bouundaries.
* Calculate the subnets describing all (and only all) the addresses between two boundaries.
*
* @param \IPLib\Address\AddressInterface $from
* @param \IPLib\Address\AddressInterface $to
@@ -154,8 +156,13 @@ class RangesFromBounradyCalculator
*/
private function subnetFromBits($bits, $networkPrefix)
{
$address = $this->addressFromBits($bits);
$startAddress = $this->addressFromBits($bits);
$numOnes = $this->numBits - $networkPrefix;
if ($numOnes === 0) {
return new Subnet($startAddress, $startAddress, $networkPrefix);
}
$endAddress = $this->addressFromBits(substr($bits, 0, -$numOnes) . str_repeat('1', $numOnes));
return new Subnet($address, $address, $networkPrefix);
return new Subnet($startAddress, $endAddress, $networkPrefix);
}
}

View File

@@ -0,0 +1,171 @@
<?php
namespace IPLib\Service;
/**
* Helper class to work with unsigned integers.
*
* @internal
*/
class UnsignedIntegerMath
{
/**
* Convert a string containing a decimal, octal or hexadecimal number into its bytes.
*
* @param string $value
* @param int $numBytes the wanted number of bytes
* @param bool $onlyDecimal Only parse decimal numbers
*
* @return int[]|null
*/
public function getBytes($value, $numBytes, $onlyDecimal = false)
{
$m = null;
if ($onlyDecimal) {
if (preg_match('/^0*(\d+)$/', $value, $m)) {
return $this->getBytesFromDecimal($m[1], $numBytes);
}
} else {
if (preg_match('/^0[Xx]0*([0-9A-Fa-f]+)$/', $value, $m)) {
return $this->getBytesFromHexadecimal($m[1], $numBytes);
}
if (preg_match('/^0+([0-7]*)$/', $value, $m)) {
return $this->getBytesFromOctal($m[1], $numBytes);
}
if (preg_match('/^[1-9][0-9]*$/', $value)) {
return $this->getBytesFromDecimal($value, $numBytes);
}
}
// Not a valid number
return null;
}
/**
* @return int
*/
protected function getMaxSignedInt()
{
return PHP_INT_MAX;
}
/**
* @param string $value never zero-length, never extra leading zeroes
* @param int $numBytes
*
* @return int[]|null
*/
private function getBytesFromBits($value, $numBytes)
{
$valueLength = strlen($value);
if ($valueLength > $numBytes << 3) {
// overflow
return null;
}
$remainderBits = $valueLength % 8;
if ($remainderBits !== 0) {
$value = str_pad($value, $valueLength + 8 - $remainderBits, '0', STR_PAD_LEFT);
}
$bytes = array_map('bindec', str_split($value, 8));
return array_pad($bytes, -$numBytes, 0);
}
/**
* @param string $value may be zero-length, never extra leading zeroes
* @param int $numBytes
*
* @return int[]|null
*/
private function getBytesFromOctal($value, $numBytes)
{
if ($value === '') {
return array_fill(0, $numBytes, 0);
}
$bits = implode(
'',
array_map(
function ($octalDigit) {
return str_pad(decbin(octdec($octalDigit)), 3, '0', STR_PAD_LEFT);
},
str_split($value, 1)
)
);
$bits = ltrim($bits, '0');
return $bits === '' ? array_fill(0, $numBytes, 0) : static::getBytesFromBits($bits, $numBytes);
}
/**
* @param string $value never zero-length, never extra leading zeroes
* @param int $numBytes
*
* @return int[]|null
*/
private function getBytesFromDecimal($value, $numBytes)
{
$valueLength = strlen($value);
$maxSignedIntLength = strlen((string) $this->getMaxSignedInt());
if ($valueLength < $maxSignedIntLength) {
return $this->getBytesFromBits(decbin((int) $value), $numBytes);
}
// Divide by two, so that we have 1 less bit
$carry = 0;
$halfValue = ltrim(
implode(
'',
array_map(
function ($digit) use (&$carry) {
$number = $carry + (int) $digit;
$carry = ($number % 2) * 10;
return (string) $number >> 1;
},
str_split($value, 1)
)
),
'0'
);
$halfValueBytes = $this->getBytesFromDecimal($halfValue, $numBytes);
if ($halfValueBytes === null) {
return null;
}
$carry = $carry === 0 ? 0 : 1;
$result = array_fill(0, $numBytes, 0);
for ($index = $numBytes - 1; $index >= 0; $index--) {
$byte = $carry + ($halfValueBytes[$index] << 1);
if ($byte <= 0xFF) {
$carry = 0;
} else {
$carry = ($byte & ~0xFF) >> 8;
$byte -= 0x100;
}
$result[$index] = $byte;
}
if ($carry !== 0) {
// Overflow
return null;
}
return $result;
}
/**
* @param string $value never zero-length, never extra leading zeroes
* @param int $numBytes
*
* @return int[]|null
*/
private function getBytesFromHexadecimal($value, $numBytes)
{
$valueLength = strlen($value);
if ($valueLength > $numBytes << 1) {
// overflow
return null;
}
$value = str_pad($value, $valueLength + $valueLength % 2, '0', STR_PAD_LEFT);
$bytes = array_map('hexdec', str_split($value, 2));
return array_pad($bytes, -$numBytes, 0);
}
}

View File

@@ -183,7 +183,7 @@ if (!is_callable('random_bytes')) {
try {
$RandomCompatCOMtest = new COM('CAPICOM.Utilities.1');
/** @psalm-suppress TypeDoesNotContainType */
if (method_exists($RandomCompatCOMtest, 'GetRandom')) {
if (is_callable(array($RandomCompatCOMtest, 'GetRandom'))) {
// See random_bytes_com_dotnet.php
require_once $RandomCompatDIR.DIRECTORY_SEPARATOR.'random_bytes_com_dotnet.php';
}