mirror of
https://github.com/PrivateBin/PrivateBin.git
synced 2026-03-23 17:24:07 -04:00
Compare commits
87 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6b3ad32665 | ||
|
|
0997520c1d | ||
|
|
2dc4422a27 | ||
|
|
3fe7e77390 | ||
|
|
ce107c928e | ||
|
|
24a4328c55 | ||
|
|
bba485ef6d | ||
|
|
d8ae1be2ff | ||
|
|
42a9c92b5e | ||
|
|
9d27e7a65d | ||
|
|
d42975580a | ||
|
|
176dff3b70 | ||
|
|
5a9879623f | ||
|
|
740d62005e | ||
|
|
40019624fd | ||
|
|
e3f4aa982c | ||
|
|
ca07398b66 | ||
|
|
f96b0c0afe | ||
|
|
14d08ec56d | ||
|
|
22d0b1ec22 | ||
|
|
f21567133c | ||
|
|
b92b38cee8 | ||
|
|
87b41a0c3d | ||
|
|
2e3bacb699 | ||
|
|
5d61b90d6b | ||
|
|
512b3d1172 | ||
|
|
1d6cfb7f3b | ||
|
|
9e6e29bc93 | ||
|
|
e5b096ed8c | ||
|
|
add980d36f | ||
|
|
7ec94e0db5 | ||
|
|
6b7dc44039 | ||
|
|
ce3f10f143 | ||
|
|
694138c5d4 | ||
|
|
211d3e4622 | ||
|
|
d04eab52c9 | ||
|
|
22b4c89227 | ||
|
|
6d24ff824e | ||
|
|
9f68658106 | ||
|
|
0de9f868fa | ||
|
|
0686087cfd | ||
|
|
2dd8b94b05 | ||
|
|
608605cd54 | ||
|
|
a41d0ca4dd | ||
|
|
844c4d15e9 | ||
|
|
a111357fae | ||
|
|
47efedf23c | ||
|
|
801cdc627e | ||
|
|
39e865ca64 | ||
|
|
50075ea948 | ||
|
|
ed9c4f45f4 | ||
|
|
e144739dec | ||
|
|
8de24fae67 | ||
|
|
9274564162 | ||
|
|
e04e75adf3 | ||
|
|
c72932d653 | ||
|
|
895df63206 | ||
|
|
f25661beda | ||
|
|
ec8851e46c | ||
|
|
106141efa4 | ||
|
|
61903665df | ||
|
|
0e53d1ee86 | ||
|
|
9dde7f034a | ||
|
|
428a9d9c41 | ||
|
|
da42968d7c | ||
|
|
be8883d10e | ||
|
|
b26fb08732 | ||
|
|
588e01fbec | ||
|
|
b060d57524 | ||
|
|
2f15d4c70a | ||
|
|
108cf69f8b | ||
|
|
c83ba8256f | ||
|
|
eee7b0144a | ||
|
|
5e47feb7e1 | ||
|
|
9a370d33ea | ||
|
|
9ac9f9e1a7 | ||
|
|
952f8b7b9a | ||
|
|
18f32f3625 | ||
|
|
72ace70b04 | ||
|
|
ce331e8271 | ||
|
|
b883d5eb4c | ||
|
|
3099e10dfa | ||
|
|
d74172064b | ||
|
|
9687144c92 | ||
|
|
a2af88a36e | ||
|
|
28776ac178 | ||
|
|
3edeefd799 |
6
.gitattributes
vendored
6
.gitattributes
vendored
@@ -1,4 +1,4 @@
|
||||
doc/* export-ignore
|
||||
tst/* export-ignore
|
||||
doc/ export-ignore
|
||||
tst/ export-ignore
|
||||
.gitattributes export-ignore
|
||||
.gitignore export-ignore
|
||||
.gitignore export-ignore
|
||||
|
||||
41
CHANGELOG.md
41
CHANGELOG.md
@@ -1,12 +1,49 @@
|
||||
# ZeroBin version history #
|
||||
|
||||
* **0.22 (2015-11-09)**:
|
||||
* ADDED: Tab character input support
|
||||
* ADDED: Dark bootstrap theme
|
||||
* ADDED: Option to hide clone button on expiring pastes
|
||||
* ADDED: Option to set a different default language then English and/or enforce it as the only language
|
||||
* ADDED: Database now contains version to allow automatic update of structure, only if necessary; removing database structure check on each request
|
||||
* ADDED: Favicons
|
||||
* FIXING: Regressions in database layer, prohibiting pastes from being stored
|
||||
* FIXING: Fixing "missing" comments when they were posted during the same second to the same paste
|
||||
* FIXING: JS failing when password input disabled
|
||||
* CHANGED: Switching positions of "New" and "Send" button, highlighting the latter to improve workflow
|
||||
* CHANGED: Renamed config file to make updates easier
|
||||
* CHANGED: Switching to JSON-based REST-API
|
||||
* CHANGED: Database structure to store attachments, allowing larger attachments to be stored (depending on maximum BLOB size of database backend)
|
||||
* CHANGED: Refactored data model, traffic limiting & request handling
|
||||
* **0.21.1 (2015-09-21)**:
|
||||
* FIXING: lost meta data when using DB model instead of flat files
|
||||
* FIXING: mobile navbar getting triggered on load
|
||||
* CHANGED: database table "paste" gets automatically extended with a "meta" column
|
||||
* CHANGED: navbar of "bootstrap" template now spans full width of view port on large screens
|
||||
* **0.21 (2015-09-19)**:
|
||||
* ADDED: Translations for German, French and Polish, language selection menu (optional)
|
||||
* ADDED: File upload and image display support (optional)
|
||||
* ADDED: Markdown format support
|
||||
* ADDED: "bootstrap-compact" template that hides some of the options in a drop down menu to ensure the nav bar fitting on one line on smaller screen sizes
|
||||
* FIXING: Various usability issues with different screen sizes / device types in the "bootstrap" template
|
||||
* CHANGED: Instead of having different options to enable and preselect certain formats there is now a generic `[formatter_options]` section where formats can be added to the displayed format drop down menu. A `defaultformatter` can be set, it defaults to "plaintext". The `syntaxhighlighting` configuration got deprecated.
|
||||
* `zerobin.js` got a major refactoring:
|
||||
* moved from global namespace into anonymous function
|
||||
* events are no longer set via "onclick" attributes in the templates, but bound by from JS side
|
||||
* for simpler maintenance the functions were grouped into objects: zerobin (display logic, event handling), filter (compression,
|
||||
encryption), i18n (translation, counterpart of i18n.php) and helper (stateless utilities)
|
||||
* Wiki pages were added to address common topics:
|
||||
* [Upgrading from ZeroBin 0.19 Alpha](https://github.com/elrido/ZeroBin/wiki/Upgrading-from-ZeroBin-0.19-Alpha)
|
||||
* [ZeroBin Directory of public servers](https://github.com/elrido/ZeroBin/wiki/ZeroBin-Directory)
|
||||
* [Translation](https://github.com/elrido/ZeroBin/wiki/Translation)
|
||||
* [Templates](https://github.com/elrido/ZeroBin/wiki/Templates)
|
||||
* **0.20 (2015-09-03)**:
|
||||
* ADDED: Password protected pastes (optional)
|
||||
* ADDED: configuration options for highlighting, password, discussions, expiration times, rate limiting
|
||||
* ADDED: JSON-only retrieval of paste incl. discussion, used to be able to refresh paste when posting a comment
|
||||
* ADDED: bootstrap CSS based template
|
||||
* CHANGE: "Burn after reading" pastes are now deleted only after the paste was successfully decrypted via callback. This prevents accidental deletion by chatbots following URLs and the like. Usage of a password is suggested to ensure only the desired recipient is able to encrypt it.
|
||||
* CHANGE: the "opendiscussion" option now only controls if the discussion checkbox is preselected. Use "discussion = false" to disable the discussion feature completely (which also removes the checkbox from the template).
|
||||
* CHANGED: "Burn after reading" pastes are now deleted only after the paste was successfully decrypted via callback. This prevents accidental deletion by chatbots following URLs and the like. Usage of a password is suggested to ensure only the desired recipient is able to encrypt it.
|
||||
* CHANGED: the "opendiscussion" option now only controls if the discussion checkbox is preselected. Use "discussion = false" to disable the discussion feature completely (which also removes the checkbox from the template).
|
||||
* FIXING: Behaviour of several conflicting configuration options. As a general measure unit tests for 9 of the options and all their possible configurations were added via a unit test generator.
|
||||
* updated JS libraries: jquery to 1.11.3, sjcl to 1.0.2, base64.js to 2.1.9, deflate to 0.5, inflate to 0.3 and prettify to latest
|
||||
* generally improved documentation, both inline phpdoc / JSdoc source code documentation, as well as Wiki pages on installation, configuration, development and JSON-API
|
||||
|
||||
11
CREDITS.md
11
CREDITS.md
@@ -5,5 +5,14 @@ Sébastien Sauvage - original idea and main developer
|
||||
Alexey Gladkov - syntax highlighting
|
||||
Greg Knaddison - robots.txt
|
||||
MrKooky - HTML5 markup, CSS cleanup
|
||||
Simon Rupf - MVC refactoring, configuration support and unit tests
|
||||
Simon Rupf - MVC refactoring, configuration, i18n and unit tests
|
||||
Hexalyse - Password protection
|
||||
Viktor Stanchev - File upload support
|
||||
azlux - Tab character input support
|
||||
Adam Fisher - Favicons
|
||||
|
||||
Translations:
|
||||
Hexalyse - French
|
||||
Kuba Niewiarowski - Polish
|
||||
Gabbalo - German
|
||||
Draky50110 - French
|
||||
|
||||
141
INSTALL.md
141
INSTALL.md
@@ -1,20 +1,49 @@
|
||||
Installation
|
||||
============
|
||||
|
||||
For Administrators
|
||||
------------------
|
||||
## Basic installation
|
||||
|
||||
In the index.php in the main folder you can define a different PATH. This is
|
||||
useful if you want to secure your installation and want to move the
|
||||
configuration, data files, templates and PHP libraries (directories cfg, data,
|
||||
lib, tpl and tst) outside of your document root. This new location must still
|
||||
be accessible to your webserver / PHP process.
|
||||
**TL;DR:** Download the [latest release archive](https://github.com/elrido/ZeroBin/releases/latest)
|
||||
and extract it in your web hosts folder were you want to install your ZeroBin instance.
|
||||
|
||||
> ### PATH Example ###
|
||||
> Your zerobin installation lives in a subfolder called "paste" inside of your
|
||||
### Requirements
|
||||
|
||||
- PHP version 5.2.6 or above
|
||||
- GD extension
|
||||
- mcrypt extension (recommended)
|
||||
- some disk space or (optional) a database supported by PDO
|
||||
- A web browser with javascript support
|
||||
|
||||
### Configuration
|
||||
|
||||
In the file `cfg/conf.ini` you can configure ZeroBin. A `cfg/conf.ini.sample`
|
||||
is provided containing all options on default values. You can copy it to
|
||||
`cfg/conf.ini` and adapt it as needed. The config file is divided into multiple
|
||||
sections, which are enclosed in square brackets.
|
||||
|
||||
In the `[main]` section you can enable or disable the discussion feature, set the
|
||||
limit of stored pastes and comments in bytes. The `[traffic]` section lets you
|
||||
set a time limit in seconds. Users may not post more often then this limit to
|
||||
your ZeroBin installation.
|
||||
|
||||
More details can be found in the
|
||||
[configuration documentation](https://github.com/elrido/ZeroBin/wiki/Configuration).
|
||||
|
||||
## Advanced installation
|
||||
|
||||
### Changing the path
|
||||
|
||||
In the index.php you can define a different `PATH`. This is useful to secure your
|
||||
installation. You can move the configuration, data files, templates and PHP
|
||||
libraries (directories cfg, data, lib, tpl, tmp and tst) outside of your document
|
||||
root. This new location must still be accessible to your webserver / PHP process
|
||||
([open_basedir setting](http://php.net/manual/en/ini.core.php#ini.open-basedir)).
|
||||
|
||||
> #### PATH Example
|
||||
> Your zerobin installation lives in a subfolder called "paste" inside of your
|
||||
> document root. The URL looks like this:
|
||||
> http://example.com/paste/
|
||||
> The ZeroBin folder on your webserver is really:
|
||||
> The full path of ZeroBin on your webserver is:
|
||||
> /home/example.com/htdocs/paste
|
||||
>
|
||||
> When setting the path like this:
|
||||
@@ -22,76 +51,56 @@ be accessible to your webserver / PHP process.
|
||||
> ZeroBin will look for your includes here:
|
||||
> /home/example.com/secret/zerobin
|
||||
|
||||
In the file "cfg/conf.ini" you can configure ZeroBin. The config file is
|
||||
divided into multiple sections, which are enclosed in square brackets. In the
|
||||
"[main]" section you can enable or disable the discussion feature, set the
|
||||
limit of stored pastes and comments in bytes. The "[traffic]" section lets you
|
||||
set a time limit in seconds. Users may not post more often then this limit to
|
||||
your ZeroBin.
|
||||
### Using a database instead of flat files
|
||||
|
||||
Finally the "[model]" and "[model_options]" sections let you configure your
|
||||
favourite way of storing the pastes and discussions on your server.
|
||||
"zerobin_data" is the default model, which stores everything in files in the
|
||||
data folder. This is the recommended setup for low traffic sites. Under high
|
||||
load, in distributed setups or if you are not allowed to store files locally,
|
||||
you might want to switch to the "zerobin_db" model. This lets you store your
|
||||
data in a database. Basically all databases that are supported by PDO (PHP
|
||||
data objects) may be used. Automatic table creation is provided for pdo_ibm,
|
||||
pdo_informix, pdo_mssql, pdo_mysql, pdo_oci, pdo_pgsql and pdo_sqlite. You may
|
||||
want to provide a table prefix, if you have to share the zerobin database with
|
||||
another application. The table prefix option is called "tbl".
|
||||
In the configuration file the `[model]` and `[model_options]` sections let you
|
||||
configure your favourite way of storing the pastes and discussions on your server.
|
||||
|
||||
> ### Note ###
|
||||
> The "zerobin_db" model has only been tested with SQLite and MySQL, although
|
||||
> it would not be recommended to use SQLite in a production environment. If you
|
||||
> gain any experience running ZeroBin on other RDBMS, please let us know.
|
||||
`zerobin_data` is the default model, which stores everything in files in the data
|
||||
folder. This is the recommended setup for most sites.
|
||||
|
||||
Under high load, in distributed setups or if you are not allowed to store files
|
||||
locally, you might want to switch to the `zerobin_db` model. This lets you store
|
||||
your data in a database. Basically all databases that are supported by
|
||||
[PDO](http://php.net/manual/en/book.pdo.php) may be used. Automatic table
|
||||
creation is provided for `pdo_ibm`, `pdo_informix`, `pdo_mssql`, `pdo_mysql`,
|
||||
`pdo_oci`, `pdo_pgsql` and `pdo_sqlite`. You may want to provide a table prefix,
|
||||
if you have to share the zerobin database with another application. The table
|
||||
prefix option is called `tbl`.
|
||||
|
||||
> #### Note
|
||||
> The "zerobin_db" model has only been tested with SQLite and MySQL, although it
|
||||
would not be recommended to use SQLite in a production environment. If you gain
|
||||
any experience running ZeroBin on other RDBMS, please let us know.
|
||||
|
||||
For reference or if you want to create the table schema for yourself:
|
||||
|
||||
CREATE TABLE prefix_paste (
|
||||
dataid CHAR(16),
|
||||
data TEXT,
|
||||
dataid CHAR(16) NOT NULL,
|
||||
data BLOB,
|
||||
postdate INT,
|
||||
expiredate INT,
|
||||
opendiscussion INT,
|
||||
burnafterreading INT
|
||||
burnafterreading INT,
|
||||
meta TEXT,
|
||||
attachment MEDIUMBLOB,
|
||||
attachmentname BLOB,
|
||||
PRIMARY KEY (dataid)
|
||||
);
|
||||
|
||||
CREATE TABLE prefix_comment (
|
||||
dataid CHAR(16),
|
||||
pasteid CHAR(16),
|
||||
parentid CHAR(16),
|
||||
data TEXT,
|
||||
nickname VARCHAR(255),
|
||||
vizhash TEXT,
|
||||
postdate INT
|
||||
data BLOB,
|
||||
nickname BLOB,
|
||||
vizhash BLOB,
|
||||
postdate INT,
|
||||
PRIMARY KEY (dataid)
|
||||
);
|
||||
|
||||
For Developers
|
||||
--------------
|
||||
If you want to create your own data models, you might want to know how the
|
||||
arrays, that you have to store, look like:
|
||||
|
||||
public function create($pasteid, $paste)
|
||||
{
|
||||
$pasteid = substr(hash('md5', $paste['data']), 0, 16);
|
||||
|
||||
$paste['data'] // text
|
||||
$paste['meta']['postdate'] // int UNIX timestamp
|
||||
$paste['meta']['expire_date'] // int UNIX timestamp
|
||||
$paste['meta']['opendiscussion'] // true (if false it is unset)
|
||||
$paste['meta']['burnafterreading'] // true (if false it is unset; if true, then opendiscussion is unset)
|
||||
}
|
||||
CREATE INDEX parent ON prefix_comment(pasteid);
|
||||
|
||||
public function createComment($pasteid, $parentid, $commentid, $comment)
|
||||
{
|
||||
$pasteid // the id of the paste this comment belongs to
|
||||
$parentid // the id of the parent of this comment, may be the paste id itself
|
||||
$commentid = substr(hash('md5', $paste['data']), 0, 16);
|
||||
|
||||
$comment['data'] // text
|
||||
$comment['meta']['nickname'] // text or null (if anonymous)
|
||||
$comment['meta']['vizhash'] // text or null (if anonymous)
|
||||
$comment['meta']['postdate'] // int UNIX timestamp
|
||||
}
|
||||
|
||||
CREATE TABLE prefix_config (
|
||||
id CHAR(16) NOT NULL, value TEXT, PRIMARY KEY (id)
|
||||
);
|
||||
INSERT INTO prefix_config VALUES('VERSION', '0.22');
|
||||
|
||||
77
README.md
77
README.md
@@ -1,4 +1,4 @@
|
||||
# ZeroBin 0.20
|
||||
# ZeroBin 0.22
|
||||
|
||||
ZeroBin is a minimalist, opensource online pastebin where the server has zero
|
||||
knowledge of pasted data.
|
||||
@@ -12,16 +12,16 @@ without loosing any data.
|
||||
|
||||
## What ZeroBin provides
|
||||
|
||||
- As a server administrator you don't have to worry if your users post content
|
||||
+ As a server administrator you don't have to worry if your users post content
|
||||
that is considered illegal in your country. You have no knowledge of any
|
||||
pastes content. If requested or enforced, you can delete any paste from your
|
||||
system.
|
||||
of the pastes content. If requested or enforced, you can delete any paste from
|
||||
your system.
|
||||
|
||||
- Pastebin like system to store text documents, code samples, etc.
|
||||
+ Pastebin-like system to store text documents, code samples, etc.
|
||||
|
||||
- Encryption of data sent to server, even if it does not provide HTTPS.
|
||||
+ Encryption of data sent to server, even if it does not provide HTTPS.
|
||||
|
||||
- Possibility to set a password which is required to read the paste. It further
|
||||
+ Possibility to set a password which is required to read the paste. It further
|
||||
protects a paste and prevents people stumbling upon your paste's link
|
||||
from being able to read it without the password.
|
||||
|
||||
@@ -29,6 +29,15 @@ without loosing any data.
|
||||
|
||||
- As a user you have to trust the server administrator, your internet provider
|
||||
and any country the traffic passes not to inject any malicious javascript code.
|
||||
Ideally, the ZeroBin installation used would provide HTTPS, secured by
|
||||
[HSTS](https://en.wikipedia.org/wiki/HTTP_Strict_Transport_Security) and
|
||||
[HKPH](https://en.wikipedia.org/wiki/HTTP_Public_Key_Pinning) using a
|
||||
certificate either validated by a trusted third party (check the certificate
|
||||
when first using a new ZeroBin instance) or self-signed by the server operator,
|
||||
validated using a
|
||||
[DNSSEC](https://en.wikipedia.org/wiki/Domain_Name_System_Security_Extensions) protected
|
||||
[DANE](https://en.wikipedia.org/wiki/DNS-based_Authentication_of_Named_Entities)
|
||||
record.
|
||||
|
||||
- The "key" used to encrypt the paste is part of the URL. If you publicly post
|
||||
the URL of a paste that is not password-protected, everybody can read it.
|
||||
@@ -43,48 +52,38 @@ without loosing any data.
|
||||
Some features are optional and can be enabled or disabled in the [configuration
|
||||
file](https://github.com/elrido/ZeroBin/wiki/Configuration):
|
||||
|
||||
- Password protection
|
||||
* Password protection
|
||||
|
||||
- Discussions
|
||||
* Discussions
|
||||
|
||||
- Expiration times, including a "forever" and "burn after reading" option
|
||||
* Expiration times, including a "forever" and "burn after reading" option
|
||||
|
||||
- Syntax highlighting using prettify.js, including 4 prettify themes
|
||||
* Markdown format support for HTML formatted pastes
|
||||
|
||||
- Templates: By default there is a bootstrap based and a "classic ZeroBin" theme
|
||||
* Syntax highlighting for source code using prettify.js, including 4 prettify themes
|
||||
|
||||
* File upload support, images get displayed (disabled by default, possibility to adjust size limit)
|
||||
|
||||
* Templates: By default there is a bootstrap CSS and a "classic ZeroBin" theme
|
||||
and it is easy to adapt these to your own websites layout or create your own.
|
||||
|
||||
* Translation system and automatic browser language detection (if enabled in browser)
|
||||
|
||||
* Language selection (disabled by default, as it uses a session cookie)
|
||||
|
||||
## Further resources
|
||||
|
||||
- [Installation guide](https://github.com/elrido/ZeroBin/wiki/Installation)
|
||||
* [Installation guide](https://github.com/elrido/ZeroBin/wiki/Installation)
|
||||
|
||||
- [Configuration guide](https://github.com/elrido/ZeroBin/wiki/Configuration)
|
||||
* [Upgrading from 0.19 Alpha](https://github.com/elrido/ZeroBin/wiki/Upgrading-from-ZeroBin-0.19-Alpha)
|
||||
|
||||
- [Developer guide](https://github.com/elrido/ZeroBin/wiki/Development)
|
||||
* [Configuration guide](https://github.com/elrido/ZeroBin/wiki/Configuration)
|
||||
|
||||
* [Templates](https://github.com/elrido/ZeroBin/wiki/Templates)
|
||||
|
||||
* [Translation guide](https://github.com/elrido/ZeroBin/wiki/Translation)
|
||||
|
||||
* [Developer guide](https://github.com/elrido/ZeroBin/wiki/Development)
|
||||
|
||||
Run into any issues? Have ideas for further developments? Please
|
||||
[report](https://github.com/elrido/ZeroBin/issues) them!
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
Copyright (c) 2012 Sébastien SAUVAGE (sebsauvage.net)
|
||||
|
||||
This software is provided 'as-is', without any express or implied warranty.
|
||||
In no event will the authors be held liable for any damages arising from
|
||||
the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must
|
||||
not claim that you wrote the original software. If you use this
|
||||
software in a product, an acknowledgment in the product documentation
|
||||
would be appreciated but is not required.
|
||||
|
||||
2. Altered source versions must be plainly marked as such, and must
|
||||
not be misrepresented as being the original software.
|
||||
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
1
cfg/.gitignore
vendored
Normal file
1
cfg/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/conf.ini
|
||||
@@ -5,7 +5,7 @@
|
||||
; @link http://sebsauvage.net/wiki/doku.php?id=php:zerobin
|
||||
; @copyright 2012 Sébastien SAUVAGE (sebsauvage.net)
|
||||
; @license http://www.opensource.org/licenses/zlib-license.php The zlib/libpng License
|
||||
; @version 0.20
|
||||
; @version 0.22
|
||||
|
||||
[main]
|
||||
; enable or disable the discussion feature, defaults to true
|
||||
@@ -14,17 +14,21 @@ discussion = true
|
||||
; preselect the discussion feature, defaults to false
|
||||
opendiscussion = false
|
||||
|
||||
; enable or disable syntax highlighting, defaults to true
|
||||
syntaxhighlighting = true
|
||||
; enable or disable the password feature, defaults to true
|
||||
password = true
|
||||
|
||||
; (optional) set a syntax highlighting theme, as found in css/prettify/
|
||||
; syntaxhighlightingtheme = "sons-of-obsidian"
|
||||
; enable or disable the file upload feature, defaults to false
|
||||
fileupload = false
|
||||
|
||||
; preselect the burn-after-reading feature, defaults to false
|
||||
burnafterreadingselected = false
|
||||
|
||||
; enable or disable the password feature, defaults to true
|
||||
password = true
|
||||
; which display mode to preselect by default, defaults to "plaintext"
|
||||
; make sure the value exists in [formatter_options]
|
||||
defaultformatter = "plaintext"
|
||||
|
||||
; (optional) set a syntax highlighting theme, as found in css/prettify/
|
||||
; syntaxhighlightingtheme = "sons-of-obsidian"
|
||||
|
||||
; size limit per paste or comment in bytes, defaults to 2 Mibibytes
|
||||
sizelimit = 2097152
|
||||
@@ -39,11 +43,24 @@ template = "bootstrap"
|
||||
; use "1.7" if you are upgrading from a ZeroBin Alpha 0.19 installation
|
||||
base64version = "2.1.9"
|
||||
|
||||
; by default ZeroBin will guess the visitors language based on the browsers
|
||||
; settings. Optionally you can enable the language selection menu, which uses
|
||||
; a session cookie to store the choice until the browser is closed.
|
||||
languageselection = false
|
||||
|
||||
; set the language your installs defaults to, defaults to English
|
||||
; if this is set and language selection is disabled, this will be the only language
|
||||
; languagedefault = "en"
|
||||
|
||||
[expire]
|
||||
; expire value that is selected per default
|
||||
; make sure the value exists in [expire_options]
|
||||
default = "1week"
|
||||
|
||||
; optionally the "clone" button can be disabled on expiring pastes
|
||||
; note that this only hides the button, copy & paste is still possible
|
||||
; clone = false
|
||||
|
||||
[expire_options]
|
||||
; Set each one of these to the number of seconds in the expiration period,
|
||||
; or 0 if it should never expire
|
||||
@@ -57,22 +74,22 @@ default = "1week"
|
||||
1year = 31536000
|
||||
never = 0
|
||||
|
||||
[expire_labels]
|
||||
; descriptive labels for the expiration times
|
||||
; must match those in [expire_options]
|
||||
5min = "5 minutes"
|
||||
10min = "10 minutes"
|
||||
1hour = "1 hour"
|
||||
1day = "1 day"
|
||||
1week = "1 week"
|
||||
1month = "1 month"
|
||||
1year = "1 year"
|
||||
never = "Never"
|
||||
[formatter_options]
|
||||
; Set available formatters, their order and their labels
|
||||
plaintext = "Plain Text"
|
||||
syntaxhighlighting = "Source Code"
|
||||
markdown = "Markdown"
|
||||
|
||||
[traffic]
|
||||
; time limit between calls from the same IP address in seconds
|
||||
; Set this to 0 to disable rate limiting.
|
||||
limit = 10
|
||||
|
||||
; (optional) if your website runs behind a reverse proxy or load balancer,
|
||||
; set the HTTP header containing the visitors IP address, i.e. X_FORWARDED_FOR
|
||||
; header = "X_FORWARDED_FOR"
|
||||
|
||||
; directory to store the traffic limits in
|
||||
dir = PATH "data"
|
||||
|
||||
[model]
|
||||
@@ -99,5 +116,4 @@ dir = PATH "data"
|
||||
;dsn = "sqlite:" PATH "data/db.sq3"
|
||||
;usr = null
|
||||
;pwd = null
|
||||
;opt[12] = true ; PDO::ATTR_PERSISTENT
|
||||
|
||||
;opt[12] = true ; PDO::ATTR_PERSISTENT
|
||||
11
css/bootstrap/bootstrap-3.3.5.css
vendored
11
css/bootstrap/bootstrap-3.3.5.css
vendored
File diff suppressed because one or more lines are too long
8061
css/bootstrap/darkstrap-0.9.3.css
Normal file
8061
css/bootstrap/darkstrap-0.9.3.css
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,8 +1,41 @@
|
||||
/* ZeroBin 0.20 - http://sebsauvage.net/wiki/doku.php?id=php:zerobin */
|
||||
/* ZeroBin 0.22 - http://sebsauvage.net/wiki/doku.php?id=php:zerobin */
|
||||
|
||||
|
||||
body {
|
||||
padding: 70px 0 30px 0;
|
||||
padding: 0 0 30px;
|
||||
}
|
||||
|
||||
body.navbar-spacing {
|
||||
padding-top: 70px;
|
||||
}
|
||||
|
||||
.navbar-nav {
|
||||
margin: 0 8px;
|
||||
}
|
||||
|
||||
.dropdown-menu > li > label, .dropdown-menu > li > div {
|
||||
clear: both;
|
||||
display: block;
|
||||
font-weight: normal;
|
||||
line-height: 1.42857;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.dropdown-menu > li > label {
|
||||
color: #333;
|
||||
padding: 3px 20px 3px 40px;
|
||||
}
|
||||
|
||||
.dropdown-menu > li > div {
|
||||
color: #777;
|
||||
padding: 3px 20px;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
#image img {
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
#deletelink {
|
||||
@@ -21,8 +54,20 @@ body {
|
||||
margin: 5px 0;
|
||||
}
|
||||
|
||||
#comments, #comments button {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.comment {
|
||||
border-left: 1px solid #ccc;
|
||||
padding: 5px 0 5px 5px;
|
||||
padding: 5px 0 5px 10px;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
footer h4 {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
li.L0, li.L1, li.L2, li.L3, li.L5, li.L6, li.L7, li.L8 {
|
||||
list-style-type: decimal !important;
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
/* ZeroBin 0.20 - http://sebsauvage.net/wiki/doku.php?id=php:zerobin */
|
||||
/* ZeroBin 0.22 - http://sebsauvage.net/wiki/doku.php?id=php:zerobin */
|
||||
|
||||
|
||||
/* CSS Reset from YUI 3.4.1 (build 4118) - Copyright 2011 Yahoo! Inc. All rights reserved.
|
||||
@@ -18,16 +18,16 @@ html {
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
font-family: Helvetica, Arial, sans-serif;
|
||||
font-size: 0.9em;
|
||||
margin-bottom: 15px;
|
||||
padding-left: 60px;
|
||||
padding-right: 60px;
|
||||
}
|
||||
|
||||
a { color: #0f388f; }
|
||||
|
||||
h1 {
|
||||
a { color: #0f388f; cursor:pointer; }
|
||||
|
||||
h1.title {
|
||||
font-size: 3.5em;
|
||||
font-weight: bold;
|
||||
color: #000;
|
||||
@@ -36,7 +36,7 @@ h1 {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
h1:before {
|
||||
h1.title:before {
|
||||
content: attr(title);
|
||||
position: absolute;
|
||||
color: rgba(255,255,255,0.15);
|
||||
@@ -45,7 +45,7 @@ h1:before {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
h2 {
|
||||
h2.title {
|
||||
color: #000;
|
||||
font-size: 1em;
|
||||
display: inline;
|
||||
@@ -54,18 +54,17 @@ h2 {
|
||||
position: relative;
|
||||
bottom: 8px;
|
||||
}
|
||||
|
||||
h3 {
|
||||
|
||||
h3.title {
|
||||
color: #94a3b4;
|
||||
font-size: 0.7em;
|
||||
display: inline;
|
||||
margin-top: 10px;
|
||||
position: relative;
|
||||
bottom: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
#aboutbox {
|
||||
font-size: 1.2em;
|
||||
color: #94a3b4;
|
||||
padding: 4px 8px 4px 16px;
|
||||
position: relative;
|
||||
@@ -77,15 +76,12 @@ h3 {
|
||||
|
||||
#aboutbox a { color: #94a3b4; }
|
||||
|
||||
#message, #cleartext, .replymessage {
|
||||
#message, #cleartext, #prettymessage, #attachment, .replymessage {
|
||||
clear: both;
|
||||
color: #000;
|
||||
background-color: #fff;
|
||||
white-space: pre-wrap;
|
||||
font-family: Consolas, "Lucida Console", "DejaVu Sans Mono", Monaco, monospace;
|
||||
font-size: 9pt;
|
||||
border: 1px solid #28343F;
|
||||
padding: 5px;
|
||||
box-sizing: border-box;
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
@@ -94,6 +90,17 @@ h3 {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#message, .replymessage {
|
||||
padding: 5px;
|
||||
white-space: pre-wrap;
|
||||
font-family: Consolas, "Lucida Console", "DejaVu Sans Mono", Monaco, monospace;
|
||||
}
|
||||
|
||||
#image img {
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
#status {
|
||||
clear: both;
|
||||
padding: 5px 10px;
|
||||
@@ -119,7 +126,7 @@ h3 {
|
||||
|
||||
#copyhint { color: #666; font-size: 0.85em; }
|
||||
|
||||
button, .button, #expiration {
|
||||
button, .button {
|
||||
color: #fff;
|
||||
background-color: #323b47;
|
||||
background-repeat: no-repeat;
|
||||
@@ -127,7 +134,7 @@ button, .button, #expiration {
|
||||
padding: 4px 8px;
|
||||
font-size: 1em;
|
||||
margin-right: 5px;
|
||||
display: inline;
|
||||
display: inline-block;
|
||||
background-image: linear-gradient(bottom, #323b47 0, #51606e 100%);
|
||||
background-image: -o-linear-gradient(bottom, #323b47 0, #51606e 100%);
|
||||
background-image: -moz-linear-gradient(bottom, #323b47 0, #51606e 100%);
|
||||
@@ -178,7 +185,7 @@ button img {
|
||||
top: 2px;
|
||||
}
|
||||
|
||||
#expiration, #rawtextbutton, #burnafterreadingoption, #opendisc {
|
||||
.button {
|
||||
background-color: #414d5a;
|
||||
padding: 6px 8px;
|
||||
margin: 0 5px 0 0;
|
||||
@@ -186,14 +193,14 @@ button img {
|
||||
bottom: 1px; /* WTF ? Why is this shifted by 1 pixel ? */
|
||||
}
|
||||
|
||||
#expiration select {
|
||||
.button select {
|
||||
color: #eee;
|
||||
background: transparent;
|
||||
border: none;
|
||||
}
|
||||
|
||||
|
||||
#expiration select option {
|
||||
.button select option {
|
||||
color:#eee;
|
||||
background: #414d5a;
|
||||
}
|
||||
@@ -202,7 +209,7 @@ button img {
|
||||
padding: 1px 0 1px 0;
|
||||
}
|
||||
|
||||
#remainingtime {
|
||||
#remainingtime, #password {
|
||||
color: #94a3b4;
|
||||
display: inline;
|
||||
font-size: 0.85em;
|
||||
@@ -234,7 +241,7 @@ input {
|
||||
font-weight: bold !important;
|
||||
}
|
||||
|
||||
.nonworking {
|
||||
#image, .nonworking {
|
||||
background-color: #fff;
|
||||
color: #000;
|
||||
width: 100%;
|
||||
@@ -244,7 +251,7 @@ input {
|
||||
-webkit-border-radius: 4px;
|
||||
-moz-border-radius: 4px;
|
||||
border-radius: 4px;
|
||||
padding: 5px;
|
||||
padding: 5px 0;
|
||||
}
|
||||
|
||||
.hidden { display: none !important; }
|
||||
@@ -279,7 +286,7 @@ input {
|
||||
min-width: 200px;
|
||||
}
|
||||
|
||||
h4 {
|
||||
h4.title {
|
||||
font-size: 1.2em;
|
||||
color: #94a3b4;
|
||||
font-style: italic;
|
||||
@@ -386,3 +393,41 @@ img.vizhash {
|
||||
color: #000000;
|
||||
font-size: 1.2em;
|
||||
}
|
||||
|
||||
#prettyprint.prettyprinted {
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
#cleartext {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
#cleartext * {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
#cleartext ol {
|
||||
list-style: auto;
|
||||
margin-left: 15px;
|
||||
}
|
||||
|
||||
#cleartext ul {
|
||||
list-style: disc;
|
||||
margin-left: 15px;
|
||||
}
|
||||
|
||||
#cleartext h1, #cleartext h2, #cleartext h3, #cleartext h4, #cleartext h5, #cleartext h6 {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
#cleartext h1 {
|
||||
font-size: 2em;
|
||||
}
|
||||
|
||||
#cleartext h2 {
|
||||
font-size: 1.5em;
|
||||
}
|
||||
|
||||
#cleartext h3 {
|
||||
font-size: 1.2em;
|
||||
}
|
||||
|
||||
BIN
favicon.ico
Normal file
BIN
favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.7 KiB |
139
i18n/de.json
Normal file
139
i18n/de.json
Normal file
@@ -0,0 +1,139 @@
|
||||
{
|
||||
"en": "de",
|
||||
"Paste does not exist, has expired or has been deleted.":
|
||||
"Diesen Text gibt es nicht, er ist abgelaufen oder wurde gelöscht.",
|
||||
"ZeroBin requires php 5.2.6 or above to work. Sorry.":
|
||||
"ZeroBin benötigt PHP 5.2.6 oder höher, um zu funktionieren. Sorry.",
|
||||
"ZeroBin requires configuration section [%s] to be present in configuration file.":
|
||||
"ZeroBin benötigt den Konfigurationsabschnitt [%s] in der Konfigurationsdatei um zu funktionieren.",
|
||||
"Please wait %d seconds between each post.":
|
||||
"Bitte warte %d Sekunden zwischen dem Absenden.",
|
||||
"Paste is limited to %s of encrypted data.":
|
||||
"Texte sind auf %s verschlüsselte Datenmenge beschränkt.",
|
||||
"Invalid data.":
|
||||
"Ungültige Daten.",
|
||||
"You are unlucky. Try again.":
|
||||
"Du hast Pech. Versuchs nochmal.",
|
||||
"Error saving comment. Sorry.":
|
||||
"Fehler beim Speichern des Kommentars. Sorry.",
|
||||
"Error saving paste. Sorry.":
|
||||
"Fehler beim Speichern des Textes. Sorry.",
|
||||
"Invalid paste ID.":
|
||||
"Ungültige Text-ID.",
|
||||
"Paste is not of burn-after-reading type.":
|
||||
"Text ist kein \"Einmal\"-Typ.",
|
||||
"Wrong deletion token. Paste was not deleted.":
|
||||
"Falscher Lösch-Code. Text wurde nicht gelöscht.",
|
||||
"Paste was properly deleted.":
|
||||
"Text wurde erfolgreich gelöscht.",
|
||||
"ZeroBin": "ZeroBin",
|
||||
"ZeroBin is a minimalist, opensource online pastebin where the server has zero knowledge of pasted data. Data is encrypted/decrypted <i>in the browser</i> using 256 bits AES. More information on the <a href=\"https://github.com/elrido/ZeroBin/wiki\">project page</a>.":
|
||||
"ZeroBin ist ein minimalistischer, quelloffener \"Pastebin\"-artiger Dienst, bei dem der Server keinerlei Kenntnis der Inhalte hat. Die Daten werden <i>im Browser</i> mit 256 Bit AES ver- und entschlüsselt. Weitere Informationen sind auf der <a href=\"https://github.com/elrido/ZeroBin/wiki\">Projektseite</a> zu finden.",
|
||||
"Because ignorance is bliss":
|
||||
"Unwissenheit ist ein Segen",
|
||||
"Javascript is required for ZeroBin to work.<br />Sorry for the inconvenience.":
|
||||
"Javascript ist eine Voraussetzung, um ZeroBin zu nutzen.<br />Bitte entschuldige die Unannehmlichkeiten.",
|
||||
"ZeroBin requires a modern browser to work.":
|
||||
"ZeroBin setzt einen modernen Browser voraus, um funktionieren zu können.",
|
||||
"Still using Internet Explorer? Do yourself a favor, switch to a modern browser:":
|
||||
"Du benutzt immer noch den Internet Explorer? Tu Dir einen Gefallen und wechsle zu einem moderneren Browser:",
|
||||
"New":
|
||||
"Neu",
|
||||
"Send":
|
||||
"Senden",
|
||||
"Clone":
|
||||
"Klonen",
|
||||
"Raw text":
|
||||
"Reiner Text",
|
||||
"Expires":
|
||||
"Ablaufzeit",
|
||||
"Burn after reading":
|
||||
"Einmal-Text",
|
||||
"Open discussion":
|
||||
"Diskussion eröffnen",
|
||||
"Password (recommended)":
|
||||
"Passwort (empfohlen)",
|
||||
"Discussion":
|
||||
"Diskussion",
|
||||
"Toggle navigation":
|
||||
"Navigation umschalten",
|
||||
"%d seconds": ["%d Sekunde", "%d Sekunden"],
|
||||
"%d minutes": ["%d Minute", "%d Minuten"],
|
||||
"%d hours": ["%d Stunde", "%d Stunden"],
|
||||
"%d days": ["%d Tag", "%d Tage"],
|
||||
"%d weeks": ["%d Woche", "%d Wochen"],
|
||||
"%d months": ["%d Monat", "%d Monate"],
|
||||
"%d years": ["%d Jahr", "%d Jahre"],
|
||||
"Never":
|
||||
"Nie",
|
||||
"Note: This is a test service: Data may be deleted anytime. Kittens will die if you abuse this service.":
|
||||
"Hinweis: Dies ist ein Versuchsdienst. Daten können jederzeit gelöscht werden. Kätzchen werden sterben wenn Du diesen Dienst missbrauchst.",
|
||||
"This document will expire in %d seconds.":
|
||||
["Dieses Dokument läuft in einer Sekunde ab.", "Dieses Dokument läuft in %d Sekunden ab."],
|
||||
"This document will expire in %d minutes.":
|
||||
["Dieses Dokument läuft in einer Minute ab.", "Dieses Dokument läuft in %d Minuten ab."],
|
||||
"This document will expire in %d hours.":
|
||||
["Dieses Dokument läuft in einer Stunde ab.", "Dieses Dokument läuft in %d Stunden ab."],
|
||||
"This document will expire in %d days.":
|
||||
["Dieses Dokument läuft in einem Tag ab.", "Dieses Dokument läuft in %d Tagen ab."],
|
||||
"This document will expire in %d months.":
|
||||
["Dieses Dokument läuft in einem Monat ab.", "Dieses Dokument läuft in %d Monaten ab."],
|
||||
"Please enter the password for this paste:":
|
||||
"Bitte gib das Passwort für diesen Text ein:",
|
||||
"Could not decrypt data (Wrong key?)":
|
||||
"Konnte Daten nicht entschlüsseln (Falscher Schlüssel?)",
|
||||
"Could not delete the paste, it was not stored in burn after reading mode.":
|
||||
"Konnte den Text nicht löschen, er wurde nicht im Einmal-Modus gespeichert.",
|
||||
"FOR YOUR EYES ONLY. Don't close this window, this message can't be displayed again.":
|
||||
"DIESER TEXT IST NUR FÜR DICH GEDACHT. Schliesse das Fenster nicht, diese Nachricht kann nur einmal geöffnet werden.",
|
||||
"Could not decrypt comment; Wrong key?":
|
||||
"Konnte Kommentar nicht entschlüsseln; Falscher Schlüssel?",
|
||||
"Reply":
|
||||
"Antworten",
|
||||
"Anonymous":
|
||||
"Anonym",
|
||||
"Anonymous avatar (Vizhash of the IP address)":
|
||||
"Anonymer Avatar (Vizhash der IP-Addresse)",
|
||||
"Add comment":
|
||||
"Kommentar hinzufügen",
|
||||
"Optional nickname...":
|
||||
"Optionales Pseudonym...",
|
||||
"Post comment":
|
||||
"Kommentar absenden",
|
||||
"Sending comment...":
|
||||
"Sende Kommentar...",
|
||||
"Comment posted.":
|
||||
"Kommentar gesendet.",
|
||||
"Could not refresh display: %s":
|
||||
"Konnte Ansicht nicht aktualisieren: %s",
|
||||
"unknown status":
|
||||
"Unbekannter Grund",
|
||||
"server error or not responding":
|
||||
"Fehler auf dem Server oder keine Antwort vom Server",
|
||||
"Could not post comment: %s":
|
||||
"Konnte Kommentar nicht senden: %s",
|
||||
"Sending paste (Please move your mouse for more entropy)...":
|
||||
"Sende Text (Bitte bewege Deine Maus um die Entropie zu erhöhen)...",
|
||||
"Sending paste...":
|
||||
"Sende Text...",
|
||||
"Your paste is <a id=\"pasteurl\" href=\"%s\">%s</a> <span id=\"copyhint\">(Hit [Ctrl]+[c] to copy)</span>":
|
||||
"Dein Text ist unter <a id=\"pasteurl\" href=\"%s\">%s</a> zu finden <span id=\"copyhint\">(Drücke [Strg]+[c] um den Link zu kopieren)</span>",
|
||||
"Delete data":
|
||||
"Lösche Daten",
|
||||
"Could not create paste: %s":
|
||||
"Konnte Text nicht erstellen: %s",
|
||||
"Cannot decrypt paste: Decryption key missing in URL (Did you use a redirector or an URL shortener which strips part of the URL?)":
|
||||
"Konnte Text nicht entschlüsseln: Der Schlüssel fehlt in der Adresse (Hast Du eine Umleitung oder einen URL-Verkürzer benutzt, der Teile der Adresse entfernt?)",
|
||||
"Format": "Format",
|
||||
"Plain Text": "Nur Text",
|
||||
"Source Code": "Quellcode",
|
||||
"Markdown": "Markdown",
|
||||
"Download attachment": "Anhang herunterladen",
|
||||
"Cloned file attached.": "Kopierte Datei angehängt.",
|
||||
"Attach a file": "Datei anhängen",
|
||||
"Remove attachment": "Anhang entfernen",
|
||||
"Your browser does not support uploading encrypted files. Please use a newer browser.":
|
||||
"Dein Browser unterstützt das hochladen von verschlüsselten Dateien nicht. Bitte verwende einen neueren Browser.",
|
||||
"Invalid attachment.": "Ungültiger Datei-Anhang.",
|
||||
"Options": "Optionen"
|
||||
}
|
||||
148
i18n/fr.json
Normal file
148
i18n/fr.json
Normal file
@@ -0,0 +1,148 @@
|
||||
{
|
||||
"en": "fr",
|
||||
"Paste does not exist, has expired or has been deleted.":
|
||||
"Le paste n'existe pas, a expiré, ou a été supprimé.",
|
||||
"ZeroBin requires php 5.2.6 or above to work. Sorry.":
|
||||
"Désolé, ZeroBin nécessite php 5.2.6 ou supérieur pour fonctionner.",
|
||||
"ZeroBin requires configuration section [%s] to be present in configuration file.":
|
||||
"ZeroBin a besoin de la section de configuration [%s] dans le fichier de configuration pour fonctionner.",
|
||||
"Please wait %d seconds between each post.":
|
||||
"Merci d'attendre %d secondes entre chaque publication.",
|
||||
"Paste is limited to %s of encrypted data.":
|
||||
"Le paste est limité à %s de données chiffrées.",
|
||||
"Invalid data.":
|
||||
"Données invalides.",
|
||||
"You are unlucky. Try again.":
|
||||
"Pas de chance. Essayez encore.",
|
||||
"Error saving comment. Sorry.":
|
||||
"Erreur lors de la sauvegarde du commentaire.",
|
||||
"Error saving paste. Sorry.":
|
||||
"Erreur lors de la sauvegarde du paste. Désolé.",
|
||||
"Invalid paste ID.":
|
||||
"ID du paste invalide.",
|
||||
"Paste is not of burn-after-reading type.":
|
||||
"Le paste n'est pas de type \"Effacer après lecture\".",
|
||||
"Wrong deletion token. Paste was not deleted.":
|
||||
"Jeton de suppression incorrect. Le paste n'a pas été supprimé.",
|
||||
"Paste was properly deleted.":
|
||||
"Le paste a été correctement supprimé.",
|
||||
"ZeroBin": "ZeroBin",
|
||||
"ZeroBin is a minimalist, opensource online pastebin where the server has zero knowledge of pasted data. Data is encrypted/decrypted <i>in the browser</i> using 256 bits AES. More information on the <a href=\"https://github.com/elrido/ZeroBin/wiki\">project page</a>.":
|
||||
"Zerobin est un 'pastebin' (ou gestionnaire d'extraits de texte et de code source) minimaliste et open source, dans lequel le serveur n'a aucune connaissance des données envoyées. Les données sont chiffrées/déchiffrées <i>dans le navigateur</i> par un chiffrage AES 256 bits. Plus d'informations sur <a href=\"https://github.com/elrido/ZeroBin/wiki\">la page du projet</a>.",
|
||||
"Because ignorance is bliss":
|
||||
"Parce que l'ignorance est le bonheur",
|
||||
"Javascript is required for ZeroBin to work.<br />Sorry for the inconvenience.":
|
||||
"Javascript est requis pour faire fonctionner ZeroBin. <br />Désolé pour cet inconvénient.",
|
||||
"ZeroBin requires a modern browser to work.":
|
||||
"ZeroBin nécessite un navigateur moderne pour fonctionner.",
|
||||
"Still using Internet Explorer? Do yourself a favor, switch to a modern browser:":
|
||||
"Encore sur Internet Explorer ? Faites-vous une faveur, passez à un navigateur moderne :",
|
||||
"New":
|
||||
"Nouveau",
|
||||
"Send":
|
||||
"Envoyer",
|
||||
"Clone":
|
||||
"Cloner",
|
||||
"Raw text":
|
||||
"Texte brut",
|
||||
"Expires":
|
||||
"Expire",
|
||||
"Burn after reading":
|
||||
"Effacer après lecture",
|
||||
"Open discussion":
|
||||
"Autoriser la discussion",
|
||||
"Password (recommended)":
|
||||
"Mot de passe (recommandé)",
|
||||
"Discussion":
|
||||
"Discussion",
|
||||
"Toggle navigation":
|
||||
"Basculer la navigation",
|
||||
"%d seconds": ["%d seconde", "%d secondes"],
|
||||
"%d minutes": ["%d minute", "%d minutes"],
|
||||
"%d hours": ["%d heure", "%d heures"],
|
||||
"%d days": ["%d jour", "%d jours"],
|
||||
"%d weeks": ["%d semaine", "%d semaines"],
|
||||
"%d months": ["%d mois", "%d mois"],
|
||||
"%d years": ["%d an", "%d ans"],
|
||||
"Never":
|
||||
"Jamais",
|
||||
"Note: This is a test service: Data may be deleted anytime. Kittens will die if you abuse this service.":
|
||||
"Note : Ceci est un service de test : les données peuvent être supprimées à tout moment. Des chatons mourront si vous utilisez ce service de manière abusive.",
|
||||
"This document will expire in %d seconds.":
|
||||
["Ce document expirera dans %d seconde.", "Ce document expirera dans %d secondes."],
|
||||
"This document will expire in %d minutes.":
|
||||
["Ce document expirera dans %d minute.", "Ce document expirera dans %d minutes."],
|
||||
"This document will expire in %d hours.":
|
||||
["Ce document expirera dans %d heure.", "Ce document expirera dans %d heures."],
|
||||
"This document will expire in %d days.":
|
||||
["Ce document expirera dans %d jour.", "Ce document expirera dans %d jours."],
|
||||
"This document will expire in %d months.":
|
||||
["Ce document expirera dans %d mois.", "Ce document expirera dans %d mois."],
|
||||
"Please enter the password for this paste:":
|
||||
"Entrez le mot de passe pour ce paste:",
|
||||
"Could not decrypt data (Wrong key?)":
|
||||
"Impossible de déchiffrer les données (mauvaise clé ?)",
|
||||
"Could not delete the paste, it was not stored in burn after reading mode.":
|
||||
"Impossible de supprimer le paste, car il n'a pas été stoclé en mode \"Effacer après lecture\".",
|
||||
"FOR YOUR EYES ONLY. Don't close this window, this message can't be displayed again.":
|
||||
"POUR VOS YEUX UNIQUEMENT. Ne fermez pas cette fenêtre, ce paste ne pourra plus être affiché.",
|
||||
"Could not decrypt comment; Wrong key?":
|
||||
"Umpossible de déchiffrer le commentaire ; mauvaise clé ?",
|
||||
"Reply":
|
||||
"Répondre",
|
||||
"Anonymous":
|
||||
"Anonyme",
|
||||
"Anonymous avatar (Vizhash of the IP address)":
|
||||
"Avatar anonyme (Vizhash de l'adresse IP)",
|
||||
"Add comment":
|
||||
"Ajouter un commentaire",
|
||||
"Optional nickname...":
|
||||
"Pseudonyme optionnel...",
|
||||
"Post comment":
|
||||
"Poster le commentaire",
|
||||
"Sending comment...":
|
||||
"Envoi du commentaire...",
|
||||
"Comment posted.":
|
||||
"Commentaire posté.",
|
||||
"Could not refresh display: %s":
|
||||
"Impossible de rafraichir l'affichage : %s",
|
||||
"unknown status":
|
||||
"Statut inconnu",
|
||||
"server error or not responding":
|
||||
"Le serveur ne répond pas ou a rencontré une erreur",
|
||||
"Could not post comment: %s":
|
||||
"Impossible de poster le commentaire : %s",
|
||||
"Sending paste (Please move your mouse for more entropy)...":
|
||||
"Envoi du paste (Merci de bouger votre souris pour plus d'entropie)...",
|
||||
"Sending paste...":
|
||||
"Envoi du paste...",
|
||||
"Your paste is <a id=\"pasteurl\" href=\"%s\">%s</a> <span id=\"copyhint\">(Hit [Ctrl]+[c] to copy)</span>":
|
||||
"Votre paste est disponible à l'adresse <a id=\"pasteurl\" href=\"%s\">%s</a> <span id=\"copyhint\">(Appuyez sur [Ctrl]+[c] pour copier)</span>",
|
||||
"Delete data":
|
||||
"Supprimer les données du paste",
|
||||
"Could not create paste: %s":
|
||||
"Impossible de créer le 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?)":
|
||||
"Impossible de déchiffrer le paste : Clé de déchiffrage manquante dans l'URL (Avez-vous utilisé un redirecteur ou un site de réduction d'URL qui supprime une partie de l'URL ?)",
|
||||
"B": "o",
|
||||
"KiB": "Kio",
|
||||
"MiB": "Mio",
|
||||
"GiB": "Gio",
|
||||
"TiB": "Tio",
|
||||
"PiB": "Pio",
|
||||
"EiB": "Eio",
|
||||
"ZiB": "Zio",
|
||||
"YiB": "Yio",
|
||||
"Format": "Format",
|
||||
"Plain Text": "Texte brut",
|
||||
"Source Code": "Code source",
|
||||
"Markdown": "Markdown",
|
||||
"Download attachment": "Télécharger la pièce jointe",
|
||||
"Cloned file attached.": "Cloner le fichier attaché.",
|
||||
"Attach a file": "Attacher un fichier ",
|
||||
"Remove attachment": "Enlever l'attachement",
|
||||
"Your browser does not support uploading encrypted files. Please use a newer browser.":
|
||||
"Votre navigateur ne supporte pas l'envoi de fichiers chiffrés. Merci d'utiliser un navigateur plus récent.",
|
||||
"Invalid attachment.": "Attachement invalide.",
|
||||
"Options": "Options"
|
||||
}
|
||||
181
i18n/languages.json
Normal file
181
i18n/languages.json
Normal file
@@ -0,0 +1,181 @@
|
||||
{
|
||||
"ab": ["аҧсуа бызшәа", "Abkhaz"],
|
||||
"aa": ["Afaraf", "Afar"],
|
||||
"af": ["Afrikaans", "Afrikaans"],
|
||||
"ak": ["Akan", "Akan"],
|
||||
"sq": ["Shqip", "Albanian"],
|
||||
"am": ["አማርኛ", "Amharic"],
|
||||
"ar": ["العربية", "Arabic"],
|
||||
"an": ["aragonés", "Aragonese"],
|
||||
"hy": ["Հայերեն", "Armenian"],
|
||||
"as": ["অসমীয়া", "Assamese"],
|
||||
"ac": ["Ástralic", "Australian"],
|
||||
"av": ["авар мацӀ", "Avaric"],
|
||||
"ae": ["avesta", "Avestan"],
|
||||
"ay": ["aymar aru", "Aymara"],
|
||||
"az": ["azərbaycan dili", "Azerbaijani"],
|
||||
"bm": ["bamanankan", "Bambara"],
|
||||
"ba": ["башҡорт теле", "Bashkir"],
|
||||
"eu": ["euskara", "Basque"],
|
||||
"be": ["беларуская мова", "Belarusian"],
|
||||
"bn": ["বাংলা", "Bengali"],
|
||||
"bh": ["भोजपुरी", "Bihari"],
|
||||
"bi": ["Bislama", "Bislama"],
|
||||
"bs": ["bosanski jezik", "Bosnian"],
|
||||
"br": ["brezhoneg", "Breton"],
|
||||
"bg": ["български език", "Bulgarian"],
|
||||
"my": ["ဗမာစာ", "Burmese"],
|
||||
"ca": ["català", "Catalan"],
|
||||
"ch": ["Chamoru", "Chamorro"],
|
||||
"ce": ["нохчийн мотт", "Chechen"],
|
||||
"ny": ["chiCheŵa", "Chichewa"],
|
||||
"zh": ["中文", "Chinese"],
|
||||
"cv": ["чӑваш чӗлхи", "Chuvash"],
|
||||
"kw": ["Kernewek", "Cornish"],
|
||||
"co": ["corsu", "Corsican"],
|
||||
"cr": ["ᓀᐦᐃᔭᐍᐏᐣ", "Cree"],
|
||||
"hr": ["hrvatski jezik", "Croatian"],
|
||||
"cs": ["čeština", "Czech"],
|
||||
"da": ["dansk", "Danish"],
|
||||
"dv": ["ދިވެހި", "Divehi"],
|
||||
"nl": ["Nederlands", "Dutch"],
|
||||
"dz": ["རྫོང་ཁ", "Dzongkha"],
|
||||
"en": ["English", "English"],
|
||||
"eo": ["Esperanto", "Esperanto"],
|
||||
"et": ["eesti", "Estonian"],
|
||||
"ee": ["Eʋegbe", "Ewe"],
|
||||
"fo": ["føroyskt", "Faroese"],
|
||||
"fj": ["vosa Vakaviti", "Fijian"],
|
||||
"fi": ["suomi", "Finnish"],
|
||||
"fr": ["français", "French"],
|
||||
"ff": ["Fulfulde", "Fula"],
|
||||
"gl": ["galego", "Galician"],
|
||||
"ka": ["ქართული", "Georgian"],
|
||||
"de": ["Deutsch", "German"],
|
||||
"el": ["ελληνικά", "Greek"],
|
||||
"gn": ["Avañe'ẽ", "Guaraní"],
|
||||
"gu": ["ગુજરાતી", "Gujarati"],
|
||||
"ht": ["Kreyòl ayisyen", "Haitian"],
|
||||
"ha": ["هَوُسَ", "Hausa"],
|
||||
"he": ["עברית", "Hebrew"],
|
||||
"hz": ["Otjiherero", "Herero"],
|
||||
"hi": ["हिन्दी", "Hindi"],
|
||||
"ho": ["Hiri Motu", "Hiri Motu"],
|
||||
"hu": ["magyar", "Hungarian"],
|
||||
"ia": ["Interlingua", "Interlingua"],
|
||||
"ie": ["Interlingue", "Interlingue"],
|
||||
"ga": ["Gaeilge", "Irish"],
|
||||
"ig": ["Asụsụ Igbo", "Igbo"],
|
||||
"ik": ["Iñupiaq", "Inupiaq"],
|
||||
"io": ["Ido", "Ido"],
|
||||
"is": ["Íslenska", "Icelandic"],
|
||||
"it": ["italiano", "Italian"],
|
||||
"iu": ["ᐃᓄᒃᑎᑐᑦ", "Inuktitut"],
|
||||
"ja": ["日本語", "Japanese"],
|
||||
"jv": ["basa Jawa", "Javanese"],
|
||||
"kl": ["kalaallisut", "Greenlandic"],
|
||||
"kn": ["ಕನ್ನಡ", "Kannada"],
|
||||
"kr": ["Kanuri", "Kanuri"],
|
||||
"ks": ["कश्मीरी", "Kashmiri"],
|
||||
"kk": ["қазақ тілі", "Kazakh"],
|
||||
"km": ["ខ្មែរ", "Khmer"],
|
||||
"ki": ["Gĩkũyũ", "Kikuyu"],
|
||||
"rw": ["Ikinyarwanda", "Kinyarwanda"],
|
||||
"ky": ["Кыргызча", "Kyrgyz"],
|
||||
"kv": ["коми кыв", "Komi"],
|
||||
"kg": ["Kikongo", "Kongo"],
|
||||
"ko": ["한국어", "Korean"],
|
||||
"ku": ["Kurdî", "Kurdish"],
|
||||
"kj": ["Kuanyama", "Kwanyama"],
|
||||
"la": ["lingua latina", "Latin"],
|
||||
"lb": ["Lëtzebuergesch", "Luxembourgish"],
|
||||
"lg": ["Luganda", "Ganda"],
|
||||
"li": ["Limburgs", "Limburgish"],
|
||||
"ln": ["Lingála", "Lingala"],
|
||||
"lo": ["ພາສາລາວ", "Lao"],
|
||||
"lt": ["lietuvių kalba", "Lithuanian"],
|
||||
"lu": ["Tshiluba", "Luba-Katanga"],
|
||||
"lv": ["latviešu valoda", "Latvian"],
|
||||
"gv": ["Gaelg", "Manx"],
|
||||
"mk": ["македонски јазик", "Macedonian"],
|
||||
"mg": ["fiteny malagasy", "Malagasy"],
|
||||
"ms": ["بهاس ملايو", "Malay"],
|
||||
"ml": ["മലയാളം", "Malayalam"],
|
||||
"mt": ["Malti", "Maltese"],
|
||||
"mi": ["te reo Māori", "Māori"],
|
||||
"mr": ["मराठी", "Marathi"],
|
||||
"mh": ["Kajin M̧ajeļ", "Marshallese"],
|
||||
"mn": ["Монгол хэл", "Mongolian"],
|
||||
"na": ["Ekakairũ Naoero", "Nauru"],
|
||||
"nv": ["Diné bizaad", "Navajo"],
|
||||
"nd": ["isiNdebele", "Northern Ndebele"],
|
||||
"ne": ["नेपाली", "Nepali"],
|
||||
"ng": ["Owambo", "Ndonga"],
|
||||
"no": ["Norsk", "Norwegian"],
|
||||
"ii": ["ꆈꌠ꒿", "Nuosu"],
|
||||
"nr": ["isiNdebele", "Southern Ndebele"],
|
||||
"oc": ["occitan", "Occitan"],
|
||||
"oj": ["ᐊᓂᔑᓈᐯᒧᐎᓐ", "Ojibwe"],
|
||||
"om": ["Afaan Oromoo", "Oromo"],
|
||||
"or": ["ଓଡ଼ିଆ", "Oriya"],
|
||||
"os": ["ирон æвзаг", "Ossetian"],
|
||||
"pa": ["ਪੰਜਾਬੀ", "Punjabi"],
|
||||
"pi": ["पाऴि", "Pāli"],
|
||||
"fa": ["فارسی", "Persian"],
|
||||
"pl": ["polszczyzna", "Polish"],
|
||||
"ps": ["پښتو", "Pashto"],
|
||||
"pt": ["português", "Portuguese"],
|
||||
"qu": ["Runa Simi", "Quechua"],
|
||||
"rm": ["rumantsch grischun", "Romansh"],
|
||||
"rn": ["Ikirundi", "Kirundi"],
|
||||
"ro": ["limba română", "Romanian"],
|
||||
"ru": ["Русский", "Russian"],
|
||||
"sc": ["sardu", "Sardinian"],
|
||||
"sd": ["सिन्धी", "Sindhi"],
|
||||
"se": ["Davvisámegiella", "Northern Sami"],
|
||||
"sm": ["gagana fa'a Samoa", "Samoan"],
|
||||
"sg": ["yângâ tî sängö", "Sango"],
|
||||
"sr": ["српски језик", "Serbian"],
|
||||
"gd": ["Gàidhlig", "Gaelic"],
|
||||
"sn": ["chiShona", "Shona"],
|
||||
"si": ["සිංහල", "Sinhalese"],
|
||||
"sk": ["slovenčina", "Slovak"],
|
||||
"sl": ["slovenščina", "Slovene"],
|
||||
"so": ["Soomaaliga", "Somali"],
|
||||
"st": ["Sesotho", "Southern Sotho"],
|
||||
"es": ["español", "Spanish"],
|
||||
"su": ["Basa Sunda", "Sundanese"],
|
||||
"sw": ["Kiswahili", "Swahili"],
|
||||
"ss": ["SiSwati", "Swati"],
|
||||
"sv": ["svenska", "Swedish"],
|
||||
"ta": ["தமிழ்", "Tamil"],
|
||||
"te": ["తెలుగు", "Telugu"],
|
||||
"tg": ["тоҷикӣ", "Tajik"],
|
||||
"th": ["ไทย", "Thai"],
|
||||
"ti": ["ትግርኛ", "Tigrinya"],
|
||||
"bo": ["བོད་ཡིག", "Tibetan"],
|
||||
"tk": ["Түркмен", "Turkmen"],
|
||||
"tl": ["ᜏᜒᜃᜅ᜔ ᜆᜄᜎᜓᜄ᜔", "Tagalog"],
|
||||
"tn": ["Setswana", "Tswana"],
|
||||
"to": ["faka Tonga", "Tonga"],
|
||||
"tr": ["Türkçe", "Turkish"],
|
||||
"ts": ["Xitsonga", "Tsonga"],
|
||||
"tt": ["татар теле", "Tatar"],
|
||||
"ty": ["Reo Tahiti", "Tahitian"],
|
||||
"ug": ["ئۇيغۇرچە", "Uyghur"],
|
||||
"uk": ["українська мова", "Ukrainian"],
|
||||
"ur": ["اردو", "Urdu"],
|
||||
"uz": ["Oʻzbek", "Uzbek"],
|
||||
"ve": ["Tshivenḓa", "Venda"],
|
||||
"vi": ["Việt Nam", "Vietnamese"],
|
||||
"vo": ["Volapük", "Volapük"],
|
||||
"wa": ["walon", "Walloon"],
|
||||
"cy": ["Cymraeg", "Welsh"],
|
||||
"wo": ["Wollof", "Wolof"],
|
||||
"fy": ["Frysk", "Western Frisian"],
|
||||
"xh": ["isiXhosa", "Xhosa"],
|
||||
"yi": ["ייִדיש", "Yiddish"],
|
||||
"yo": ["Yorùbá", "Yoruba"],
|
||||
"za": ["Saɯ cueŋƅ", "Zhuang"],
|
||||
"zu": ["isiZulu", "Zulu"]
|
||||
}
|
||||
139
i18n/pl.json
Normal file
139
i18n/pl.json
Normal file
@@ -0,0 +1,139 @@
|
||||
{
|
||||
"en": "pl",
|
||||
"Paste does not exist, has expired or has been deleted.":
|
||||
"Wklejka nie istnieje, wygasła albo została usunięta.",
|
||||
"ZeroBin requires php 5.2.6 or above to work. Sorry.":
|
||||
"ZeroBin wymaga PHP w wersji 5.2.6 lub nowszej, sorry.",
|
||||
"ZeroBin requires configuration section [%s] to be present in configuration file.":
|
||||
"ZeroBin wymaga obecności sekcji [%s] w pliku konfiguracyjnym.",
|
||||
"Please wait %d seconds between each post.":
|
||||
"Poczekaj %d sekund pomiędzy każdą wklejką.",
|
||||
"Paste is limited to %s of encrypted data.":
|
||||
"Wklejka jest limitowana do %s zaszyfrowanych danych.",
|
||||
"Invalid data.":
|
||||
"Nieprawidłowe dane.",
|
||||
"You are unlucky. Try again.":
|
||||
"Miałeś pecha. Spróbuj ponownie.",
|
||||
"Error saving comment. Sorry.":
|
||||
"Błąd przy zapisywaniu komentarza, sorry.",
|
||||
"Error saving paste. Sorry.":
|
||||
"Błąd przy zapisywaniu wklejki, sorry.",
|
||||
"Invalid paste ID.":
|
||||
"Nieprawidłowe ID wklejki.",
|
||||
"Paste is not of burn-after-reading type.":
|
||||
"Ta wklejka nie ulega autodestrukcji po przeczytaniu.",
|
||||
"Wrong deletion token. Paste was not deleted.":
|
||||
"Nieprawidłowy token usuwania. Wklejka nie została usunięta.",
|
||||
"Paste was properly deleted.":
|
||||
"Wklejka usunięta poprawnie.",
|
||||
"ZeroBin": "ZeroBin",
|
||||
"ZeroBin is a minimalist, opensource online pastebin where the server has zero knowledge of pasted data. Data is encrypted/decrypted <i>in the browser</i> using 256 bits AES. More information on the <a href=\"https://github.com/elrido/ZeroBin/wiki\">project page</a>.":
|
||||
"ZeroBin jest minimalistycznym, otwartoźródłowym serwisem typu pastebin, w którym serwer nie ma jakichkolwiek informacji o tym, co jest wklejane. Dane są szyfrowane i deszyfrowane <i>w przeglądarce</i> z użyciem 256-bitowego klucza AES. Więcej informacji na <a href=\"https://github.com/elrido/ZeroBin/wiki\">stronie projektu</a>.",
|
||||
"Because ignorance is bliss":
|
||||
"Ponieważ ignorancja jest cnotą",
|
||||
"Javascript is required for ZeroBin to work.<br />Sorry for the inconvenience.":
|
||||
"Do działania ZeroBina jest wymagany JavaScript. Przepraszamy za tę niedogodność.",
|
||||
"ZeroBin requires a modern browser to work.":
|
||||
"ZeroBin wymaga do działania nowoczesnej przeglądarki.",
|
||||
"Still using Internet Explorer? Do yourself a favor, switch to a modern browser:":
|
||||
"Cały czas używasz Internet Explorera? Zrób sobie przysługę, przesiądź się na nowoczesną przeglądarkę:",
|
||||
"New":
|
||||
"Nowa",
|
||||
"Send":
|
||||
"Wyślij",
|
||||
"Clone":
|
||||
"Sklonuj",
|
||||
"Raw text":
|
||||
"Czysty tekst",
|
||||
"Expires":
|
||||
"Wygasa",
|
||||
"Burn after reading":
|
||||
"Zniszcz po przeczytaniu",
|
||||
"Open discussion":
|
||||
"Otwarta dyskusja",
|
||||
"Password (recommended)":
|
||||
"Hasło (zalecane)",
|
||||
"Discussion":
|
||||
"Dyskusja",
|
||||
"Toggle navigation":
|
||||
"Przełącz nawigację",
|
||||
"%d seconds": ["%d second", "%d second", "%d second"],
|
||||
"%d minutes": ["%d minut", "%d minut", "%d minut"],
|
||||
"%d hours": ["%d godzina", "%d godzina", "%d godzina"],
|
||||
"%d days": ["%d dzień", "%d dzień", "%d dzień"],
|
||||
"%d weeks": ["%d tydzień", "%d tydzień", "%d tydzień"],
|
||||
"%d months": ["%d miesiąc", "%d miesiąc", "%d miesiąc"],
|
||||
"%d years": ["%d rok", "%d rok", "%d rok"],
|
||||
"Never":
|
||||
"Nigdy",
|
||||
"Note: This is a test service: Data may be deleted anytime. Kittens will die if you abuse this service.":
|
||||
"Notka: To jest usługa testowa. Dane mogą zostać usunięte w dowolnym momencie. Kociątka umrą, jeśli nadużyjesz tej usługi.",
|
||||
"This document will expire in %d seconds.":
|
||||
["This document will expire in %d second.", "This document will expire in %d seconds."],
|
||||
"This document will expire in %d minutes.":
|
||||
["This document will expire in %d minute.", "This document will expire in %d minutes."],
|
||||
"This document will expire in %d hours.":
|
||||
["This document will expire in %d hour.", "This document will expire in %d hours."],
|
||||
"This document will expire in %d days.":
|
||||
["This document will expire in %d day.", "This document will expire in %d days."],
|
||||
"This document will expire in %d months.":
|
||||
["This document will expire in %d month.", "This document will expire in %d months."],
|
||||
"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",
|
||||
"Anonymous avatar (Vizhash of the IP address)":
|
||||
"Anonymous avatar (Vizhash of the 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 (Please move your mouse for more entropy)...":
|
||||
"Sending paste (Please move your mouse for more entropy)...",
|
||||
"Sending paste...":
|
||||
"Sending paste...",
|
||||
"Your paste is <a id=\"pasteurl\" href=\"%s\">%s</a> <span id=\"copyhint\">(Hit [Ctrl]+[c] to copy)</span>":
|
||||
"Your paste is <a id=\"pasteurl\" href=\"%s\">%s</a> <span id=\"copyhint\">(Hit [Ctrl]+[c] to copy)</span>",
|
||||
"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?)",
|
||||
"Format": "Format",
|
||||
"Plain Text": "Plain Text",
|
||||
"Source Code": "Source Code",
|
||||
"Markdown": "Markdown",
|
||||
"Download attachment": "Download attachment",
|
||||
"Cloned file attached.": "Cloned file attached.",
|
||||
"Attach a file": "Attach a file",
|
||||
"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"
|
||||
}
|
||||
BIN
img/favicons/android-chrome-192x192.png
Normal file
BIN
img/favicons/android-chrome-192x192.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.1 KiB |
BIN
img/favicons/apple-touch-icon.png
Normal file
BIN
img/favicons/apple-touch-icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 819 B |
BIN
img/favicons/favicon-16x16.png
Normal file
BIN
img/favicons/favicon-16x16.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 278 B |
BIN
img/favicons/favicon-32x32.png
Normal file
BIN
img/favicons/favicon-32x32.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 373 B |
BIN
img/favicons/favicon-96x96.png
Normal file
BIN
img/favicons/favicon-96x96.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 666 B |
@@ -7,11 +7,12 @@
|
||||
* @link http://sebsauvage.net/wiki/doku.php?id=php:zerobin
|
||||
* @copyright 2012 Sébastien SAUVAGE (sebsauvage.net)
|
||||
* @license http://www.opensource.org/licenses/zlib-license.php The zlib/libpng License
|
||||
* @version 0.20
|
||||
* @version 0.21.1
|
||||
*/
|
||||
|
||||
// change this, if your php files and data is outside of your webservers document root
|
||||
define('PATH', '');
|
||||
|
||||
define('PUBLIC_PATH', dirname(__FILE__));
|
||||
require PATH . 'lib/auto.php';
|
||||
new zerobin;
|
||||
|
||||
16
js/comment.jsonld
Normal file
16
js/comment.jsonld
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"@context": {
|
||||
"so": "http://schema.org/",
|
||||
"status": "so:Integer",
|
||||
"id": "so:name",
|
||||
"parentid": "so:name",
|
||||
"url: {
|
||||
"@id": "so:url",
|
||||
"@type": "@id"
|
||||
},
|
||||
"data": "so:Text",
|
||||
"meta": {
|
||||
"@id": "?jsonld=commentmeta"
|
||||
}
|
||||
}
|
||||
}
|
||||
8
js/commentmeta.jsonld
Normal file
8
js/commentmeta.jsonld
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"@context": {
|
||||
"so": "http://schema.org/",
|
||||
"postdate": "so:Integer",
|
||||
"nickname": "so:Text",
|
||||
"vizhash": "so:Text"
|
||||
}
|
||||
}
|
||||
24
js/paste.jsonld
Normal file
24
js/paste.jsonld
Normal file
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"@context": {
|
||||
"so": "http://schema.org/",
|
||||
"status": {"@id": "so:Integer"},
|
||||
"id": {"@id": "so:name"},
|
||||
"deletetoken": {"@id": "so:Text"},
|
||||
"url": {
|
||||
"@type": "@id",
|
||||
"@id": "so:url"
|
||||
},
|
||||
"data": {"@id": "so:Text"},
|
||||
"attachment": {"@id": "so:Text"},
|
||||
"attachmentname": {"@id": "so:Text"},
|
||||
"meta": {
|
||||
"@id": "?jsonld=pastemeta"
|
||||
},
|
||||
"comments": {
|
||||
"@id": "?jsonld=comment",
|
||||
"@container": "@list"
|
||||
},
|
||||
"comment_count": {"@id": "so:Integer"},
|
||||
"comment_offset": {"@id": "so:Integer"}
|
||||
}
|
||||
}
|
||||
11
js/pastemeta.jsonld
Normal file
11
js/pastemeta.jsonld
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"@context": {
|
||||
"so": "http://schema.org/",
|
||||
"formatter": {"@id": "so:Text"},
|
||||
"postdate": {"@id": "so:Integer"},
|
||||
"opendiscussion": {"@id": "so:True"},
|
||||
"burnafterreading": {"@id": "so:True"},
|
||||
"expire_date": {"@id": "so:Integer"},
|
||||
"remaining_time": {"@id": "so:Integer"}
|
||||
}
|
||||
}
|
||||
1296
js/showdown.js
Normal file
1296
js/showdown.js
Normal file
File diff suppressed because it is too large
Load Diff
1921
js/zerobin.js
1921
js/zerobin.js
File diff suppressed because it is too large
Load Diff
357
lib/RainTPL.php
357
lib/RainTPL.php
@@ -84,7 +84,7 @@ class RainTPL{
|
||||
|
||||
/**
|
||||
* Check template
|
||||
*
|
||||
*
|
||||
* true: checks template update time, if changed it compile them
|
||||
* false: loads the compiled template. Set false if server doesn't have write permission for cache_directory.
|
||||
*
|
||||
@@ -133,7 +133,7 @@ class RainTPL{
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $tpl = array(); //
|
||||
protected $tpl = array(); //
|
||||
|
||||
/**
|
||||
* static cache enabled / disabled
|
||||
@@ -154,7 +154,7 @@ class RainTPL{
|
||||
*
|
||||
* @var array the file
|
||||
*/
|
||||
protected static $config_name_sum = array();
|
||||
protected static $config_name_sum = array();
|
||||
|
||||
// -------------------------
|
||||
|
||||
@@ -207,41 +207,41 @@ class RainTPL{
|
||||
}
|
||||
|
||||
// Cache is off and, return_string is false
|
||||
// Rain just echo the template
|
||||
// Rain just echo the template
|
||||
|
||||
if( !$this->cache && !$return_string ){
|
||||
extract( $this->var );
|
||||
include $this->tpl['compiled_filename'];
|
||||
unset( $this->tpl );
|
||||
}
|
||||
if( !$this->cache && !$return_string ){
|
||||
extract( $this->var );
|
||||
include $this->tpl['compiled_filename'];
|
||||
unset( $this->tpl );
|
||||
}
|
||||
|
||||
|
||||
// cache or return_string are enabled
|
||||
// rain get the output buffer to save the output in the cache or to return it as string
|
||||
// rain get the output buffer to save the output in the cache or to return it as string
|
||||
|
||||
else{
|
||||
else{
|
||||
|
||||
//----------------------
|
||||
// get the output buffer
|
||||
//----------------------
|
||||
ob_start();
|
||||
extract( $this->var );
|
||||
include $this->tpl['compiled_filename'];
|
||||
$raintpl_contents = ob_get_clean();
|
||||
//----------------------
|
||||
//----------------------
|
||||
// get the output buffer
|
||||
//----------------------
|
||||
ob_start();
|
||||
extract( $this->var );
|
||||
include $this->tpl['compiled_filename'];
|
||||
$raintpl_contents = ob_get_clean();
|
||||
//----------------------
|
||||
|
||||
|
||||
// save the output in the cache
|
||||
if( $this->cache )
|
||||
file_put_contents( $this->tpl['cache_filename'], "<?php if(!class_exists('raintpl')){exit;}?>" . $raintpl_contents );
|
||||
// save the output in the cache
|
||||
if( $this->cache )
|
||||
file_put_contents( $this->tpl['cache_filename'], "<?php if(!class_exists('raintpl')){exit;}?>" . $raintpl_contents );
|
||||
|
||||
// free memory
|
||||
unset( $this->tpl );
|
||||
// free memory
|
||||
unset( $this->tpl );
|
||||
|
||||
// return or print the template
|
||||
if( $return_string ) return $raintpl_contents; else echo $raintpl_contents;
|
||||
// return or print the template
|
||||
if( $return_string ) return $raintpl_contents; else echo $raintpl_contents;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -258,15 +258,15 @@ class RainTPL{
|
||||
*/
|
||||
public function cache( $tpl_name, $expire_time = self::CACHE_EXPIRE_TIME, $cache_id = '' ){
|
||||
|
||||
// set the cache_id
|
||||
$this->cache_id = $cache_id;
|
||||
// set the cache_id
|
||||
$this->cache_id = $cache_id;
|
||||
|
||||
if( !$this->check_template( $tpl_name ) && file_exists( $this->tpl['cache_filename'] ) && ( time() - filemtime( $this->tpl['cache_filename'] ) < $expire_time ) )
|
||||
return substr( file_get_contents( $this->tpl['cache_filename'] ), 43 );
|
||||
else{
|
||||
//delete the cache of the selected template
|
||||
if (file_exists($this->tpl['cache_filename']))
|
||||
unlink($this->tpl['cache_filename'] );
|
||||
if (file_exists($this->tpl['cache_filename']))
|
||||
unlink($this->tpl['cache_filename'] );
|
||||
$this->cache = true;
|
||||
}
|
||||
}
|
||||
@@ -287,8 +287,8 @@ class RainTPL{
|
||||
self::configure( $key, $value );
|
||||
else if( property_exists( __CLASS__, $setting ) ){
|
||||
self::$$setting = $value;
|
||||
self::$config_name_sum[ $setting ] = $value; // take trace of all config
|
||||
}
|
||||
self::$config_name_sum[ $setting ] = $value; // take trace of all config
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -305,13 +305,13 @@ class RainTPL{
|
||||
|
||||
if( !isset($this->tpl['checked']) ){
|
||||
|
||||
$tpl_basename = basename( $tpl_name ); // template basename
|
||||
$tpl_basedir = strpos($tpl_name,"/") ? dirname($tpl_name) . '/' : null; // template basedirectory
|
||||
$tpl_dir = PATH . self::$tpl_dir . $tpl_basedir; // template directory
|
||||
$this->tpl['tpl_filename'] = $tpl_dir . $tpl_basename . '.' . self::$tpl_ext; // template filename
|
||||
$temp_compiled_filename = PATH . self::$cache_dir . $tpl_basename . "." . md5( $tpl_dir . serialize(self::$config_name_sum));
|
||||
$this->tpl['compiled_filename'] = $temp_compiled_filename . '.rtpl.php'; // cache filename
|
||||
$this->tpl['cache_filename'] = $temp_compiled_filename . '.s_' . $this->cache_id . '.rtpl.php'; // static cache filename
|
||||
$tpl_basename = basename( $tpl_name ); // template basename
|
||||
$tpl_basedir = strpos($tpl_name,"/") ? dirname($tpl_name) . '/' : null; // template basedirectory
|
||||
$tpl_dir = PATH . self::$tpl_dir . $tpl_basedir; // template directory
|
||||
$this->tpl['tpl_filename'] = $tpl_dir . $tpl_basename . '.' . self::$tpl_ext; // template filename
|
||||
$temp_compiled_filename = PATH . self::$cache_dir . $tpl_basename . "." . md5( $tpl_dir . serialize(self::$config_name_sum));
|
||||
$this->tpl['compiled_filename'] = $temp_compiled_filename . '.rtpl.php'; // cache filename
|
||||
$this->tpl['cache_filename'] = $temp_compiled_filename . '.s_' . $this->cache_id . '.rtpl.php'; // static cache filename
|
||||
|
||||
// if the template doesn't exsist throw an error
|
||||
if( self::$check_template_update && !file_exists( $this->tpl['tpl_filename'] ) ){
|
||||
@@ -329,6 +329,7 @@ class RainTPL{
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* execute stripslaches() on the xml block. Invoqued by preg_replace_callback function below
|
||||
*
|
||||
@@ -337,9 +338,11 @@ class RainTPL{
|
||||
* @return string
|
||||
*/
|
||||
protected function xml_reSubstitution($capture) {
|
||||
return "<?php echo '<?xml ".stripslashes($capture[1])." ?>'; ?>";
|
||||
return "<?php echo '<?xml ".stripslashes($capture[1])." ?>'; ?>";
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Compile and write the compiled template file
|
||||
*
|
||||
@@ -398,20 +401,20 @@ class RainTPL{
|
||||
protected function compileTemplate( $template_code, $tpl_basedir ){
|
||||
|
||||
//tag list
|
||||
$tag_regexp = array( 'loop' => '(\{loop(?: name){0,1}="\${0,1}[^"]*"\})',
|
||||
'loop_close' => '(\{\/loop\})',
|
||||
'if' => '(\{if(?: condition){0,1}="[^"]*"\})',
|
||||
'elseif' => '(\{elseif(?: condition){0,1}="[^"]*"\})',
|
||||
'else' => '(\{else\})',
|
||||
'if_close' => '(\{\/if\})',
|
||||
'function' => '(\{function="[^"]*"\})',
|
||||
'noparse' => '(\{noparse\})',
|
||||
'noparse_close'=> '(\{\/noparse\})',
|
||||
'ignore' => '(\{ignore\}|\{\*)',
|
||||
'ignore_close' => '(\{\/ignore\}|\*\})',
|
||||
'include' => '(\{include="[^"]*"(?: cache="[^"]*")?\})',
|
||||
'template_info'=> '(\{\$template_info\})',
|
||||
'function' => '(\{function="(\w*?)(?:.*?)"\})'
|
||||
$tag_regexp = array( 'loop' => '(\{loop(?: name){0,1}="\${0,1}[^"]*"\})',
|
||||
'loop_close' => '(\{\/loop\})',
|
||||
'if' => '(\{if(?: condition){0,1}="[^"]*"\})',
|
||||
'elseif' => '(\{elseif(?: condition){0,1}="[^"]*"\})',
|
||||
'else' => '(\{else\})',
|
||||
'if_close' => '(\{\/if\})',
|
||||
'function' => '(\{function="[^"]*"\})',
|
||||
'noparse' => '(\{noparse\})',
|
||||
'noparse_close'=> '(\{\/noparse\})',
|
||||
'ignore' => '(\{ignore\}|\{\*)',
|
||||
'ignore_close' => '(\{\/ignore\}|\*\})',
|
||||
'include' => '(\{include="[^"]*"(?: cache="[^"]*")?\})',
|
||||
'template_info'=> '(\{\$template_info\})',
|
||||
'function' => '(\{function="(\w*?)(?:.*?)"\})'
|
||||
);
|
||||
|
||||
$tag_regexp = "/" . join( "|", $tag_regexp ) . "/";
|
||||
@@ -444,35 +447,35 @@ class RainTPL{
|
||||
|
||||
//variables initialization
|
||||
$compiled_code = $open_if = $comment_is_open = $ignore_is_open = null;
|
||||
$loop_level = 0;
|
||||
$loop_level = 0;
|
||||
|
||||
//read all parsed code
|
||||
while( $html = array_shift( $parsed_code ) ){
|
||||
//read all parsed code
|
||||
while( $html = array_shift( $parsed_code ) ){
|
||||
|
||||
//close ignore tag
|
||||
//close ignore tag
|
||||
if( !$comment_is_open && ( strpos( $html, '{/ignore}' ) !== FALSE || strpos( $html, '*}' ) !== FALSE ) )
|
||||
$ignore_is_open = false;
|
||||
$ignore_is_open = false;
|
||||
|
||||
//code between tag ignore id deleted
|
||||
elseif( $ignore_is_open ){
|
||||
//ignore the code
|
||||
}
|
||||
//code between tag ignore id deleted
|
||||
elseif( $ignore_is_open ){
|
||||
//ignore the code
|
||||
}
|
||||
|
||||
//close no parse tag
|
||||
//close no parse tag
|
||||
elseif( strpos( $html, '{/noparse}' ) !== FALSE )
|
||||
$comment_is_open = false;
|
||||
$comment_is_open = false;
|
||||
|
||||
//code between tag noparse is not compiled
|
||||
elseif( $comment_is_open )
|
||||
$compiled_code .= $html;
|
||||
//code between tag noparse is not compiled
|
||||
elseif( $comment_is_open )
|
||||
$compiled_code .= $html;
|
||||
|
||||
//ignore
|
||||
//ignore
|
||||
elseif( strpos( $html, '{ignore}' ) !== FALSE || strpos( $html, '{*' ) !== FALSE )
|
||||
$ignore_is_open = true;
|
||||
$ignore_is_open = true;
|
||||
|
||||
//noparse
|
||||
elseif( strpos( $html, '{noparse}' ) !== FALSE )
|
||||
$comment_is_open = true;
|
||||
//noparse
|
||||
elseif( strpos( $html, '{noparse}' ) !== FALSE )
|
||||
$comment_is_open = true;
|
||||
|
||||
//include tag
|
||||
elseif( preg_match( '/\{include="([^"]*)"(?: cache="([^"]*)"){0,1}\}/', $html, $code ) ){
|
||||
@@ -509,19 +512,19 @@ class RainTPL{
|
||||
|
||||
}
|
||||
|
||||
//loop
|
||||
//loop
|
||||
elseif( preg_match( '/\{loop(?: name){0,1}="\${0,1}([^"]*)"\}/', $html, $code ) ){
|
||||
|
||||
//increase the loop counter
|
||||
$loop_level++;
|
||||
//increase the loop counter
|
||||
$loop_level++;
|
||||
|
||||
//replace the variable in the loop
|
||||
$var = $this->var_replace( '$' . $code[ 1 ], $tag_left_delimiter=null, $tag_right_delimiter=null, $php_left_delimiter=null, $php_right_delimiter=null, $loop_level-1 );
|
||||
|
||||
//loop variables
|
||||
$counter = "\$counter$loop_level"; // count iteration
|
||||
$key = "\$key$loop_level"; // key
|
||||
$value = "\$value$loop_level"; // value
|
||||
$counter = "\$counter$loop_level"; // count iteration
|
||||
$key = "\$key$loop_level"; // key
|
||||
$value = "\$value$loop_level"; // value
|
||||
|
||||
//loop code
|
||||
$compiled_code .= "<?php $counter=-1; if( isset($var) && is_array($var) && sizeof($var) ) foreach( $var as $key => $value ){ $counter++; ?>";
|
||||
@@ -653,6 +656,7 @@ class RainTPL{
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Reduce a path
|
||||
*
|
||||
@@ -672,7 +676,7 @@ class RainTPL{
|
||||
|
||||
/**
|
||||
* replace the path of image src, link href and a href
|
||||
*
|
||||
*
|
||||
* url => template_dir/url
|
||||
* url# => url
|
||||
* http://url => http://url
|
||||
@@ -728,8 +732,6 @@ class RainTPL{
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* replace constants
|
||||
*
|
||||
@@ -812,28 +814,28 @@ class RainTPL{
|
||||
//if there's a function
|
||||
if( $function_var ){
|
||||
|
||||
// check if there's a function or a static method and separate, function by parameters
|
||||
// check if there's a function or a static method and separate, function by parameters
|
||||
$function_var = str_replace("::", "@double_dot@", $function_var );
|
||||
|
||||
// get the position of the first :
|
||||
if( $dot_position = strpos( $function_var, ":" ) ){
|
||||
// get the position of the first :
|
||||
if( $dot_position = strpos( $function_var, ":" ) ){
|
||||
|
||||
// get the function and the parameters
|
||||
$function = substr( $function_var, 0, $dot_position );
|
||||
$params = substr( $function_var, $dot_position+1 );
|
||||
// get the function and the parameters
|
||||
$function = substr( $function_var, 0, $dot_position );
|
||||
$params = substr( $function_var, $dot_position+1 );
|
||||
|
||||
}
|
||||
else{
|
||||
}
|
||||
else{
|
||||
|
||||
//get the function
|
||||
$function = str_replace( "@double_dot@", "::", $function_var );
|
||||
$params = null;
|
||||
//get the function
|
||||
$function = str_replace( "@double_dot@", "::", $function_var );
|
||||
$params = null;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// replace back the @double_dot@ with ::
|
||||
$function = str_replace( "@double_dot@", "::", $function );
|
||||
$params = str_replace( "@double_dot@", "::", $params );
|
||||
// replace back the @double_dot@ with ::
|
||||
$function = str_replace( "@double_dot@", "::", $function );
|
||||
$params = str_replace( "@double_dot@", "::", $params );
|
||||
|
||||
|
||||
}
|
||||
@@ -880,105 +882,105 @@ class RainTPL{
|
||||
//all variables
|
||||
if( preg_match_all( '/' . $tag_left_delimiter . '\$(\w+(?:\.\${0,1}[A-Za-z0-9_]+)*(?:(?:\[\${0,1}[A-Za-z0-9_]+\])|(?:\-\>\${0,1}[A-Za-z0-9_]+))*)(.*?)' . $tag_right_delimiter . '/', $html, $matches ) ){
|
||||
|
||||
for( $parsed=array(), $i=0, $n=count($matches[0]); $i<$n; $i++ )
|
||||
$parsed[$matches[0][$i]] = array('var'=>$matches[1][$i],'extra_var'=>$matches[2][$i]);
|
||||
for( $parsed=array(), $i=0, $n=count($matches[0]); $i<$n; $i++ )
|
||||
$parsed[$matches[0][$i]] = array('var'=>$matches[1][$i],'extra_var'=>$matches[2][$i]);
|
||||
|
||||
foreach( $parsed as $tag => $array ){
|
||||
foreach( $parsed as $tag => $array ){
|
||||
|
||||
//variable name ex: news.title
|
||||
$var = $array['var'];
|
||||
//variable name ex: news.title
|
||||
$var = $array['var'];
|
||||
|
||||
//function and parameters associate to the variable ex: substr:0,100
|
||||
$extra_var = $array['extra_var'];
|
||||
//function and parameters associate to the variable ex: substr:0,100
|
||||
$extra_var = $array['extra_var'];
|
||||
|
||||
// check if there's any function disabled by black_list
|
||||
$this->function_check( $tag );
|
||||
// check if there's any function disabled by black_list
|
||||
$this->function_check( $tag );
|
||||
|
||||
$extra_var = $this->var_replace( $extra_var, null, null, null, null, $loop_level );
|
||||
$extra_var = $this->var_replace( $extra_var, null, null, null, null, $loop_level );
|
||||
|
||||
// check if there's an operator = in the variable tags, if there's this is an initialization so it will not output any value
|
||||
$is_init_variable = preg_match( "/^[a-z_A-Z\.\[\](\-\>)]*=[^=]*$/", $extra_var );
|
||||
// check if there's an operator = in the variable tags, if there's this is an initialization so it will not output any value
|
||||
$is_init_variable = preg_match( "/^[a-z_A-Z\.\[\](\-\>)]*=[^=]*$/", $extra_var );
|
||||
|
||||
//function associate to variable
|
||||
$function_var = ( $extra_var and $extra_var[0] == '|') ? substr( $extra_var, 1 ) : null;
|
||||
//function associate to variable
|
||||
$function_var = ( $extra_var and $extra_var[0] == '|') ? substr( $extra_var, 1 ) : null;
|
||||
|
||||
//variable path split array (ex. $news.title o $news[title]) or object (ex. $news->title)
|
||||
$temp = preg_split( "/\.|\[|\-\>/", $var );
|
||||
//variable path split array (ex. $news.title o $news[title]) or object (ex. $news->title)
|
||||
$temp = preg_split( "/\.|\[|\-\>/", $var );
|
||||
|
||||
//variable name
|
||||
$var_name = $temp[ 0 ];
|
||||
//variable name
|
||||
$var_name = $temp[ 0 ];
|
||||
|
||||
//variable path
|
||||
$variable_path = substr( $var, strlen( $var_name ) );
|
||||
//variable path
|
||||
$variable_path = substr( $var, strlen( $var_name ) );
|
||||
|
||||
//parentesis transform [ e ] in [" e in "]
|
||||
$variable_path = str_replace( '[', '["', $variable_path );
|
||||
$variable_path = str_replace( ']', '"]', $variable_path );
|
||||
//parentesis transform [ e ] in [" e in "]
|
||||
$variable_path = str_replace( '[', '["', $variable_path );
|
||||
$variable_path = str_replace( ']', '"]', $variable_path );
|
||||
|
||||
//transform .$variable in ["$variable"] and .variable in ["variable"]
|
||||
$variable_path = preg_replace('/\.(\${0,1}\w+)/', '["\\1"]', $variable_path );
|
||||
//transform .$variable in ["$variable"] and .variable in ["variable"]
|
||||
$variable_path = preg_replace('/\.(\${0,1}\w+)/', '["\\1"]', $variable_path );
|
||||
|
||||
// if is an assignment also assign the variable to $this->var['value']
|
||||
if( $is_init_variable )
|
||||
$extra_var = "=\$this->var['{$var_name}']{$variable_path}" . $extra_var;
|
||||
// if is an assignment also assign the variable to $this->var['value']
|
||||
if( $is_init_variable )
|
||||
$extra_var = "=\$this->var['{$var_name}']{$variable_path}" . $extra_var;
|
||||
|
||||
|
||||
|
||||
//if there's a function
|
||||
if( $function_var ){
|
||||
//if there's a function
|
||||
if( $function_var ){
|
||||
|
||||
// check if there's a function or a static method and separate, function by parameters
|
||||
$function_var = str_replace("::", "@double_dot@", $function_var );
|
||||
// check if there's a function or a static method and separate, function by parameters
|
||||
$function_var = str_replace("::", "@double_dot@", $function_var );
|
||||
|
||||
|
||||
// get the position of the first :
|
||||
if( $dot_position = strpos( $function_var, ":" ) ){
|
||||
// get the position of the first :
|
||||
if( $dot_position = strpos( $function_var, ":" ) ){
|
||||
|
||||
// get the function and the parameters
|
||||
$function = substr( $function_var, 0, $dot_position );
|
||||
$params = substr( $function_var, $dot_position+1 );
|
||||
// get the function and the parameters
|
||||
$function = substr( $function_var, 0, $dot_position );
|
||||
$params = substr( $function_var, $dot_position+1 );
|
||||
|
||||
}
|
||||
else{
|
||||
}
|
||||
else{
|
||||
|
||||
//get the function
|
||||
$function = str_replace( "@double_dot@", "::", $function_var );
|
||||
$params = null;
|
||||
//get the function
|
||||
$function = str_replace( "@double_dot@", "::", $function_var );
|
||||
$params = null;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// replace back the @double_dot@ with ::
|
||||
$function = str_replace( "@double_dot@", "::", $function );
|
||||
$params = str_replace( "@double_dot@", "::", $params );
|
||||
}
|
||||
else
|
||||
$function = $params = null;
|
||||
// replace back the @double_dot@ with ::
|
||||
$function = str_replace( "@double_dot@", "::", $function );
|
||||
$params = str_replace( "@double_dot@", "::", $params );
|
||||
}
|
||||
else
|
||||
$function = $params = null;
|
||||
|
||||
//if it is inside a loop
|
||||
if( $loop_level ){
|
||||
//verify the variable name
|
||||
if( $var_name == 'key' )
|
||||
$php_var = '$key' . $loop_level;
|
||||
elseif( $var_name == 'value' )
|
||||
$php_var = '$value' . $loop_level . $variable_path;
|
||||
elseif( $var_name == 'counter' )
|
||||
$php_var = '$counter' . $loop_level;
|
||||
else
|
||||
$php_var = '$' . $var_name . $variable_path;
|
||||
}else
|
||||
$php_var = '$' . $var_name . $variable_path;
|
||||
//if it is inside a loop
|
||||
if( $loop_level ){
|
||||
//verify the variable name
|
||||
if( $var_name == 'key' )
|
||||
$php_var = '$key' . $loop_level;
|
||||
elseif( $var_name == 'value' )
|
||||
$php_var = '$value' . $loop_level . $variable_path;
|
||||
elseif( $var_name == 'counter' )
|
||||
$php_var = '$counter' . $loop_level;
|
||||
else
|
||||
$php_var = '$' . $var_name . $variable_path;
|
||||
}else
|
||||
$php_var = '$' . $var_name . $variable_path;
|
||||
|
||||
// compile the variable for php
|
||||
if( isset( $function ) )
|
||||
$php_var = $php_left_delimiter . ( !$is_init_variable && $echo ? 'echo ' : null ) . ( $params ? "( $function( $php_var, $params ) )" : "$function( $php_var )" ) . $php_right_delimiter;
|
||||
else
|
||||
$php_var = $php_left_delimiter . ( !$is_init_variable && $echo ? 'echo ' : null ) . $php_var . $extra_var . $php_right_delimiter;
|
||||
// compile the variable for php
|
||||
if( isset( $function ) )
|
||||
$php_var = $php_left_delimiter . ( !$is_init_variable && $echo ? 'echo ' : null ) . ( $params ? "( $function( $php_var, $params ) )" : "$function( $php_var )" ) . $php_right_delimiter;
|
||||
else
|
||||
$php_var = $php_left_delimiter . ( !$is_init_variable && $echo ? 'echo ' : null ) . $php_var . $extra_var . $php_right_delimiter;
|
||||
|
||||
$html = str_replace( $tag, $php_var, $html );
|
||||
$html = str_replace( $tag, $php_var, $html );
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $html;
|
||||
}
|
||||
@@ -1015,6 +1017,8 @@ class RainTPL{
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Prints debug info about exception or passes it further if debug is disabled.
|
||||
*
|
||||
@@ -1053,6 +1057,7 @@ class RainTPL{
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Basic Rain tpl exception.
|
||||
*/
|
||||
@@ -1085,6 +1090,8 @@ class RainTpl_Exception extends Exception{
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Exception thrown when template file does not exists.
|
||||
*/
|
||||
@@ -1155,4 +1162,18 @@ class RainTpl_SyntaxException extends RainTpl_Exception{
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* shorthand translate function for use in templates
|
||||
*
|
||||
* alias for i18n::translate()
|
||||
*
|
||||
* @access public
|
||||
* @param string $messageId
|
||||
* @param mixed $args one or multiple parameters injected into placeholders
|
||||
* @return string
|
||||
*/
|
||||
function t() {
|
||||
return call_user_func_array(array('i18n', 'translate'), func_get_args());
|
||||
}
|
||||
|
||||
// -- end
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
* @link http://sebsauvage.net/wiki/doku.php?id=php:zerobin
|
||||
* @copyright 2012 Sébastien SAUVAGE (sebsauvage.net)
|
||||
* @license http://www.opensource.org/licenses/zlib-license.php The zlib/libpng License
|
||||
* @version 0.20
|
||||
* @version 0.22
|
||||
*/
|
||||
|
||||
spl_autoload_register('auto::loader');
|
||||
|
||||
235
lib/configuration.php
Normal file
235
lib/configuration.php
Normal file
@@ -0,0 +1,235 @@
|
||||
<?php
|
||||
/**
|
||||
* ZeroBin
|
||||
*
|
||||
* a zero-knowledge paste bin
|
||||
*
|
||||
* @link http://sebsauvage.net/wiki/doku.php?id=php:zerobin
|
||||
* @copyright 2012 Sébastien SAUVAGE (sebsauvage.net)
|
||||
* @license http://www.opensource.org/licenses/zlib-license.php The zlib/libpng License
|
||||
* @version 0.22
|
||||
*/
|
||||
|
||||
/**
|
||||
* configuration
|
||||
*
|
||||
* parses configuration file, ensures default values present
|
||||
*/
|
||||
class configuration
|
||||
{
|
||||
/**
|
||||
* parsed configuration
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $_configuration;
|
||||
|
||||
/**
|
||||
* default configuration
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $_defaults = array(
|
||||
'main' => array(
|
||||
'discussion' => true,
|
||||
'opendiscussion' => false,
|
||||
'password' => true,
|
||||
'fileupload' => false,
|
||||
'burnafterreadingselected' => false,
|
||||
'defaultformatter' => 'plaintext',
|
||||
'syntaxhighlightingtheme' => null,
|
||||
'sizelimit' => 2097152,
|
||||
'template' => 'bootstrap',
|
||||
'notice' => '',
|
||||
'base64version' => '2.1.9',
|
||||
'languageselection' => false,
|
||||
'languagedefault' => '',
|
||||
),
|
||||
'expire' => array(
|
||||
'default' => '1week',
|
||||
'clone' => true,
|
||||
),
|
||||
'expire_options' => array(
|
||||
'5min' => 300,
|
||||
'10min' => 600,
|
||||
'1hour' => 3600,
|
||||
'1day' => 86400,
|
||||
'1week' => 604800,
|
||||
'1month' => 2592000,
|
||||
'1year' => 31536000,
|
||||
'never' => 0,
|
||||
),
|
||||
'formatter_options' => array(
|
||||
'plaintext' => 'Plain Text',
|
||||
'syntaxhighlighting' => 'Source Code',
|
||||
'markdown' => 'Markdown',
|
||||
),
|
||||
'traffic' => array(
|
||||
'limit' => 10,
|
||||
'header' => null,
|
||||
'dir' => 'data',
|
||||
),
|
||||
'model' => array(
|
||||
'class' => 'zerobin_data',
|
||||
),
|
||||
'model_options' => array(
|
||||
'dir' => 'data',
|
||||
),
|
||||
);
|
||||
|
||||
/**
|
||||
* parse configuration file and ensure default configuration values are present
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$config = array();
|
||||
$configFile = PATH . 'cfg' . DIRECTORY_SEPARATOR . 'conf.ini';
|
||||
if (is_readable($configFile))
|
||||
{
|
||||
$config = parse_ini_file($configFile, true);
|
||||
foreach (array('main', 'model') as $section) {
|
||||
if (!array_key_exists($section, $config)) {
|
||||
throw new Exception(i18n::_('ZeroBin requires configuration section [%s] to be present in configuration file.', $section), 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
$opts = '_options';
|
||||
foreach ($this->_defaults as $section => $values)
|
||||
{
|
||||
// fill missing sections with default values
|
||||
if (!array_key_exists($section, $config) || count($config[$section]) == 0)
|
||||
{
|
||||
$this->_configuration[$section] = $values;
|
||||
if (array_key_exists('dir', $this->_configuration[$section]))
|
||||
{
|
||||
$this->_configuration[$section]['dir'] = PATH . $this->_configuration[$section]['dir'];
|
||||
}
|
||||
continue;
|
||||
}
|
||||
// provide different defaults for database model
|
||||
elseif ($section == 'model_options' && $this->_configuration['model']['class'] == 'zerobin_db')
|
||||
{
|
||||
$values = array(
|
||||
'dsn' => 'sqlite:' . PATH . 'data/db.sq3',
|
||||
'tbl' => null,
|
||||
'usr' => null,
|
||||
'pwd' => null,
|
||||
'opt' => array(PDO::ATTR_PERSISTENT => true),
|
||||
);
|
||||
}
|
||||
|
||||
// "*_options" sections don't require all defaults to be set
|
||||
if (
|
||||
$section !== 'model_options' &&
|
||||
($from = strlen($section) - strlen($opts)) >= 0 &&
|
||||
strpos($section, $opts, $from) !== false
|
||||
)
|
||||
{
|
||||
if (is_int(current($values)))
|
||||
{
|
||||
$config[$section] = array_map('intval', $config[$section]);
|
||||
}
|
||||
$this->_configuration[$section] = $config[$section];
|
||||
}
|
||||
// check for missing keys and set defaults if necessary
|
||||
else
|
||||
{
|
||||
foreach ($values as $key => $val)
|
||||
{
|
||||
if ($key == 'dir')
|
||||
{
|
||||
$val = PATH . $val;
|
||||
}
|
||||
$result = $val;
|
||||
if (array_key_exists($key, $config[$section]))
|
||||
{
|
||||
if ($val === null)
|
||||
{
|
||||
$result = $config[$section][$key];
|
||||
}
|
||||
elseif (is_bool($val))
|
||||
{
|
||||
$val = strtolower($config[$section][$key]);
|
||||
if (in_array($val, array('true', 'yes', 'on')))
|
||||
{
|
||||
$result = true;
|
||||
}
|
||||
elseif (in_array($val, array('false', 'no', 'off')))
|
||||
{
|
||||
$result = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
$result = (bool) $config[$section][$key];
|
||||
}
|
||||
}
|
||||
elseif (is_int($val))
|
||||
{
|
||||
$result = (int) $config[$section][$key];
|
||||
}
|
||||
elseif (is_string($val) && !empty($config[$section][$key]))
|
||||
{
|
||||
$result = (string) $config[$section][$key];
|
||||
}
|
||||
}
|
||||
$this->_configuration[$section][$key] = $result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ensure a valid expire default key is set
|
||||
if (!array_key_exists($this->_configuration['expire']['default'], $this->_configuration['expire_options']))
|
||||
{
|
||||
$this->_configuration['expire']['default'] = key($this->_configuration['expire_options']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* get configuration as array
|
||||
*
|
||||
* return array
|
||||
*/
|
||||
public function get()
|
||||
{
|
||||
return $this->_configuration;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* get a key from the configuration, typically the main section or all keys
|
||||
*
|
||||
* @param string $key
|
||||
* @param string $section defaults to main
|
||||
* @throws Exception
|
||||
* return mixed
|
||||
*/
|
||||
public function getKey($key, $section = 'main')
|
||||
{
|
||||
$options = $this->getSection($section);
|
||||
if (!array_key_exists($key, $options))
|
||||
{
|
||||
throw new Exception(i18n::_('Invalid data.') . " $section / $key", 4);
|
||||
}
|
||||
return $this->_configuration[$section][$key];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* get a key from the configuration, typically the main section or all keys
|
||||
*
|
||||
* @param string $key if empty, return all configuration options
|
||||
* @param string $section defaults to main
|
||||
* @throws Exception
|
||||
* return mixed
|
||||
*/
|
||||
public function getSection($section)
|
||||
{
|
||||
if (!array_key_exists($section, $this->_configuration))
|
||||
{
|
||||
throw new Exception(i18n::_('ZeroBin requires configuration section [%s] to be present in configuration file.', $section), 3);
|
||||
}
|
||||
return $this->_configuration[$section];
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,7 @@
|
||||
* @link http://sebsauvage.net/wiki/doku.php?id=php:zerobin
|
||||
* @copyright 2012 Sébastien SAUVAGE (sebsauvage.net)
|
||||
* @license http://www.opensource.org/licenses/zlib-license.php The zlib/libpng License
|
||||
* @version 0.20
|
||||
* @version 0.22
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -33,7 +33,36 @@ class filter
|
||||
}
|
||||
|
||||
/**
|
||||
* format a given number of bytes
|
||||
* format a given time string into a human readable label (localized)
|
||||
*
|
||||
* accepts times in the format "[integer][time unit]"
|
||||
*
|
||||
* @access public
|
||||
* @static
|
||||
* @param string $time
|
||||
* @throws Exception
|
||||
* @return string
|
||||
*/
|
||||
public static function time_humanreadable($time)
|
||||
{
|
||||
if (preg_match('/^(\d+) *(\w+)$/', $time, $matches) !== 1) {
|
||||
throw new Exception("Error parsing time format '$time'", 30);
|
||||
}
|
||||
switch ($matches[2]) {
|
||||
case 'sec':
|
||||
$unit = 'second';
|
||||
break;
|
||||
case 'min':
|
||||
$unit = 'minute';
|
||||
break;
|
||||
default:
|
||||
$unit = rtrim($matches[2], 's');
|
||||
}
|
||||
return i18n::_(array('%d ' . $unit, '%d ' . $unit . 's'), (int) $matches[1]);
|
||||
}
|
||||
|
||||
/**
|
||||
* format a given number of bytes in IEC 80000-13:2008 notation (localized)
|
||||
*
|
||||
* @access public
|
||||
* @static
|
||||
@@ -42,26 +71,13 @@ class filter
|
||||
*/
|
||||
public static function size_humanreadable($size)
|
||||
{
|
||||
$iec = array('B', 'kiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB');
|
||||
$iec = array('B', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB');
|
||||
$i = 0;
|
||||
while ( ( $size / 1024 ) >= 1 ) {
|
||||
$size = $size / 1024;
|
||||
$i++;
|
||||
}
|
||||
return number_format($size, ($i ? 2 : 0), '.', ' ') . ' ' . $iec[$i];
|
||||
}
|
||||
|
||||
/**
|
||||
* validate paste ID
|
||||
*
|
||||
* @access public
|
||||
* @static
|
||||
* @param string $dataid
|
||||
* @return bool
|
||||
*/
|
||||
public static function is_valid_paste_id($dataid)
|
||||
{
|
||||
return (bool) preg_match('#\A[a-f\d]{16}\z#', $dataid);
|
||||
return number_format($size, ($i ? 2 : 0), '.', ' ') . ' ' . i18n::_($iec[$i]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
392
lib/i18n.php
Normal file
392
lib/i18n.php
Normal file
@@ -0,0 +1,392 @@
|
||||
<?php
|
||||
/**
|
||||
* ZeroBin
|
||||
*
|
||||
* a zero-knowledge paste bin
|
||||
*
|
||||
* @link http://sebsauvage.net/wiki/doku.php?id=php:zerobin
|
||||
* @copyright 2012 Sébastien SAUVAGE (sebsauvage.net)
|
||||
* @license http://www.opensource.org/licenses/zlib-license.php The zlib/libpng License
|
||||
* @version 0.22
|
||||
*/
|
||||
|
||||
/**
|
||||
* i18n
|
||||
*
|
||||
* provides internationalization tools like translation, browser language detection, etc.
|
||||
*/
|
||||
class i18n
|
||||
{
|
||||
/**
|
||||
* language
|
||||
*
|
||||
* @access protected
|
||||
* @static
|
||||
* @var string
|
||||
*/
|
||||
protected static $_language = 'en';
|
||||
|
||||
/**
|
||||
* language fallback
|
||||
*
|
||||
* @access protected
|
||||
* @static
|
||||
* @var string
|
||||
*/
|
||||
protected static $_languageFallback = 'en';
|
||||
|
||||
/**
|
||||
* language labels
|
||||
*
|
||||
* @access protected
|
||||
* @static
|
||||
* @var array
|
||||
*/
|
||||
protected static $_languageLabels = array();
|
||||
|
||||
/**
|
||||
* available languages
|
||||
*
|
||||
* @access protected
|
||||
* @static
|
||||
* @var array
|
||||
*/
|
||||
protected static $_availableLanguages = array();
|
||||
|
||||
/**
|
||||
* path to language files
|
||||
*
|
||||
* @access protected
|
||||
* @static
|
||||
* @var string
|
||||
*/
|
||||
protected static $_path = '';
|
||||
|
||||
/**
|
||||
* translation cache
|
||||
*
|
||||
* @access protected
|
||||
* @static
|
||||
* @var array
|
||||
*/
|
||||
protected static $_translations = array();
|
||||
|
||||
/**
|
||||
* translate a string, alias for translate()
|
||||
*
|
||||
* @access public
|
||||
* @static
|
||||
* @param string $messageId
|
||||
* @param mixed $args one or multiple parameters injected into placeholders
|
||||
* @return string
|
||||
*/
|
||||
public static function _($messageId)
|
||||
{
|
||||
return call_user_func_array(array('i18n', 'translate'), func_get_args());
|
||||
}
|
||||
|
||||
/**
|
||||
* translate a string
|
||||
*
|
||||
* @access public
|
||||
* @static
|
||||
* @param string $messageId
|
||||
* @param mixed $args one or multiple parameters injected into placeholders
|
||||
* @return string
|
||||
*/
|
||||
public static function translate($messageId)
|
||||
{
|
||||
if (empty($messageId)) return $messageId;
|
||||
if (count(self::$_translations) === 0) self::loadTranslations();
|
||||
$messages = $messageId;
|
||||
if (is_array($messageId))
|
||||
{
|
||||
$messageId = count($messageId) > 1 ? $messageId[1] : $messageId[0];
|
||||
}
|
||||
if (!array_key_exists($messageId, self::$_translations))
|
||||
{
|
||||
self::$_translations[$messageId] = $messages;
|
||||
}
|
||||
$args = func_get_args();
|
||||
if (is_array(self::$_translations[$messageId]))
|
||||
{
|
||||
$number = (int) $args[1];
|
||||
$key = self::_getPluralForm($number);
|
||||
$max = count(self::$_translations[$messageId]) - 1;
|
||||
if ($key > $max) $key = $max;
|
||||
|
||||
$args[0] = self::$_translations[$messageId][$key];
|
||||
$args[1] = $number;
|
||||
}
|
||||
else
|
||||
{
|
||||
$args[0] = self::$_translations[$messageId];
|
||||
}
|
||||
return call_user_func_array('sprintf', $args);
|
||||
}
|
||||
|
||||
/**
|
||||
* loads translations
|
||||
*
|
||||
* From: http://stackoverflow.com/questions/3770513/detect-browser-language-in-php#3771447
|
||||
*
|
||||
* @access public
|
||||
* @static
|
||||
* @return void
|
||||
*/
|
||||
public static function loadTranslations()
|
||||
{
|
||||
$availableLanguages = self::getAvailableLanguages();
|
||||
|
||||
// check if the lang cookie was set and that language exists
|
||||
if (array_key_exists('lang', $_COOKIE) && in_array($_COOKIE['lang'], $availableLanguages))
|
||||
{
|
||||
$match = $_COOKIE['lang'];
|
||||
}
|
||||
// find a translation file matching the browsers language preferences
|
||||
else
|
||||
{
|
||||
$match = self::_getMatchingLanguage(
|
||||
self::getBrowserLanguages(), $availableLanguages
|
||||
);
|
||||
}
|
||||
|
||||
// load translations
|
||||
self::$_language = $match;
|
||||
self::$_translations = ($match == 'en') ? array() : json_decode(
|
||||
file_get_contents(self::_getPath($match . '.json')),
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* get list of available translations based on files found
|
||||
*
|
||||
* @access public
|
||||
* @static
|
||||
* @return array
|
||||
*/
|
||||
public static function getAvailableLanguages()
|
||||
{
|
||||
if (count(self::$_availableLanguages) == 0)
|
||||
{
|
||||
$i18n = dir(self::_getPath());
|
||||
while (false !== ($file = $i18n->read()))
|
||||
{
|
||||
if (preg_match('/^([a-z]{2}).json$/', $file, $match) === 1)
|
||||
{
|
||||
self::$_availableLanguages[] = $match[1];
|
||||
}
|
||||
}
|
||||
self::$_availableLanguages[] = 'en';
|
||||
}
|
||||
return self::$_availableLanguages;
|
||||
}
|
||||
|
||||
/**
|
||||
* detect the clients supported languages and return them ordered by preference
|
||||
*
|
||||
* From: http://stackoverflow.com/questions/3770513/detect-browser-language-in-php#3771447
|
||||
*
|
||||
* @access public
|
||||
* @static
|
||||
* @return array
|
||||
*/
|
||||
public static function getBrowserLanguages()
|
||||
{
|
||||
$languages = array();
|
||||
if (array_key_exists('HTTP_ACCEPT_LANGUAGE', $_SERVER))
|
||||
{
|
||||
$languageRanges = explode(',', trim($_SERVER['HTTP_ACCEPT_LANGUAGE']));
|
||||
foreach ($languageRanges as $languageRange) {
|
||||
if (preg_match(
|
||||
'/(\*|[a-zA-Z0-9]{1,8}(?:-[a-zA-Z0-9]{1,8})*)(?:\s*;\s*q\s*=\s*(0(?:\.\d{0,3})|1(?:\.0{0,3})))?/',
|
||||
trim($languageRange), $match
|
||||
))
|
||||
{
|
||||
if (!isset($match[2]))
|
||||
{
|
||||
$match[2] = '1.0';
|
||||
}
|
||||
else
|
||||
{
|
||||
$match[2] = (string) floatval($match[2]);
|
||||
}
|
||||
if (!isset($languages[$match[2]]))
|
||||
{
|
||||
$languages[$match[2]] = array();
|
||||
}
|
||||
$languages[$match[2]][] = strtolower($match[1]);
|
||||
}
|
||||
}
|
||||
krsort($languages);
|
||||
}
|
||||
return $languages;
|
||||
}
|
||||
|
||||
/**
|
||||
* get currently loaded language
|
||||
*
|
||||
* @access public
|
||||
* @static
|
||||
* @return string
|
||||
*/
|
||||
public static function getLanguage()
|
||||
{
|
||||
return self::$_language;
|
||||
}
|
||||
|
||||
/**
|
||||
* get list of language labels
|
||||
*
|
||||
* Only for given language codes, otherwise all labels.
|
||||
*
|
||||
* @access public
|
||||
* @static
|
||||
* @param array $languages
|
||||
* @return array
|
||||
*/
|
||||
public static function getLanguageLabels($languages = array())
|
||||
{
|
||||
$file = self::_getPath('languages.json');
|
||||
if (count(self::$_languageLabels) == 0 && is_readable($file))
|
||||
{
|
||||
self::$_languageLabels = json_decode(file_get_contents($file), true);
|
||||
}
|
||||
if (count($languages) == 0) return self::$_languageLabels;
|
||||
return array_intersect_key(self::$_languageLabels, array_flip($languages));
|
||||
}
|
||||
|
||||
/**
|
||||
* set the default language
|
||||
*
|
||||
* @access public
|
||||
* @static
|
||||
* @param string $lang
|
||||
* @return void
|
||||
*/
|
||||
public static function setLanguageFallback($lang)
|
||||
{
|
||||
if (in_array($lang, self::getAvailableLanguages()))
|
||||
self::$_languageFallback = $lang;
|
||||
}
|
||||
|
||||
/**
|
||||
* get language file path
|
||||
*
|
||||
* @access protected
|
||||
* @static
|
||||
* @param string $file
|
||||
* @return string
|
||||
*/
|
||||
protected static function _getPath($file = '')
|
||||
{
|
||||
if (strlen(self::$_path) == 0)
|
||||
{
|
||||
self::$_path = PUBLIC_PATH . DIRECTORY_SEPARATOR . 'i18n';
|
||||
}
|
||||
return self::$_path . (strlen($file) ? DIRECTORY_SEPARATOR . $file : '');
|
||||
}
|
||||
|
||||
/**
|
||||
* determines the plural form to use based on current language and given number
|
||||
*
|
||||
* From: http://localization-guide.readthedocs.org/en/latest/l10n/pluralforms.html
|
||||
*
|
||||
* @access protected
|
||||
* @static
|
||||
* @param int $n
|
||||
* @return int
|
||||
*/
|
||||
protected static function _getPluralForm($n)
|
||||
{
|
||||
switch (self::$_language) {
|
||||
case 'fr':
|
||||
return ($n > 1 ? 1 : 0);
|
||||
case 'pl':
|
||||
return ($n == 1 ? 0 : $n%10 >= 2 && $n %10 <=4 && ($n%100 < 10 || $n%100 >= 20) ? 1 : 2);
|
||||
// en, de
|
||||
default:
|
||||
return ($n != 1 ? 1 : 0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* compares two language preference arrays and returns the preferred match
|
||||
*
|
||||
* From: http://stackoverflow.com/questions/3770513/detect-browser-language-in-php#3771447
|
||||
*
|
||||
* @access protected
|
||||
* @static
|
||||
* @param array $acceptedLanguages
|
||||
* @param array $availableLanguages
|
||||
* @return string
|
||||
*/
|
||||
protected static function _getMatchingLanguage($acceptedLanguages, $availableLanguages) {
|
||||
$matches = array();
|
||||
$any = false;
|
||||
foreach ($acceptedLanguages as $acceptedQuality => $acceptedValues) {
|
||||
$acceptedQuality = floatval($acceptedQuality);
|
||||
if ($acceptedQuality === 0.0) continue;
|
||||
foreach ($availableLanguages as $availableValue)
|
||||
{
|
||||
$availableQuality = 1.0;
|
||||
foreach ($acceptedValues as $acceptedValue)
|
||||
{
|
||||
if ($acceptedValue === '*')
|
||||
{
|
||||
$any = true;
|
||||
}
|
||||
$matchingGrade = self::_matchLanguage($acceptedValue, $availableValue);
|
||||
if ($matchingGrade > 0)
|
||||
{
|
||||
$q = (string) ($acceptedQuality * $availableQuality * $matchingGrade);
|
||||
if (!isset($matches[$q]))
|
||||
{
|
||||
$matches[$q] = array();
|
||||
}
|
||||
if (!in_array($availableValue, $matches[$q]))
|
||||
{
|
||||
$matches[$q][] = $availableValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (count($matches) === 0 && $any)
|
||||
{
|
||||
if (count($availableLanguages) > 0)
|
||||
{
|
||||
$matches['1.0'] = $availableLanguages;
|
||||
}
|
||||
}
|
||||
if (count($matches) === 0)
|
||||
{
|
||||
return self::$_languageFallback;
|
||||
}
|
||||
krsort($matches);
|
||||
$topmatches = current($matches);
|
||||
return current($topmatches);
|
||||
}
|
||||
|
||||
/**
|
||||
* compare two language IDs and return the degree they match
|
||||
*
|
||||
* From: http://stackoverflow.com/questions/3770513/detect-browser-language-in-php#3771447
|
||||
*
|
||||
* @access protected
|
||||
* @static
|
||||
* @param string $a
|
||||
* @param string $b
|
||||
* @return float
|
||||
*/
|
||||
protected static function _matchLanguage($a, $b) {
|
||||
$a = explode('-', $a);
|
||||
$b = explode('-', $b);
|
||||
for ($i=0, $n=min(count($a), count($b)); $i<$n; $i++)
|
||||
{
|
||||
if ($a[$i] !== $b[$i]) break;
|
||||
}
|
||||
return $i === 0 ? 0 : (float) $i / count($a);
|
||||
}
|
||||
}
|
||||
71
lib/model.php
Normal file
71
lib/model.php
Normal file
@@ -0,0 +1,71 @@
|
||||
<?php
|
||||
/**
|
||||
* ZeroBin
|
||||
*
|
||||
* a zero-knowledge paste bin
|
||||
*
|
||||
* @link http://sebsauvage.net/wiki/doku.php?id=php:zerobin
|
||||
* @copyright 2012 Sébastien SAUVAGE (sebsauvage.net)
|
||||
* @license http://www.opensource.org/licenses/zlib-license.php The zlib/libpng License
|
||||
* @version 0.22
|
||||
*/
|
||||
|
||||
/**
|
||||
* model
|
||||
*
|
||||
* Factory of ZeroBin instance models.
|
||||
*/
|
||||
class model
|
||||
{
|
||||
/**
|
||||
* Configuration.
|
||||
*
|
||||
* @var configuration
|
||||
*/
|
||||
private $_conf;
|
||||
|
||||
/**
|
||||
* Data storage.
|
||||
*
|
||||
* @var zerobin_abstract
|
||||
*/
|
||||
private $_store = null;
|
||||
|
||||
/**
|
||||
* Factory constructor.
|
||||
*
|
||||
* @param configuration $conf
|
||||
*/
|
||||
public function __construct(configuration $conf)
|
||||
{
|
||||
$this->_conf = $conf;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a paste, optionally a specific instance.
|
||||
*
|
||||
* @param string $pasteId
|
||||
* @return model_paste
|
||||
*/
|
||||
public function getPaste($pasteId = null)
|
||||
{
|
||||
$paste = new model_paste($this->_conf, $this->_getStore());
|
||||
if ($pasteId !== null) $paste->setId($pasteId);
|
||||
return $paste;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets, and creates if neccessary, a store object
|
||||
*/
|
||||
private function _getStore()
|
||||
{
|
||||
if ($this->_store === null)
|
||||
{
|
||||
$this->_store = forward_static_call(
|
||||
array($this->_conf->getKey('class', 'model'), 'getInstance'),
|
||||
$this->_conf->getSection('model_options')
|
||||
);
|
||||
}
|
||||
return $this->_store;
|
||||
}
|
||||
}
|
||||
157
lib/model/abstract.php
Normal file
157
lib/model/abstract.php
Normal file
@@ -0,0 +1,157 @@
|
||||
<?php
|
||||
/**
|
||||
* ZeroBin
|
||||
*
|
||||
* a zero-knowledge paste bin
|
||||
*
|
||||
* @link http://sebsauvage.net/wiki/doku.php?id=php:zerobin
|
||||
* @copyright 2012 Sébastien SAUVAGE (sebsauvage.net)
|
||||
* @license http://www.opensource.org/licenses/zlib-license.php The zlib/libpng License
|
||||
* @version 0.22
|
||||
*/
|
||||
|
||||
/**
|
||||
* model_abstract
|
||||
*
|
||||
* Abstract model for ZeroBin objects.
|
||||
*/
|
||||
abstract class model_abstract
|
||||
{
|
||||
/**
|
||||
* Instance ID.
|
||||
*
|
||||
* @access protected
|
||||
* @var string
|
||||
*/
|
||||
protected $_id = '';
|
||||
|
||||
/**
|
||||
* Instance data.
|
||||
*
|
||||
* @access protected
|
||||
* @var stdClass
|
||||
*/
|
||||
protected $_data;
|
||||
|
||||
/**
|
||||
* Configuration.
|
||||
*
|
||||
* @access protected
|
||||
* @var configuration
|
||||
*/
|
||||
protected $_conf;
|
||||
|
||||
/**
|
||||
* Data storage.
|
||||
*
|
||||
* @access protected
|
||||
* @var zerobin_abstract
|
||||
*/
|
||||
protected $_store;
|
||||
|
||||
/**
|
||||
* Instance constructor.
|
||||
*
|
||||
* @access public
|
||||
* @param configuration $configuration
|
||||
* @param zerobin_abstract $storage
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(configuration $configuration, zerobin_abstract $storage)
|
||||
{
|
||||
$this->_conf = $configuration;
|
||||
$this->_store = $storage;
|
||||
$this->_data = new stdClass;
|
||||
$this->_data->meta = new stdClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get ID.
|
||||
*
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function getId()
|
||||
{
|
||||
return $this->_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set ID.
|
||||
*
|
||||
* @access public
|
||||
* @param string $id
|
||||
* @throws Exception
|
||||
* @return void
|
||||
*/
|
||||
public function setId($id)
|
||||
{
|
||||
if (!self::isValidId($id)) throw new Exception('Invalid paste ID.', 60);
|
||||
$this->_id = $id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set data and recalculate ID.
|
||||
*
|
||||
* @access public
|
||||
* @param string $data
|
||||
* @throws Exception
|
||||
* @return void
|
||||
*/
|
||||
public function setData($data)
|
||||
{
|
||||
if (!sjcl::isValid($data)) throw new Exception('Invalid data.', 61);
|
||||
$this->_data->data = $data;
|
||||
|
||||
// We just want a small hash to avoid collisions:
|
||||
// Half-MD5 (64 bits) will do the trick
|
||||
$this->setId(substr(hash('md5', $data), 0, 16));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get instance data.
|
||||
*
|
||||
* @access public
|
||||
* @return stdObject
|
||||
*/
|
||||
abstract public function get();
|
||||
|
||||
/**
|
||||
* Store the instance's data.
|
||||
*
|
||||
* @access public
|
||||
* @throws Exception
|
||||
* @return void
|
||||
*/
|
||||
abstract public function store();
|
||||
|
||||
/**
|
||||
* Delete the current instance.
|
||||
*
|
||||
* @access public
|
||||
* @throws Exception
|
||||
* @return void
|
||||
*/
|
||||
abstract public function delete();
|
||||
|
||||
/**
|
||||
* Test if current instance exists in store.
|
||||
*
|
||||
* @access public
|
||||
* @return bool
|
||||
*/
|
||||
abstract public function exists();
|
||||
|
||||
/**
|
||||
* Validate ID.
|
||||
*
|
||||
* @access public
|
||||
* @static
|
||||
* @param string $id
|
||||
* @return bool
|
||||
*/
|
||||
public static function isValidId($id)
|
||||
{
|
||||
return (bool) preg_match('#\A[a-f\d]{16}\z#', (string) $id);
|
||||
}
|
||||
}
|
||||
189
lib/model/comment.php
Normal file
189
lib/model/comment.php
Normal file
@@ -0,0 +1,189 @@
|
||||
<?php
|
||||
/**
|
||||
* ZeroBin
|
||||
*
|
||||
* a zero-knowledge paste bin
|
||||
*
|
||||
* @link http://sebsauvage.net/wiki/doku.php?id=php:zerobin
|
||||
* @copyright 2012 Sébastien SAUVAGE (sebsauvage.net)
|
||||
* @license http://www.opensource.org/licenses/zlib-license.php The zlib/libpng License
|
||||
* @version 0.22
|
||||
*/
|
||||
|
||||
/**
|
||||
* model_comment
|
||||
*
|
||||
* Model of a ZeroBin comment.
|
||||
*/
|
||||
class model_comment extends model_abstract
|
||||
{
|
||||
/**
|
||||
* Instance's parent.
|
||||
*
|
||||
* @access private
|
||||
* @var model_paste
|
||||
*/
|
||||
private $_paste;
|
||||
|
||||
/**
|
||||
* Get comment data.
|
||||
*
|
||||
* @access public
|
||||
* @throws Exception
|
||||
* @return stdObject
|
||||
*/
|
||||
public function get()
|
||||
{
|
||||
// @todo add support to read specific comment
|
||||
$comments = $this->_store->readComments($this->getPaste()->getId());
|
||||
foreach ($comments as $comment) {
|
||||
if (
|
||||
$comment->parentid == $this->getParentId() &&
|
||||
$comment->id == $this->getId()
|
||||
) {
|
||||
$this->_data = $comment;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return $this->_data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Store the comment's data.
|
||||
*
|
||||
* @access public
|
||||
* @throws Exception
|
||||
* @return void
|
||||
*/
|
||||
public function store()
|
||||
{
|
||||
// Make sure paste exists.
|
||||
$pasteid = $this->getPaste()->getId();
|
||||
if (!$this->getPaste()->exists())
|
||||
throw new Exception('Invalid data.', 67);
|
||||
|
||||
// Make sure the discussion is opened in this paste and in configuration.
|
||||
if (!$this->getPaste()->isOpendiscussion() || !$this->_conf->getKey('discussion'))
|
||||
throw new Exception('Invalid data.', 68);
|
||||
|
||||
// Check for improbable collision.
|
||||
if ($this->exists())
|
||||
throw new Exception('You are unlucky. Try again.', 69);
|
||||
|
||||
$this->_data->meta->postdate = time();
|
||||
|
||||
// store comment
|
||||
if (
|
||||
$this->_store->createComment(
|
||||
$pasteid,
|
||||
$this->getParentId(),
|
||||
$this->getId(),
|
||||
json_decode(json_encode($this->_data), true)
|
||||
) === false
|
||||
) throw new Exception('Error saving comment. Sorry.', 70);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete the comment.
|
||||
*
|
||||
* @access public
|
||||
* @throws Exception
|
||||
* @return void
|
||||
*/
|
||||
public function delete()
|
||||
{
|
||||
throw new Exception('To delete a comment, delete its parent paste', 64);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if comment exists in store.
|
||||
*
|
||||
* @access public
|
||||
* @return bool
|
||||
*/
|
||||
public function exists()
|
||||
{
|
||||
return $this->_store->existsComment(
|
||||
$this->getPaste()->getId(),
|
||||
$this->getParentId(),
|
||||
$this->getId()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set paste.
|
||||
*
|
||||
* @access public
|
||||
* @param model_paste $paste
|
||||
* @throws Exception
|
||||
* @return void
|
||||
*/
|
||||
public function setPaste(model_paste $paste)
|
||||
{
|
||||
$this->_paste = $paste;
|
||||
$this->_data->meta->pasteid = $paste->getId();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get paste.
|
||||
*
|
||||
* @access public
|
||||
* @return model_paste
|
||||
*/
|
||||
public function getPaste()
|
||||
{
|
||||
return $this->_paste;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set parent ID.
|
||||
*
|
||||
* @access public
|
||||
* @param string $id
|
||||
* @throws Exception
|
||||
* @return void
|
||||
*/
|
||||
public function setParentId($id)
|
||||
{
|
||||
if (!self::isValidId($id)) throw new Exception('Invalid paste ID.', 65);
|
||||
$this->_data->meta->parentid = $id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get parent ID.
|
||||
*
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function getParentId()
|
||||
{
|
||||
if (!property_exists($this->_data->meta, 'parentid')) $this->_data->meta->parentid = '';
|
||||
return $this->_data->meta->parentid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set nickname.
|
||||
*
|
||||
* @access public
|
||||
* @param string $nickname
|
||||
* @throws Exception
|
||||
* @return void
|
||||
*/
|
||||
public function setNickname($nickname)
|
||||
{
|
||||
if (!sjcl::isValid($nickname)) throw new Exception('Invalid data.', 66);
|
||||
$this->_data->meta->nickname = $nickname;
|
||||
|
||||
// Generation of the anonymous avatar (Vizhash):
|
||||
// If a nickname is provided, we generate a Vizhash.
|
||||
// (We assume that if the user did not enter a nickname, he/she wants
|
||||
// to be anonymous and we will not generate the vizhash.)
|
||||
$vh = new vizhash16x16();
|
||||
$pngdata = $vh->generate(trafficlimiter::getIp());
|
||||
if ($pngdata != '')
|
||||
{
|
||||
$this->_data->meta->vizhash = 'data:image/png;base64,' . base64_encode($pngdata);
|
||||
}
|
||||
// Once the avatar is generated, we do not keep the IP address, nor its hash.
|
||||
}
|
||||
}
|
||||
303
lib/model/paste.php
Normal file
303
lib/model/paste.php
Normal file
@@ -0,0 +1,303 @@
|
||||
<?php
|
||||
/**
|
||||
* ZeroBin
|
||||
*
|
||||
* a zero-knowledge paste bin
|
||||
*
|
||||
* @link http://sebsauvage.net/wiki/doku.php?id=php:zerobin
|
||||
* @copyright 2012 Sébastien SAUVAGE (sebsauvage.net)
|
||||
* @license http://www.opensource.org/licenses/zlib-license.php The zlib/libpng License
|
||||
* @version 0.22
|
||||
*/
|
||||
|
||||
/**
|
||||
* model_paste
|
||||
*
|
||||
* Model of a ZeroBin paste.
|
||||
*/
|
||||
class model_paste extends model_abstract
|
||||
{
|
||||
/**
|
||||
* Get paste data.
|
||||
*
|
||||
* @access public
|
||||
* @throws Exception
|
||||
* @return stdObject
|
||||
*/
|
||||
public function get()
|
||||
{
|
||||
$this->_data = $this->_store->read($this->getId());
|
||||
// See if paste has expired and delete it if neccessary.
|
||||
if (property_exists($this->_data->meta, 'expire_date'))
|
||||
{
|
||||
if ($this->_data->meta->expire_date < time())
|
||||
{
|
||||
$this->delete();
|
||||
throw new Exception(zerobin::GENERIC_ERROR, 63);
|
||||
}
|
||||
// We kindly provide the remaining time before expiration (in seconds)
|
||||
$this->_data->meta->remaining_time = $this->_data->meta->expire_date - time();
|
||||
}
|
||||
|
||||
// set formatter for for the view.
|
||||
if (!property_exists($this->_data->meta, 'formatter'))
|
||||
{
|
||||
// support < 0.21 syntax highlighting
|
||||
if (property_exists($this->_data->meta, 'syntaxcoloring') && $this->_data->meta->syntaxcoloring === true)
|
||||
{
|
||||
$this->_data->meta->formatter = 'syntaxhighlighting';
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->_data->meta->formatter = $this->_conf->getKey('defaultformatter');
|
||||
}
|
||||
}
|
||||
$this->_data->comments = array_values($this->getComments());
|
||||
$this->_data->comment_count = count($this->_data->comments);
|
||||
$this->_data->comment_offset = 0;
|
||||
$this->_data->{'@context'} = 'js/paste.jsonld';
|
||||
return $this->_data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Store the paste's data.
|
||||
*
|
||||
* @access public
|
||||
* @throws Exception
|
||||
* @return void
|
||||
*/
|
||||
public function store()
|
||||
{
|
||||
// Check for improbable collision.
|
||||
if ($this->exists())
|
||||
throw new Exception('You are unlucky. Try again.', 75);
|
||||
|
||||
$this->_data->meta->postdate = time();
|
||||
|
||||
// store paste
|
||||
if (
|
||||
$this->_store->create(
|
||||
$this->getId(),
|
||||
json_decode(json_encode($this->_data), true)
|
||||
) === false
|
||||
) throw new Exception('Error saving paste. Sorry.', 76);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete the paste.
|
||||
*
|
||||
* @access public
|
||||
* @throws Exception
|
||||
* @return void
|
||||
*/
|
||||
public function delete()
|
||||
{
|
||||
$this->_store->delete($this->getId());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if paste exists in store.
|
||||
*
|
||||
* @access public
|
||||
* @return bool
|
||||
*/
|
||||
public function exists()
|
||||
{
|
||||
return $this->_store->exists($this->getId());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a comment, optionally a specific instance.
|
||||
*
|
||||
* @access public
|
||||
* @param string $parentId
|
||||
* @param string $commentId
|
||||
* @throws Exception
|
||||
* @return model_comment
|
||||
*/
|
||||
public function getComment($parentId, $commentId = null)
|
||||
{
|
||||
if (!$this->exists())
|
||||
{
|
||||
throw new Exception('Invalid data.', 62);
|
||||
}
|
||||
$comment = new model_comment($this->_conf, $this->_store);
|
||||
$comment->setPaste($this);
|
||||
$comment->setParentId($parentId);
|
||||
if ($commentId !== null) $comment->setId($commentId);
|
||||
return $comment;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all comments, if any.
|
||||
*
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function getComments()
|
||||
{
|
||||
return $this->_store->readComments($this->getId());
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the "delete" token.
|
||||
*
|
||||
* The token is the hmac of the pastes ID signed with the server salt.
|
||||
* The paste can be deleted by calling:
|
||||
* http://example.com/zerobin/?pasteid=<pasteid>&deletetoken=<deletetoken>
|
||||
*
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function getDeleteToken()
|
||||
{
|
||||
return hash_hmac('sha1', $this->getId(), serversalt::get());
|
||||
}
|
||||
|
||||
/**
|
||||
* Set paste's attachment.
|
||||
*
|
||||
* @access public
|
||||
* @param string $attachment
|
||||
* @throws Exception
|
||||
* @return void
|
||||
*/
|
||||
public function setAttachment($attachment)
|
||||
{
|
||||
if (!$this->_conf->getKey('fileupload') || !sjcl::isValid($attachment))
|
||||
throw new Exception('Invalid attachment.', 71);
|
||||
$this->_data->meta->attachment = $attachment;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set paste's attachment name.
|
||||
*
|
||||
* @access public
|
||||
* @param string $attachmentname
|
||||
* @throws Exception
|
||||
* @return void
|
||||
*/
|
||||
public function setAttachmentName($attachmentname)
|
||||
{
|
||||
if (!$this->_conf->getKey('fileupload') || !sjcl::isValid($attachmentname))
|
||||
throw new Exception('Invalid attachment.', 72);
|
||||
$this->_data->meta->attachmentname = $attachmentname;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set paste expiration.
|
||||
*
|
||||
* @access public
|
||||
* @param string $expiration
|
||||
* @return void
|
||||
*/
|
||||
public function setExpiration($expiration)
|
||||
{
|
||||
$expire_options = $this->_conf->getSection('expire_options');
|
||||
if (array_key_exists($expiration, $expire_options))
|
||||
{
|
||||
$expire = $expire_options[$expiration];
|
||||
}
|
||||
else
|
||||
{
|
||||
// using getKey() to ensure a default value is present
|
||||
$expire = $this->_conf->getKey($this->_conf->getKey('default', 'expire'), 'expire_options');
|
||||
}
|
||||
if ($expire > 0) $this->_data->meta->expire_date = time() + $expire;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set paste's burn-after-reading type.
|
||||
*
|
||||
* @access public
|
||||
* @param string $burnafterreading
|
||||
* @throws Exception
|
||||
* @return void
|
||||
*/
|
||||
public function setBurnafterreading($burnafterreading = '1')
|
||||
{
|
||||
if ($burnafterreading === '0')
|
||||
{
|
||||
$this->_data->meta->burnafterreading = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ($burnafterreading !== '1')
|
||||
throw new Exception('Invalid data.', 73);
|
||||
$this->_data->meta->burnafterreading = true;
|
||||
$this->_data->meta->opendiscussion = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set paste's discussion state.
|
||||
*
|
||||
* @access public
|
||||
* @param string $opendiscussion
|
||||
* @throws Exception
|
||||
* @return void
|
||||
*/
|
||||
public function setOpendiscussion($opendiscussion = '1')
|
||||
{
|
||||
if (
|
||||
!$this->_conf->getKey('discussion') ||
|
||||
$this->isBurnafterreading() ||
|
||||
$opendiscussion === '0'
|
||||
)
|
||||
{
|
||||
$this->_data->meta->opendiscussion = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ($opendiscussion !== '1')
|
||||
throw new Exception('Invalid data.', 74);
|
||||
$this->_data->meta->opendiscussion = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set paste's format.
|
||||
*
|
||||
* @access public
|
||||
* @param string $format
|
||||
* @throws Exception
|
||||
* @return void
|
||||
*/
|
||||
public function setFormatter($format)
|
||||
{
|
||||
if (!array_key_exists($format, $this->_conf->getSection('formatter_options')))
|
||||
{
|
||||
$format = $this->_conf->getKey('defaultformatter');
|
||||
}
|
||||
$this->_data->meta->formatter = $format;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if paste is of burn-after-reading type.
|
||||
*
|
||||
* @access public
|
||||
* @throws Exception
|
||||
* @return boolean
|
||||
*/
|
||||
public function isBurnafterreading()
|
||||
{
|
||||
if (!property_exists($this->_data, 'data')) $this->get();
|
||||
return property_exists($this->_data->meta, 'burnafterreading') &&
|
||||
$this->_data->meta->burnafterreading === true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check if paste has discussions enabled.
|
||||
*
|
||||
* @access public
|
||||
* @throws Exception
|
||||
* @return boolean
|
||||
*/
|
||||
public function isOpendiscussion()
|
||||
{
|
||||
if (!property_exists($this->_data, 'data')) $this->get();
|
||||
return property_exists($this->_data->meta, 'opendiscussion') &&
|
||||
$this->_data->meta->opendiscussion === true;
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,7 @@
|
||||
* @link http://sebsauvage.net/wiki/doku.php?id=php:zerobin
|
||||
* @copyright 2012 Sébastien SAUVAGE (sebsauvage.net)
|
||||
* @license http://www.opensource.org/licenses/zlib-license.php The zlib/libpng License
|
||||
* @version 0.20
|
||||
* @version 0.22
|
||||
*/
|
||||
|
||||
/**
|
||||
|
||||
171
lib/request.php
Normal file
171
lib/request.php
Normal file
@@ -0,0 +1,171 @@
|
||||
<?php
|
||||
/**
|
||||
* ZeroBin
|
||||
*
|
||||
* a zero-knowledge paste bin
|
||||
*
|
||||
* @link http://sebsauvage.net/wiki/doku.php?id=php:zerobin
|
||||
* @copyright 2012 Sébastien SAUVAGE (sebsauvage.net)
|
||||
* @license http://www.opensource.org/licenses/zlib-license.php The zlib/libpng License
|
||||
* @version 0.22
|
||||
*/
|
||||
|
||||
/**
|
||||
* request
|
||||
*
|
||||
* parses request parameters and provides helper functions for routing
|
||||
*/
|
||||
class request
|
||||
{
|
||||
/**
|
||||
* Input stream to use for PUT parameter parsing.
|
||||
*
|
||||
* @access private
|
||||
* @var string
|
||||
*/
|
||||
private static $_inputStream = 'php://input';
|
||||
|
||||
/**
|
||||
* Operation to perform.
|
||||
*
|
||||
* @access private
|
||||
* @var string
|
||||
*/
|
||||
private $_operation = 'view';
|
||||
|
||||
/**
|
||||
* Request parameters.
|
||||
*
|
||||
* @access private
|
||||
* @var array
|
||||
*/
|
||||
private $_params = array();
|
||||
|
||||
/**
|
||||
* If we are in a JSON API context.
|
||||
*
|
||||
* @access private
|
||||
* @var bool
|
||||
*/
|
||||
private $_isJsonApi = false;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
// in case stupid admin has left magic_quotes enabled in php.ini (for PHP < 5.4)
|
||||
if (function_exists('get_magic_quotes_gpc') && get_magic_quotes_gpc())
|
||||
{
|
||||
$_POST = array_map('filter::stripslashes_deep', $_POST);
|
||||
$_GET = array_map('filter::stripslashes_deep', $_GET);
|
||||
$_COOKIE = array_map('filter::stripslashes_deep', $_COOKIE);
|
||||
}
|
||||
|
||||
// decide if we are in JSON API or HTML context
|
||||
if (
|
||||
(array_key_exists('HTTP_X_REQUESTED_WITH', $_SERVER) &&
|
||||
$_SERVER['HTTP_X_REQUESTED_WITH'] == 'JSONHttpRequest') ||
|
||||
(array_key_exists('HTTP_ACCEPT', $_SERVER) &&
|
||||
strpos($_SERVER['HTTP_ACCEPT'], 'application/json') !== false)
|
||||
)
|
||||
{
|
||||
$this->_isJsonApi = true;
|
||||
}
|
||||
|
||||
// parse parameters, depending on request type
|
||||
switch (array_key_exists('REQUEST_METHOD', $_SERVER) ? $_SERVER['REQUEST_METHOD'] : 'GET')
|
||||
{
|
||||
case 'DELETE':
|
||||
case 'PUT':
|
||||
parse_str(file_get_contents(self::$_inputStream), $this->_params);
|
||||
break;
|
||||
case 'POST':
|
||||
$this->_params = $_POST;
|
||||
break;
|
||||
default:
|
||||
$this->_params = $_GET;
|
||||
}
|
||||
if (
|
||||
!array_key_exists('pasteid', $this->_params) &&
|
||||
!array_key_exists('jsonld', $this->_params) &&
|
||||
array_key_exists('QUERY_STRING', $_SERVER) &&
|
||||
!empty($_SERVER['QUERY_STRING'])
|
||||
)
|
||||
{
|
||||
$this->_params['pasteid'] = $_SERVER['QUERY_STRING'];
|
||||
}
|
||||
|
||||
// prepare operation, depending on current parameters
|
||||
if (
|
||||
(array_key_exists('data', $this->_params) && !empty($this->_params['data'])) ||
|
||||
(array_key_exists('attachment', $this->_params) && !empty($this->_params['attachment']))
|
||||
)
|
||||
{
|
||||
$this->_operation = 'create';
|
||||
}
|
||||
elseif (array_key_exists('pasteid', $this->_params) && !empty($this->_params['pasteid']))
|
||||
{
|
||||
if (array_key_exists('deletetoken', $this->_params) && !empty($this->_params['deletetoken']))
|
||||
{
|
||||
$this->_operation = 'delete';
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->_operation = 'read';
|
||||
}
|
||||
}
|
||||
elseif (array_key_exists('jsonld', $this->_params) && !empty($this->_params['jsonld']))
|
||||
{
|
||||
$this->_operation = 'jsonld';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current operation.
|
||||
*
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function getOperation()
|
||||
{
|
||||
return $this->_operation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a request parameter.
|
||||
*
|
||||
* @access public
|
||||
* @param string $param
|
||||
* @param string $default
|
||||
* @return string
|
||||
*/
|
||||
public function getParam($param, $default = '')
|
||||
{
|
||||
return array_key_exists($param, $this->_params) ? $this->_params[$param] : $default;
|
||||
}
|
||||
|
||||
/**
|
||||
* If we are in a JSON API context.
|
||||
*
|
||||
* @access public
|
||||
* @return bool
|
||||
*/
|
||||
public function isJsonApiCall()
|
||||
{
|
||||
return $this->_isJsonApi;
|
||||
}
|
||||
|
||||
/**
|
||||
* Override the default input stream source, used for unit testing.
|
||||
*
|
||||
* @param string $input
|
||||
*/
|
||||
public static function setInputStream($input)
|
||||
{
|
||||
self::$_inputStream = $input;
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,7 @@
|
||||
* @link http://sebsauvage.net/wiki/doku.php?id=php:zerobin
|
||||
* @copyright 2012 Sébastien SAUVAGE (sebsauvage.net)
|
||||
* @license http://www.opensource.org/licenses/zlib-license.php The zlib/libpng License
|
||||
* @version 0.20
|
||||
* @version 0.22
|
||||
*/
|
||||
|
||||
/**
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
* @link http://sebsauvage.net/wiki/doku.php?id=php:zerobin
|
||||
* @copyright 2012 Sébastien SAUVAGE (sebsauvage.net)
|
||||
* @license http://www.opensource.org/licenses/zlib-license.php The zlib/libpng License
|
||||
* @version 0.20
|
||||
* @version 0.22
|
||||
*/
|
||||
|
||||
/**
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
* @link http://sebsauvage.net/wiki/doku.php?id=php:zerobin
|
||||
* @copyright 2012 Sébastien SAUVAGE (sebsauvage.net)
|
||||
* @license http://www.opensource.org/licenses/zlib-license.php The zlib/libpng License
|
||||
* @version 0.20
|
||||
* @version 0.22
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -26,6 +26,15 @@ class trafficlimiter extends persistence
|
||||
*/
|
||||
private static $_limit = 10;
|
||||
|
||||
/**
|
||||
* key to fetch IP address
|
||||
*
|
||||
* @access private
|
||||
* @static
|
||||
* @var string
|
||||
*/
|
||||
private static $_ipKey = 'REMOTE_ADDR';
|
||||
|
||||
/**
|
||||
* set the time limit in seconds
|
||||
*
|
||||
@@ -39,6 +48,40 @@ class trafficlimiter extends persistence
|
||||
self::$_limit = $limit;
|
||||
}
|
||||
|
||||
/**
|
||||
* set configuration options of the traffic limiter
|
||||
*
|
||||
* @access public
|
||||
* @static
|
||||
* @param configuration $conf
|
||||
* @return void
|
||||
*/
|
||||
public static function setConfiguration(configuration $conf)
|
||||
{
|
||||
self::setLimit($conf->getKey('limit', 'traffic'));
|
||||
self::setPath($conf->getKey('dir', 'traffic'));
|
||||
if (($option = $conf->getKey('header', 'traffic')) !== null)
|
||||
{
|
||||
$httpHeader = 'HTTP_' . $option;
|
||||
if (array_key_exists($httpHeader, $_SERVER) && !empty($_SERVER[$httpHeader]))
|
||||
{
|
||||
self::$_ipKey = $httpHeader;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* get the current visitors IP address
|
||||
*
|
||||
* @access public
|
||||
* @static
|
||||
* @return string
|
||||
*/
|
||||
public static function getIp()
|
||||
{
|
||||
return $_SERVER[self::$_ipKey];
|
||||
}
|
||||
|
||||
/**
|
||||
* traffic limiter
|
||||
*
|
||||
@@ -46,14 +89,15 @@ class trafficlimiter extends persistence
|
||||
*
|
||||
* @access public
|
||||
* @static
|
||||
* @param string $ip
|
||||
* @throws Exception
|
||||
* @return bool
|
||||
*/
|
||||
public static function canPass($ip)
|
||||
public static function canPass()
|
||||
{
|
||||
// disable limits if set to less then 1
|
||||
if (self::$_limit < 1) return true;
|
||||
$ip = self::getIp();
|
||||
|
||||
// disable limits if set to less then 1
|
||||
if (self::$_limit < 1) return true;
|
||||
|
||||
$file = 'traffic_limiter.php';
|
||||
if (!self::_exists($file))
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
* @link http://sebsauvage.net/wiki/doku.php?id=php:vizhash_gd
|
||||
* @copyright 2012 Sébastien SAUVAGE (sebsauvage.net)
|
||||
* @license http://www.opensource.org/licenses/zlib-license.php The zlib/libpng License
|
||||
* @version 0.0.4 beta ZeroBin 0.20
|
||||
* @version 0.0.4 beta ZeroBin 0.22
|
||||
*/
|
||||
|
||||
/**
|
||||
|
||||
639
lib/zerobin.php
639
lib/zerobin.php
@@ -7,7 +7,7 @@
|
||||
* @link http://sebsauvage.net/wiki/doku.php?id=php:zerobin
|
||||
* @copyright 2012 Sébastien SAUVAGE (sebsauvage.net)
|
||||
* @license http://www.opensource.org/licenses/zlib-license.php The zlib/libpng License
|
||||
* @version 0.20
|
||||
* @version 0.22
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -22,7 +22,7 @@ class zerobin
|
||||
*
|
||||
* @const string
|
||||
*/
|
||||
const VERSION = '0.20';
|
||||
const VERSION = '0.22';
|
||||
|
||||
/**
|
||||
* show the same error message if the paste expired or does not exist
|
||||
@@ -32,14 +32,12 @@ class zerobin
|
||||
const GENERIC_ERROR = 'Paste does not exist, has expired or has been deleted.';
|
||||
|
||||
/**
|
||||
* configuration array
|
||||
* configuration
|
||||
*
|
||||
* @access private
|
||||
* @var array
|
||||
* @var configuration
|
||||
*/
|
||||
private $_conf = array(
|
||||
'model' => 'zerobin_data',
|
||||
);
|
||||
private $_conf;
|
||||
|
||||
/**
|
||||
* data
|
||||
@@ -49,6 +47,22 @@ class zerobin
|
||||
*/
|
||||
private $_data = '';
|
||||
|
||||
/**
|
||||
* does the paste expire
|
||||
*
|
||||
* @access private
|
||||
* @var bool
|
||||
*/
|
||||
private $_doesExpire = false;
|
||||
|
||||
/**
|
||||
* formatter
|
||||
*
|
||||
* @access private
|
||||
* @var string
|
||||
*/
|
||||
private $_formatter = 'plaintext';
|
||||
|
||||
/**
|
||||
* error message
|
||||
*
|
||||
@@ -74,13 +88,29 @@ class zerobin
|
||||
private $_json = '';
|
||||
|
||||
/**
|
||||
* data storage model
|
||||
* Factory of instance models
|
||||
*
|
||||
* @access private
|
||||
* @var zerobin_abstract
|
||||
* @var model
|
||||
*/
|
||||
private $_model;
|
||||
|
||||
/**
|
||||
* request
|
||||
*
|
||||
* @access private
|
||||
* @var request
|
||||
*/
|
||||
private $_request;
|
||||
|
||||
/**
|
||||
* URL base
|
||||
*
|
||||
* @access private
|
||||
* @var string
|
||||
*/
|
||||
private $_urlbase;
|
||||
|
||||
/**
|
||||
* constructor
|
||||
*
|
||||
@@ -93,40 +123,38 @@ class zerobin
|
||||
{
|
||||
if (version_compare(PHP_VERSION, '5.2.6') < 0)
|
||||
{
|
||||
throw new Exception('ZeroBin requires php 5.2.6 or above to work. Sorry.', 1);
|
||||
}
|
||||
|
||||
// in case stupid admin has left magic_quotes enabled in php.ini
|
||||
if (get_magic_quotes_gpc())
|
||||
{
|
||||
$_POST = array_map('filter::stripslashes_deep', $_POST);
|
||||
$_GET = array_map('filter::stripslashes_deep', $_GET);
|
||||
$_COOKIE = array_map('filter::stripslashes_deep', $_COOKIE);
|
||||
throw new Exception(i18n::_('ZeroBin requires php 5.2.6 or above to work. Sorry.'), 1);
|
||||
}
|
||||
|
||||
// load config from ini file
|
||||
$this->_init();
|
||||
|
||||
// create new paste or comment
|
||||
if (!empty($_POST['data']))
|
||||
switch ($this->_request->getOperation())
|
||||
{
|
||||
$this->_create($_POST['data']);
|
||||
}
|
||||
// delete an existing paste
|
||||
elseif (!empty($_GET['deletetoken']) && !empty($_GET['pasteid']))
|
||||
{
|
||||
$this->_delete($_GET['pasteid'], $_GET['deletetoken']);
|
||||
}
|
||||
// display an existing paste
|
||||
elseif (!empty($_SERVER['QUERY_STRING']))
|
||||
{
|
||||
$this->_read($_SERVER['QUERY_STRING']);
|
||||
case 'create':
|
||||
$this->_create();
|
||||
break;
|
||||
case 'delete':
|
||||
$this->_delete(
|
||||
$this->_request->getParam('pasteid'),
|
||||
$this->_request->getParam('deletetoken')
|
||||
);
|
||||
break;
|
||||
case 'read':
|
||||
$this->_read($this->_request->getParam('pasteid'));
|
||||
break;
|
||||
case 'jsonld':
|
||||
$this->_jsonld($this->_request->getParam('jsonld'));
|
||||
return;
|
||||
}
|
||||
|
||||
// output JSON or HTML
|
||||
if (strlen($this->_json))
|
||||
if ($this->_request->isJsonApiCall())
|
||||
{
|
||||
header('Content-type: application/json');
|
||||
header('Access-Control-Allow-Origin: *');
|
||||
header('Access-Control-Allow-Methods: GET, POST, PUT, DELETE');
|
||||
header('Access-Control-Allow-Headers: X-Requested-With, Content-Type');
|
||||
echo $this->_json;
|
||||
}
|
||||
else
|
||||
@@ -153,257 +181,127 @@ class zerobin
|
||||
);
|
||||
}
|
||||
|
||||
$this->_conf = parse_ini_file(PATH . 'cfg' . DIRECTORY_SEPARATOR . 'conf.ini', true);
|
||||
foreach (array('main', 'model') as $section) {
|
||||
if (!array_key_exists($section, $this->_conf)) {
|
||||
throw new Exception("ZeroBin requires configuration section [$section] to be present in configuration file.", 2);
|
||||
}
|
||||
}
|
||||
$this->_model = $this->_conf['model']['class'];
|
||||
}
|
||||
$this->_conf = new configuration;
|
||||
$this->_model = new model($this->_conf);
|
||||
$this->_request = new request;
|
||||
$this->_urlbase = array_key_exists('REQUEST_URI', $_SERVER) ? $_SERVER['REQUEST_URI'] : '/';
|
||||
|
||||
/**
|
||||
* get the model, create one if needed
|
||||
*
|
||||
* @access private
|
||||
* @return zerobin_abstract
|
||||
*/
|
||||
private function _model()
|
||||
{
|
||||
// if needed, initialize the model
|
||||
if(is_string($this->_model)) {
|
||||
$this->_model = forward_static_call(
|
||||
array($this->_model, 'getInstance'),
|
||||
$this->_conf['model_options']
|
||||
);
|
||||
// set default language
|
||||
$lang = $this->_conf->getKey('languagedefault');
|
||||
i18n::setLanguageFallback($lang);
|
||||
// force default language, if language selection is disabled and a default is set
|
||||
if (!$this->_conf->getKey('languageselection') && strlen($lang) == 2)
|
||||
{
|
||||
$_COOKIE['lang'] = $lang;
|
||||
setcookie('lang', $lang);
|
||||
}
|
||||
return $this->_model;
|
||||
}
|
||||
|
||||
/**
|
||||
* Store new paste or comment
|
||||
*
|
||||
* POST contains:
|
||||
* data (mandatory) = json encoded SJCL encrypted text (containing keys: iv,salt,ct)
|
||||
* POST contains one or both:
|
||||
* data = json encoded SJCL encrypted text (containing keys: iv,v,iter,ks,ts,mode,adata,cipher,salt,ct)
|
||||
* attachment = json encoded SJCL encrypted text (containing keys: iv,v,iter,ks,ts,mode,adata,cipher,salt,ct)
|
||||
*
|
||||
* All optional data will go to meta information:
|
||||
* expire (optional) = expiration delay (never,5min,10min,1hour,1day,1week,1month,1year,burn) (default:never)
|
||||
* formatter (optional) = format to display the paste as (plaintext,syntaxhighlighting,markdown) (default:syntaxhighlighting)
|
||||
* burnafterreading (optional) = if this paste may only viewed once ? (0/1) (default:0)
|
||||
* opendiscusssion (optional) = is the discussion allowed on this paste ? (0/1) (default:0)
|
||||
* nickname (optional) = in discussion, encoded SJCL encrypted text nickname of author of comment (containing keys: iv,salt,ct)
|
||||
* attachmentname = json encoded SJCL encrypted text (containing keys: iv,v,iter,ks,ts,mode,adata,cipher,salt,ct)
|
||||
* nickname (optional) = in discussion, encoded SJCL encrypted text nickname of author of comment (containing keys: iv,v,iter,ks,ts,mode,adata,cipher,salt,ct)
|
||||
* parentid (optional) = in discussion, which comment this comment replies to.
|
||||
* pasteid (optional) = in discussion, which paste this comment belongs to.
|
||||
*
|
||||
* @access private
|
||||
* @param string $data
|
||||
* @return string
|
||||
*/
|
||||
private function _create($data)
|
||||
private function _create()
|
||||
{
|
||||
$error = false;
|
||||
|
||||
// Make sure last paste from the IP address was more than X seconds ago.
|
||||
trafficlimiter::setLimit($this->_conf['traffic']['limit']);
|
||||
trafficlimiter::setPath($this->_conf['traffic']['dir']);
|
||||
if (!trafficlimiter::canPass($_SERVER['REMOTE_ADDR']))
|
||||
// Ensure last paste from visitors IP address was more than configured amount of seconds ago.
|
||||
trafficlimiter::setConfiguration($this->_conf);
|
||||
if (!trafficlimiter::canPass()) return $this->_return_message(
|
||||
1, i18n::_(
|
||||
'Please wait %d seconds between each post.',
|
||||
$this->_conf->getKey('limit', 'traffic')
|
||||
)
|
||||
);
|
||||
|
||||
$data = $this->_request->getParam('data');
|
||||
$attachment = $this->_request->getParam('attachment');
|
||||
$attachmentname = $this->_request->getParam('attachmentname');
|
||||
|
||||
// Ensure content is not too big.
|
||||
$sizelimit = $this->_conf->getKey('sizelimit');
|
||||
if (
|
||||
strlen($data) + strlen($attachment) + strlen($attachmentname) > $sizelimit
|
||||
) return $this->_return_message(
|
||||
1,
|
||||
i18n::_(
|
||||
'Paste is limited to %s of encrypted data.',
|
||||
filter::size_humanreadable($sizelimit)
|
||||
)
|
||||
);
|
||||
|
||||
// The user posts a comment.
|
||||
$pasteid = $this->_request->getParam('pasteid');
|
||||
$parentid = $this->_request->getParam('parentid');
|
||||
if (!empty($pasteid) && !empty($parentid))
|
||||
{
|
||||
$this->_return_message(
|
||||
1,
|
||||
'Please wait ' .
|
||||
$this->_conf['traffic']['limit'] .
|
||||
' seconds between each post.'
|
||||
);
|
||||
return;
|
||||
}
|
||||
$paste = $this->_model->getPaste($pasteid);
|
||||
if ($paste->exists()) {
|
||||
try {
|
||||
$comment = $paste->getComment($parentid);
|
||||
|
||||
// Make sure content is not too big.
|
||||
$sizelimit = (int) $this->_getMainConfig('sizelimit', 2097152);
|
||||
if (strlen($data) > $sizelimit)
|
||||
{
|
||||
$this->_return_message(
|
||||
1,
|
||||
'Paste is limited to ' .
|
||||
filter::size_humanreadable($sizelimit) .
|
||||
' of encrypted data.'
|
||||
);
|
||||
return;
|
||||
}
|
||||
$nickname = $this->_request->getParam('nickname');
|
||||
if (!empty($nickname)) $comment->setNickname($nickname);
|
||||
|
||||
// Make sure format is correct.
|
||||
if (!sjcl::isValid($data)) return $this->_return_message(1, 'Invalid data.');
|
||||
|
||||
// Read additional meta-information.
|
||||
$meta=array();
|
||||
|
||||
// Read expiration date
|
||||
if (!empty($_POST['expire']))
|
||||
{
|
||||
$selected_expire = (string) $_POST['expire'];
|
||||
if (array_key_exists($selected_expire, $this->_conf['expire_options'])) {
|
||||
$expire = $this->_conf['expire_options'][$selected_expire];
|
||||
} else {
|
||||
$expire = $this->_conf['expire_options'][$this->_conf['expire']['default']];
|
||||
}
|
||||
if ($expire > 0) $meta['expire_date'] = time() + $expire;
|
||||
}
|
||||
|
||||
// Destroy the paste when it is read.
|
||||
if (!empty($_POST['burnafterreading']))
|
||||
{
|
||||
$burnafterreading = $_POST['burnafterreading'];
|
||||
if ($burnafterreading !== '0')
|
||||
{
|
||||
if ($burnafterreading !== '1') $error = true;
|
||||
$meta['burnafterreading'] = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Read open discussion flag.
|
||||
if ($this->_conf['main']['discussion'] && !empty($_POST['opendiscussion']))
|
||||
{
|
||||
$opendiscussion = $_POST['opendiscussion'];
|
||||
if ($opendiscussion !== '0')
|
||||
{
|
||||
if ($opendiscussion !== '1') $error = true;
|
||||
$meta['opendiscussion'] = true;
|
||||
}
|
||||
}
|
||||
|
||||
// You can't have an open discussion on a "Burn after reading" paste:
|
||||
if (isset($meta['burnafterreading'])) unset($meta['opendiscussion']);
|
||||
|
||||
// Optional nickname for comments
|
||||
if (!empty($_POST['nickname']))
|
||||
{
|
||||
// Generation of the anonymous avatar (Vizhash):
|
||||
// If a nickname is provided, we generate a Vizhash.
|
||||
// (We assume that if the user did not enter a nickname, he/she wants
|
||||
// to be anonymous and we will not generate the vizhash.)
|
||||
$nick = $_POST['nickname'];
|
||||
if (!sjcl::isValid($nick))
|
||||
{
|
||||
$error = true;
|
||||
$comment->setData($data);
|
||||
$comment->store();
|
||||
} catch(Exception $e) {
|
||||
return $this->_return_message(1, $e->getMessage());
|
||||
}
|
||||
$this->_return_message(0, $comment->getId());
|
||||
}
|
||||
else
|
||||
{
|
||||
$meta['nickname'] = $nick;
|
||||
$vz = new vizhash16x16();
|
||||
$pngdata = $vz->generate($_SERVER['REMOTE_ADDR']);
|
||||
if ($pngdata != '')
|
||||
{
|
||||
$meta['vizhash'] = 'data:image/png;base64,' . base64_encode($pngdata);
|
||||
}
|
||||
// Once the avatar is generated, we do not keep the IP address, nor its hash.
|
||||
}
|
||||
}
|
||||
|
||||
if ($error)
|
||||
{
|
||||
$this->_return_message(1, 'Invalid data.');
|
||||
return;
|
||||
}
|
||||
|
||||
// Add post date to meta.
|
||||
$meta['postdate'] = time();
|
||||
|
||||
// We just want a small hash to avoid collisions:
|
||||
// Half-MD5 (64 bits) will do the trick
|
||||
$dataid = substr(hash('md5', $data), 0, 16);
|
||||
|
||||
$storage = array('data' => $data);
|
||||
|
||||
// Add meta-information only if necessary.
|
||||
if (count($meta)) $storage['meta'] = $meta;
|
||||
|
||||
// The user posts a comment.
|
||||
if (
|
||||
!empty($_POST['parentid']) &&
|
||||
!empty($_POST['pasteid'])
|
||||
)
|
||||
{
|
||||
$pasteid = (string) $_POST['pasteid'];
|
||||
$parentid = (string) $_POST['parentid'];
|
||||
if (
|
||||
!filter::is_valid_paste_id($pasteid) ||
|
||||
!filter::is_valid_paste_id($parentid)
|
||||
)
|
||||
{
|
||||
$this->_return_message(1, 'Invalid data.');
|
||||
return;
|
||||
}
|
||||
|
||||
// Comments do not expire (it's the paste that expires)
|
||||
unset($storage['expire_date']);
|
||||
unset($storage['opendiscussion']);
|
||||
|
||||
// Make sure paste exists.
|
||||
if (
|
||||
!$this->_model()->exists($pasteid)
|
||||
)
|
||||
{
|
||||
$this->_return_message(1, 'Invalid data.');
|
||||
return;
|
||||
}
|
||||
|
||||
// Make sure the discussion is opened in this paste.
|
||||
$paste = $this->_model()->read($pasteid);
|
||||
if (
|
||||
!$paste->meta->opendiscussion
|
||||
)
|
||||
{
|
||||
$this->_return_message(1, 'Invalid data.');
|
||||
return;
|
||||
}
|
||||
|
||||
// Check for improbable collision.
|
||||
if (
|
||||
$this->_model()->existsComment($pasteid, $parentid, $dataid)
|
||||
)
|
||||
{
|
||||
$this->_return_message(1, 'You are unlucky. Try again.');
|
||||
return;
|
||||
}
|
||||
|
||||
// New comment
|
||||
if (
|
||||
$this->_model()->createComment($pasteid, $parentid, $dataid, $storage) === false
|
||||
)
|
||||
{
|
||||
$this->_return_message(1, 'Error saving comment. Sorry.');
|
||||
return;
|
||||
}
|
||||
|
||||
// 0 = no error
|
||||
$this->_return_message(0, $dataid);
|
||||
return;
|
||||
}
|
||||
// The user posts a standard paste.
|
||||
else
|
||||
{
|
||||
// Check for improbable collision.
|
||||
if (
|
||||
$this->_model()->exists($dataid)
|
||||
)
|
||||
{
|
||||
$this->_return_message(1, 'You are unlucky. Try again.');
|
||||
return;
|
||||
$paste = $this->_model->getPaste();
|
||||
try {
|
||||
$paste->setData($data);
|
||||
|
||||
if (!empty($attachment))
|
||||
{
|
||||
$paste->setAttachment($attachment);
|
||||
if (!empty($attachmentname))
|
||||
$paste->setAttachmentName($attachmentname);
|
||||
}
|
||||
|
||||
$expire = $this->_request->getParam('expire');
|
||||
if (!empty($expire)) $paste->setExpiration($expire);
|
||||
|
||||
$burnafterreading = $this->_request->getParam('burnafterreading');
|
||||
if (!empty($burnafterreading)) $paste->setBurnafterreading($burnafterreading);
|
||||
|
||||
$opendiscussion = $this->_request->getParam('opendiscussion');
|
||||
if (!empty($opendiscussion)) $paste->setOpendiscussion($opendiscussion);
|
||||
|
||||
$formatter = $this->_request->getParam('formatter');
|
||||
if (!empty($formatter)) $paste->setFormatter($formatter);
|
||||
|
||||
$paste->store();
|
||||
} catch (Exception $e) {
|
||||
return $this->_return_message(1, $e->getMessage());
|
||||
}
|
||||
|
||||
// New paste
|
||||
if (
|
||||
$this->_model()->create($dataid, $storage) === false
|
||||
) {
|
||||
$this->_return_message(1, 'Error saving paste. Sorry.');
|
||||
return;
|
||||
}
|
||||
|
||||
// Generate the "delete" token.
|
||||
// The token is the hmac of the pasteid signed with the server salt.
|
||||
// The paste can be delete by calling http://example.com/zerobin/?pasteid=<pasteid>&deletetoken=<deletetoken>
|
||||
$deletetoken = hash_hmac('sha1', $dataid, serversalt::get());
|
||||
|
||||
// 0 = no error
|
||||
$this->_return_message(0, $dataid, array('deletetoken' => $deletetoken));
|
||||
return;
|
||||
$this->_return_message(0, $paste->getId(), array('deletetoken' => $paste->getDeleteToken()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -417,63 +315,48 @@ class zerobin
|
||||
*/
|
||||
private function _delete($dataid, $deletetoken)
|
||||
{
|
||||
// Is this a valid paste identifier?
|
||||
if (!filter::is_valid_paste_id($dataid))
|
||||
{
|
||||
$this->_error = 'Invalid paste ID.';
|
||||
return;
|
||||
}
|
||||
|
||||
// Check that paste exists.
|
||||
if (!$this->_model()->exists($dataid))
|
||||
{
|
||||
$this->_error = self::GENERIC_ERROR;
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the paste itself.
|
||||
$paste = $this->_model()->read($dataid);
|
||||
|
||||
// See if paste has expired.
|
||||
if (
|
||||
isset($paste->meta->expire_date) &&
|
||||
$paste->meta->expire_date < time()
|
||||
)
|
||||
{
|
||||
// Delete the paste
|
||||
$this->_model()->delete($dataid);
|
||||
$this->_error = self::GENERIC_ERROR;
|
||||
return;
|
||||
}
|
||||
|
||||
if ($deletetoken == 'burnafterreading') {
|
||||
if (
|
||||
isset($paste->meta->burnafterreading) &&
|
||||
$paste->meta->burnafterreading
|
||||
)
|
||||
try {
|
||||
$paste = $this->_model->getPaste($dataid);
|
||||
if ($paste->exists())
|
||||
{
|
||||
// Delete the paste
|
||||
$this->_model()->delete($dataid);
|
||||
$this->_return_message(0, $dataid);
|
||||
// accessing this property ensures that the paste would be
|
||||
// deleted if it has already expired
|
||||
$burnafterreading = $paste->isBurnafterreading();
|
||||
if ($deletetoken == 'burnafterreading')
|
||||
{
|
||||
if ($burnafterreading)
|
||||
{
|
||||
$paste->delete();
|
||||
$this->_return_message(0, $dataid);
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->_return_message(1, 'Paste is not of burn-after-reading type.');
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Make sure the token is valid.
|
||||
serversalt::setPath($this->_conf->getKey('dir', 'traffic'));
|
||||
if (filter::slow_equals($deletetoken, $paste->getDeleteToken()))
|
||||
{
|
||||
// Paste exists and deletion token is valid: Delete the paste.
|
||||
$paste->delete();
|
||||
$this->_status = 'Paste was properly deleted.';
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->_error = 'Wrong deletion token. Paste was not deleted.';
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->_return_message(1, 'Paste is not of burn-after-reading type.');
|
||||
$this->_error = self::GENERIC_ERROR;
|
||||
}
|
||||
return;
|
||||
} catch (Exception $e) {
|
||||
$this->_error = $e->getMessage();
|
||||
}
|
||||
|
||||
// Make sure token is valid.
|
||||
serversalt::setPath($this->_conf['traffic']['dir']);
|
||||
if (!filter::slow_equals($deletetoken, hash_hmac('sha1', $dataid, serversalt::get())))
|
||||
{
|
||||
$this->_error = 'Wrong deletion token. Paste was not deleted.';
|
||||
return;
|
||||
}
|
||||
|
||||
// Paste exists and deletion token is valid: Delete the paste.
|
||||
$this->_model()->delete($dataid);
|
||||
$this->_status = 'Paste was properly deleted.';
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -485,65 +368,23 @@ class zerobin
|
||||
*/
|
||||
private function _read($dataid)
|
||||
{
|
||||
$isJson = false;
|
||||
if (($pos = strpos($dataid, '&json')) !== false) {
|
||||
$isJson = true;
|
||||
$dataid = substr($dataid, 0, $pos);
|
||||
}
|
||||
|
||||
// Is this a valid paste identifier?
|
||||
if (!filter::is_valid_paste_id($dataid))
|
||||
{
|
||||
$this->_error = 'Invalid paste ID.';
|
||||
return;
|
||||
}
|
||||
|
||||
// Check that paste exists.
|
||||
if ($this->_model()->exists($dataid))
|
||||
{
|
||||
// Get the paste itself.
|
||||
$paste = $this->_model()->read($dataid);
|
||||
|
||||
// See if paste has expired.
|
||||
if (
|
||||
isset($paste->meta->expire_date) &&
|
||||
$paste->meta->expire_date < time()
|
||||
)
|
||||
try {
|
||||
$paste = $this->_model->getPaste($dataid);
|
||||
if ($paste->exists())
|
||||
{
|
||||
// Delete the paste
|
||||
$this->_model()->delete($dataid);
|
||||
$this->_error = self::GENERIC_ERROR;
|
||||
$data = $paste->get();
|
||||
$this->_doesExpire = property_exists($data, 'meta') && property_exists($data->meta, 'expire_date');
|
||||
$this->_data = json_encode($data);
|
||||
}
|
||||
// If no error, return the paste.
|
||||
else
|
||||
{
|
||||
// We kindly provide the remaining time before expiration (in seconds)
|
||||
if (
|
||||
property_exists($paste->meta, 'expire_date')
|
||||
) $paste->meta->remaining_time = $paste->meta->expire_date - time();
|
||||
|
||||
// The paste itself is the first in the list of encrypted messages.
|
||||
$messages = array($paste);
|
||||
|
||||
// If it's a discussion, get all comments.
|
||||
if (
|
||||
property_exists($paste->meta, 'opendiscussion') &&
|
||||
$paste->meta->opendiscussion
|
||||
)
|
||||
{
|
||||
$messages = array_merge(
|
||||
$messages,
|
||||
$this->_model()->readComments($dataid)
|
||||
);
|
||||
}
|
||||
$this->_data = json_encode($messages);
|
||||
$this->_error = self::GENERIC_ERROR;
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
$this->_error = $e->getMessage();
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->_error = self::GENERIC_ERROR;
|
||||
}
|
||||
if ($isJson)
|
||||
|
||||
if ($this->_request->isJsonApiCall())
|
||||
{
|
||||
if (strlen($this->_error))
|
||||
{
|
||||
@@ -551,7 +392,7 @@ class zerobin
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->_return_message(0, $dataid, array('messages' => $messages));
|
||||
$this->_return_message(0, $dataid, json_decode($this->_data, true));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -574,49 +415,84 @@ class zerobin
|
||||
|
||||
// label all the expiration options
|
||||
$expire = array();
|
||||
foreach ($this->_conf['expire_options'] as $key => $value) {
|
||||
$expire[$key] = array_key_exists($key, $this->_conf['expire_labels']) ?
|
||||
$this->_conf['expire_labels'][$key] :
|
||||
$key;
|
||||
foreach ($this->_conf->getSection('expire_options') as $time => $seconds)
|
||||
{
|
||||
$expire[$time] = ($seconds == 0) ? i18n::_(ucfirst($time)): filter::time_humanreadable($time);
|
||||
}
|
||||
|
||||
// translate all the formatter options
|
||||
$formatters = array_map(array('i18n', 'translate'), $this->_conf->getSection('formatter_options'));
|
||||
|
||||
// set language cookie if that functionality was enabled
|
||||
$languageselection = '';
|
||||
if ($this->_conf->getKey('languageselection'))
|
||||
{
|
||||
$languageselection = i18n::getLanguage();
|
||||
setcookie('lang', $languageselection);
|
||||
}
|
||||
|
||||
$page = new RainTPL;
|
||||
$page::$path_replace = false;
|
||||
// we escape it here because ENT_NOQUOTES can't be used in RainTPL templates
|
||||
$page->assign('CIPHERDATA', htmlspecialchars($this->_data, ENT_NOQUOTES));
|
||||
$page->assign('ERROR', $this->_error);
|
||||
$page->assign('STATUS', $this->_status);
|
||||
$page->assign('ERROR', i18n::_($this->_error));
|
||||
$page->assign('STATUS', i18n::_($this->_status));
|
||||
$page->assign('VERSION', self::VERSION);
|
||||
$page->assign('DISCUSSION', $this->_getMainConfig('discussion', true));
|
||||
$page->assign('OPENDISCUSSION', $this->_getMainConfig('opendiscussion', true));
|
||||
$page->assign('SYNTAXHIGHLIGHTING', $this->_getMainConfig('syntaxhighlighting', true));
|
||||
$page->assign('SYNTAXHIGHLIGHTINGTHEME', $this->_getMainConfig('syntaxhighlightingtheme', ''));
|
||||
$page->assign('NOTICE', $this->_getMainConfig('notice', ''));
|
||||
$page->assign('BURNAFTERREADINGSELECTED', $this->_getMainConfig('burnafterreadingselected', false));
|
||||
$page->assign('PASSWORD', $this->_getMainConfig('password', true));
|
||||
$page->assign('BASE64JSVERSION', $this->_getMainConfig('base64version', '2.1.9'));
|
||||
$page->assign('DISCUSSION', $this->_conf->getKey('discussion'));
|
||||
$page->assign('OPENDISCUSSION', $this->_conf->getKey('opendiscussion'));
|
||||
$page->assign('MARKDOWN', array_key_exists('markdown', $formatters));
|
||||
$page->assign('SYNTAXHIGHLIGHTING', array_key_exists('syntaxhighlighting', $formatters));
|
||||
$page->assign('SYNTAXHIGHLIGHTINGTHEME', $this->_conf->getKey('syntaxhighlightingtheme'));
|
||||
$page->assign('FORMATTER', $formatters);
|
||||
$page->assign('FORMATTERDEFAULT', $this->_conf->getKey('defaultformatter'));
|
||||
$page->assign('NOTICE', i18n::_($this->_conf->getKey('notice')));
|
||||
$page->assign('BURNAFTERREADINGSELECTED', $this->_conf->getKey('burnafterreadingselected'));
|
||||
$page->assign('PASSWORD', $this->_conf->getKey('password'));
|
||||
$page->assign('FILEUPLOAD', $this->_conf->getKey('fileupload'));
|
||||
$page->assign('BASE64JSVERSION', $this->_conf->getKey('base64version'));
|
||||
$page->assign('LANGUAGESELECTION', $languageselection);
|
||||
$page->assign('LANGUAGES', i18n::getLanguageLabels(i18n::getAvailableLanguages()));
|
||||
$page->assign('EXPIRE', $expire);
|
||||
$page->assign('EXPIREDEFAULT', $this->_conf['expire']['default']);
|
||||
$page->draw($this->_getMainConfig('template', 'page'));
|
||||
$page->assign('EXPIREDEFAULT', $this->_conf->getKey('default', 'expire'));
|
||||
$page->assign('EXPIRECLONE', !$this->_doesExpire || ($this->_doesExpire && $this->_conf->getKey('clone', 'expire')));
|
||||
$page->draw($this->_conf->getKey('template'));
|
||||
}
|
||||
|
||||
/**
|
||||
* get configuration option from [main] section, optionally set a default
|
||||
* outputs requested JSON-LD context
|
||||
*
|
||||
* @access private
|
||||
* @param string $option
|
||||
* @param mixed $default (optional)
|
||||
* @return mixed
|
||||
* @param string $type
|
||||
* @return void
|
||||
*/
|
||||
private function _getMainConfig($option, $default = false)
|
||||
private function _jsonld($type)
|
||||
{
|
||||
return array_key_exists($option, $this->_conf['main']) ?
|
||||
$this->_conf['main'][$option] :
|
||||
$default;
|
||||
if (
|
||||
$type !== 'paste' && $type !== 'comment' &&
|
||||
$type !== 'pastemeta' && $type !== 'commentmeta'
|
||||
)
|
||||
{
|
||||
$type = '';
|
||||
}
|
||||
$content = '{}';
|
||||
$file = PUBLIC_PATH . DIRECTORY_SEPARATOR . 'js' . DIRECTORY_SEPARATOR . $type . '.jsonld';
|
||||
if (is_readable($file))
|
||||
{
|
||||
$content = str_replace(
|
||||
'?jsonld=',
|
||||
$this->_urlbase . '?jsonld=',
|
||||
file_get_contents($file)
|
||||
);
|
||||
}
|
||||
|
||||
header('Content-type: application/ld+json');
|
||||
header('Access-Control-Allow-Origin: *');
|
||||
header('Access-Control-Allow-Methods: GET');
|
||||
echo $content;
|
||||
}
|
||||
|
||||
/**
|
||||
* return JSON encoded message and exit
|
||||
* prepares JSON encoded status message
|
||||
*
|
||||
* @access private
|
||||
* @param bool $status
|
||||
@@ -629,11 +505,12 @@ class zerobin
|
||||
$result = array('status' => $status);
|
||||
if ($status)
|
||||
{
|
||||
$result['message'] = $message;
|
||||
$result['message'] = i18n::_($message);
|
||||
}
|
||||
else
|
||||
{
|
||||
$result['id'] = $message;
|
||||
$result['url'] = $this->_urlbase . '?' . $message;
|
||||
}
|
||||
$result += $other;
|
||||
$this->_json = json_encode($result);
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
* @link http://sebsauvage.net/wiki/doku.php?id=php:zerobin
|
||||
* @copyright 2012 Sébastien SAUVAGE (sebsauvage.net)
|
||||
* @license http://www.opensource.org/licenses/zlib-license.php The zlib/libpng License
|
||||
* @version 0.20
|
||||
* @version 0.22
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -17,12 +17,12 @@
|
||||
*/
|
||||
abstract class zerobin_abstract
|
||||
{
|
||||
/**
|
||||
/**
|
||||
* singleton instance
|
||||
*
|
||||
* @access protected
|
||||
* @static
|
||||
* @var zerobin
|
||||
* @var zerobin_abstract
|
||||
*/
|
||||
protected static $_instance = null;
|
||||
|
||||
@@ -87,7 +87,7 @@ abstract class zerobin_abstract
|
||||
*
|
||||
* @access public
|
||||
* @param string $dataid
|
||||
* @return void
|
||||
* @return bool
|
||||
*/
|
||||
abstract public function exists($pasteid);
|
||||
|
||||
@@ -122,4 +122,24 @@ abstract class zerobin_abstract
|
||||
* @return void
|
||||
*/
|
||||
abstract public function existsComment($pasteid, $parentid, $commentid);
|
||||
|
||||
/**
|
||||
* Get next free slot for comment from postdate.
|
||||
*
|
||||
* @access public
|
||||
* @param array $comments
|
||||
* @param int|string $postdate
|
||||
* @return void
|
||||
*/
|
||||
protected function getOpenSlot(&$comments, $postdate)
|
||||
{
|
||||
if (array_key_exists($postdate, $comments))
|
||||
{
|
||||
$parts = explode('.', $postdate, 2);
|
||||
if (!array_key_exists(1, $parts)) $parts[1] = 0;
|
||||
++$parts[1];
|
||||
return $this->getOpenSlot($comments, implode('.', $parts));
|
||||
}
|
||||
return $postdate;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
* @link http://sebsauvage.net/wiki/doku.php?id=php:zerobin
|
||||
* @copyright 2012 Sébastien SAUVAGE (sebsauvage.net)
|
||||
* @license http://www.opensource.org/licenses/zlib-license.php The zlib/libpng License
|
||||
* @version 0.20
|
||||
* @version 0.22
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -75,9 +75,20 @@ class zerobin_data extends zerobin_abstract
|
||||
public function read($pasteid)
|
||||
{
|
||||
if(!$this->exists($pasteid)) return false;
|
||||
return json_decode(
|
||||
$paste = json_decode(
|
||||
file_get_contents(self::_dataid2path($pasteid) . $pasteid)
|
||||
);
|
||||
if (property_exists($paste->meta, 'attachment'))
|
||||
{
|
||||
$paste->attachment = $paste->meta->attachment;
|
||||
unset($paste->meta->attachment);
|
||||
if (property_exists($paste->meta, 'attachmentname'))
|
||||
{
|
||||
$paste->attachmentname = $paste->meta->attachmentname;
|
||||
unset($paste->meta->attachmentname);
|
||||
}
|
||||
}
|
||||
return $paste;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -90,7 +101,7 @@ class zerobin_data extends zerobin_abstract
|
||||
public function delete($pasteid)
|
||||
{
|
||||
// Delete the paste itself.
|
||||
unlink(self::_dataid2path($pasteid) . $pasteid);
|
||||
@unlink(self::_dataid2path($pasteid) . $pasteid);
|
||||
|
||||
// Delete discussion if it exists.
|
||||
$discdir = self::_dataid2discussionpath($pasteid);
|
||||
@@ -100,7 +111,7 @@ class zerobin_data extends zerobin_abstract
|
||||
$dir = dir($discdir);
|
||||
while (false !== ($filename = $dir->read()))
|
||||
{
|
||||
if (is_file($discdir.$filename)) unlink($discdir.$filename);
|
||||
if (is_file($discdir.$filename)) @unlink($discdir.$filename);
|
||||
}
|
||||
$dir->close();
|
||||
|
||||
@@ -161,16 +172,17 @@ class zerobin_data extends zerobin_abstract
|
||||
// - pasteid is the paste this reply belongs to.
|
||||
// - commentid is the comment identifier itself.
|
||||
// - parentid is the comment this comment replies to (It can be pasteid)
|
||||
if (is_file($discdir.$filename))
|
||||
if (is_file($discdir . $filename))
|
||||
{
|
||||
$comment = json_decode(file_get_contents($discdir.$filename));
|
||||
$comment = json_decode(file_get_contents($discdir . $filename));
|
||||
$items = explode('.', $filename);
|
||||
// Add some meta information not contained in file.
|
||||
$comment->meta->commentid=$items[1];
|
||||
$comment->meta->parentid=$items[2];
|
||||
$comment->id = $items[1];
|
||||
$comment->parentid = $items[2];
|
||||
|
||||
// Store in array
|
||||
$comments[$comment->meta->postdate]=$comment;
|
||||
$key = $this->getOpenSlot($comments, (int) $comment->meta->postdate);
|
||||
$comments[$key] = $comment;
|
||||
}
|
||||
}
|
||||
$dir->close();
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
* @link http://sebsauvage.net/wiki/doku.php?id=php:zerobin
|
||||
* @copyright 2012 Sébastien SAUVAGE (sebsauvage.net)
|
||||
* @license http://www.opensource.org/licenses/zlib-license.php The zlib/libpng License
|
||||
* @version 0.20
|
||||
* @version 0.22
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -80,96 +80,63 @@ class zerobin_db extends zerobin_abstract
|
||||
array_key_exists('opt', $options)
|
||||
)
|
||||
{
|
||||
// check if the database contains the required tables
|
||||
// set default options
|
||||
$options['opt'][PDO::ATTR_ERRMODE] = PDO::ERRMODE_EXCEPTION;
|
||||
$options['opt'][PDO::ATTR_EMULATE_PREPARES] = false;
|
||||
$options['opt'][PDO::ATTR_PERSISTENT] = true;
|
||||
$db_tables_exist = true;
|
||||
|
||||
// setup type and dabase connection
|
||||
self::$_type = strtolower(
|
||||
substr($options['dsn'], 0, strpos($options['dsn'], ':'))
|
||||
);
|
||||
switch(self::$_type)
|
||||
{
|
||||
case 'ibm':
|
||||
$sql = 'SELECT tabname FROM SYSCAT.TABLES ';
|
||||
break;
|
||||
case 'informix':
|
||||
$sql = 'SELECT tabname FROM systables ';
|
||||
break;
|
||||
case 'mssql':
|
||||
$sql = "SELECT name FROM sysobjects "
|
||||
. "WHERE type = 'U' ORDER BY name";
|
||||
break;
|
||||
case 'mysql':
|
||||
$sql = 'SHOW TABLES';
|
||||
break;
|
||||
case 'oci':
|
||||
$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_)' "
|
||||
. "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_'";
|
||||
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";
|
||||
break;
|
||||
default:
|
||||
throw new Exception(
|
||||
'PDO type ' .
|
||||
self::$_type .
|
||||
' is currently not supported.',
|
||||
5
|
||||
);
|
||||
}
|
||||
$tableQuery = self::_getTableQuery(self::$_type);
|
||||
self::$_db = new PDO(
|
||||
$options['dsn'],
|
||||
$options['usr'],
|
||||
$options['pwd'],
|
||||
$options['opt']
|
||||
);
|
||||
$statement = self::$_db->query($sql);
|
||||
$tables = $statement->fetchAll(PDO::FETCH_COLUMN, 0);
|
||||
|
||||
// create paste table if needed
|
||||
if (!array_key_exists(self::$_prefix . 'paste', $tables))
|
||||
// check if the database contains the required tables
|
||||
$tables = self::$_db->query($tableQuery)->fetchAll(PDO::FETCH_COLUMN, 0);
|
||||
|
||||
// create paste table if necessary
|
||||
if (!in_array(self::$_prefix . 'paste', $tables))
|
||||
{
|
||||
self::$_db->exec(
|
||||
'CREATE TABLE ' . self::$_prefix . 'paste ( ' .
|
||||
'dataid CHAR(16), ' .
|
||||
'data TEXT, ' .
|
||||
'postdate INT, ' .
|
||||
'expiredate INT, ' .
|
||||
'opendiscussion INT, ' .
|
||||
'burnafterreading INT );'
|
||||
);
|
||||
self::_createPasteTable();
|
||||
$db_tables_exist = false;
|
||||
}
|
||||
|
||||
// create comment table if needed
|
||||
if (!array_key_exists(self::$_prefix . 'comment', $tables))
|
||||
// create comment table if necessary
|
||||
if (!in_array(self::$_prefix . 'comment', $tables))
|
||||
{
|
||||
self::$_db->exec(
|
||||
'CREATE TABLE ' . self::$_prefix . 'comment ( ' .
|
||||
'dataid CHAR(16), ' .
|
||||
'pasteid CHAR(16), ' .
|
||||
'parentid CHAR(16), ' .
|
||||
'data TEXT, ' .
|
||||
'nickname VARCHAR(255), ' .
|
||||
'vizhash TEXT, ' .
|
||||
'postdate INT );'
|
||||
);
|
||||
self::_createCommentTable();
|
||||
$db_tables_exist = false;
|
||||
}
|
||||
|
||||
// create config table if necessary
|
||||
$db_version = zerobin::VERSION;
|
||||
if (!in_array(self::$_prefix . 'config', $tables))
|
||||
{
|
||||
self::_createConfigTable();
|
||||
// if we only needed to create the config table, the DB is older then 0.22
|
||||
if ($db_tables_exist) $db_version = '0.21';
|
||||
}
|
||||
else
|
||||
{
|
||||
$db_version = self::_getConfig('VERSION');
|
||||
}
|
||||
|
||||
// update database structure if necessary
|
||||
if (version_compare($db_version, zerobin::VERSION, '<'))
|
||||
{
|
||||
self::_upgradeDatabase($db_version);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return parent::$_instance;
|
||||
return self::$_instance;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -183,7 +150,7 @@ class zerobin_db extends zerobin_abstract
|
||||
public function create($pasteid, $paste)
|
||||
{
|
||||
if (
|
||||
array_key_exists($pasteid, self::$_cache)
|
||||
array_key_exists($pasteid, self::$_cache)
|
||||
) {
|
||||
if(false !== self::$_cache[$pasteid]) {
|
||||
return false;
|
||||
@@ -192,21 +159,48 @@ class zerobin_db extends zerobin_abstract
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
!array_key_exists('opendiscussion', $paste['meta'])
|
||||
) $paste['meta']['opendiscussion'] = false;
|
||||
if (
|
||||
!array_key_exists('burnafterreading', $paste['meta'])
|
||||
) $paste['meta']['burnafterreading'] = false;
|
||||
$opendiscussion = $burnafterreading = false;
|
||||
$attachment = $attachmentname = '';
|
||||
$meta = $paste['meta'];
|
||||
unset($meta['postdate']);
|
||||
$expire_date = 0;
|
||||
if (array_key_exists('expire_date', $paste['meta']))
|
||||
{
|
||||
$expire_date = (int) $paste['meta']['expire_date'];
|
||||
unset($meta['expire_date']);
|
||||
}
|
||||
if (array_key_exists('opendiscussion', $paste['meta']))
|
||||
{
|
||||
$opendiscussion = (bool) $paste['meta']['opendiscussion'];
|
||||
unset($meta['opendiscussion']);
|
||||
}
|
||||
if (array_key_exists('burnafterreading', $paste['meta']))
|
||||
{
|
||||
$burnafterreading = (bool) $paste['meta']['burnafterreading'];
|
||||
unset($meta['burnafterreading']);
|
||||
}
|
||||
if (array_key_exists('attachment', $paste['meta']))
|
||||
{
|
||||
$attachment = $paste['meta']['attachment'];
|
||||
unset($meta['attachment']);
|
||||
}
|
||||
if (array_key_exists('attachmentname', $paste['meta']))
|
||||
{
|
||||
$attachmentname = $paste['meta']['attachmentname'];
|
||||
unset($meta['attachmentname']);
|
||||
}
|
||||
return self::_exec(
|
||||
'INSERT INTO ' . self::$_prefix . 'paste VALUES(?,?,?,?,?,?)',
|
||||
'INSERT INTO ' . self::$_prefix . 'paste VALUES(?,?,?,?,?,?,?,?,?)',
|
||||
array(
|
||||
$pasteid,
|
||||
$paste['data'],
|
||||
$paste['meta']['postdate'],
|
||||
$paste['meta']['expire_date'],
|
||||
(int) $paste['meta']['opendiscussion'],
|
||||
(int) $paste['meta']['burnafterreading'],
|
||||
$expire_date,
|
||||
(int) $opendiscussion,
|
||||
(int) $burnafterreading,
|
||||
json_encode($meta),
|
||||
$attachment,
|
||||
$attachmentname,
|
||||
)
|
||||
);
|
||||
}
|
||||
@@ -233,9 +227,36 @@ class zerobin_db extends zerobin_abstract
|
||||
// create object
|
||||
self::$_cache[$pasteid] = new stdClass;
|
||||
self::$_cache[$pasteid]->data = $paste['data'];
|
||||
self::$_cache[$pasteid]->meta = new stdClass;
|
||||
|
||||
$meta = json_decode($paste['meta']);
|
||||
if (!is_object($meta)) $meta = new stdClass;
|
||||
|
||||
// support older attachments
|
||||
if (property_exists($meta, 'attachment'))
|
||||
{
|
||||
self::$_cache[$pasteid]->attachment = $meta->attachment;
|
||||
unset($meta->attachment);
|
||||
if (property_exists($meta, 'attachmentname'))
|
||||
{
|
||||
self::$_cache[$pasteid]->attachmentname = $meta->attachmentname;
|
||||
unset($meta->attachmentname);
|
||||
}
|
||||
}
|
||||
// support current attachments
|
||||
elseif (array_key_exists('attachment', $paste) && strlen($paste['attachment']))
|
||||
{
|
||||
self::$_cache[$pasteid]->attachment = $paste['attachment'];
|
||||
if (array_key_exists('attachmentname', $paste) && strlen($paste['attachmentname']))
|
||||
{
|
||||
self::$_cache[$pasteid]->attachmentname = $paste['attachmentname'];
|
||||
}
|
||||
}
|
||||
self::$_cache[$pasteid]->meta = $meta;
|
||||
self::$_cache[$pasteid]->meta->postdate = (int) $paste['postdate'];
|
||||
self::$_cache[$pasteid]->meta->expire_date = (int) $paste['expiredate'];
|
||||
$expire_date = (int) $paste['expiredate'];
|
||||
if (
|
||||
$expire_date > 0
|
||||
) self::$_cache[$pasteid]->meta->expire_date = $expire_date;
|
||||
if (
|
||||
$paste['opendiscussion']
|
||||
) self::$_cache[$pasteid]->meta->opendiscussion = true;
|
||||
@@ -325,24 +346,23 @@ class zerobin_db extends zerobin_abstract
|
||||
array($pasteid)
|
||||
);
|
||||
|
||||
// create object
|
||||
$commentTemplate = new stdClass;
|
||||
$commentTemplate->meta = new stdClass;
|
||||
|
||||
// create comment list
|
||||
$comments = array();
|
||||
if (count($rows))
|
||||
{
|
||||
foreach ($rows as $row)
|
||||
{
|
||||
$i = (int) $row['postdate'];
|
||||
$comments[$i] = clone $commentTemplate;
|
||||
$i = $this->getOpenSlot($comments, (int) $row['postdate']);
|
||||
$comments[$i] = new stdClass;
|
||||
$comments[$i]->id = $row['dataid'];
|
||||
$comments[$i]->parentid = $row['parentid'];
|
||||
$comments[$i]->data = $row['data'];
|
||||
$comments[$i]->meta->nickname = $row['nickname'];
|
||||
$comments[$i]->meta->vizhash = $row['vizhash'];
|
||||
$comments[$i]->meta->postdate = $i;
|
||||
$comments[$i]->meta->commentid = $row['dataid'];
|
||||
$comments[$i]->meta->parentid = $row['parentid'];
|
||||
$comments[$i]->meta = new stdClass;
|
||||
$comments[$i]->meta->postdate = (int) $row['postdate'];
|
||||
if (array_key_exists('nickname', $row))
|
||||
$comments[$i]->meta->nickname = $row['nickname'];
|
||||
if (array_key_exists('vizhash', $row))
|
||||
$comments[$i]->meta->vizhash = $row['vizhash'];
|
||||
}
|
||||
ksort($comments);
|
||||
}
|
||||
@@ -406,4 +426,227 @@ class zerobin_db extends zerobin_abstract
|
||||
$statement->closeCursor();
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* get table list query, depending on the database type
|
||||
*
|
||||
* @access private
|
||||
* @static
|
||||
* @param string $type
|
||||
* @throws Exception
|
||||
* @return string
|
||||
*/
|
||||
private static function _getTableQuery($type)
|
||||
{
|
||||
switch($type)
|
||||
{
|
||||
case 'ibm':
|
||||
$sql = 'SELECT tabname FROM SYSCAT.TABLES ';
|
||||
break;
|
||||
case 'informix':
|
||||
$sql = 'SELECT tabname FROM systables ';
|
||||
break;
|
||||
case 'mssql':
|
||||
$sql = "SELECT name FROM sysobjects "
|
||||
. "WHERE type = 'U' ORDER BY name";
|
||||
break;
|
||||
case 'mysql':
|
||||
$sql = 'SHOW TABLES';
|
||||
break;
|
||||
case 'oci':
|
||||
$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_)' "
|
||||
. "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_'";
|
||||
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";
|
||||
break;
|
||||
default:
|
||||
throw new Exception(
|
||||
"PDO type $type is currently not supported.", 5
|
||||
);
|
||||
}
|
||||
return $sql;
|
||||
}
|
||||
|
||||
/**
|
||||
* get a value by key from the config table
|
||||
*
|
||||
* @access private
|
||||
* @static
|
||||
* @param string $key
|
||||
* @throws PDOException
|
||||
* @return string
|
||||
*/
|
||||
private static function _getConfig($key)
|
||||
{
|
||||
$row = self::_select(
|
||||
'SELECT value FROM ' . self::$_prefix . 'config WHERE id = ?',
|
||||
array($key), true
|
||||
);
|
||||
return $row['value'];
|
||||
}
|
||||
|
||||
/**
|
||||
* get the primary key clauses, depending on the database driver
|
||||
*
|
||||
* @access private
|
||||
* @static
|
||||
* @param string $key
|
||||
* @return array
|
||||
*/
|
||||
private static function _getPrimaryKeyClauses($key = 'dataid')
|
||||
{
|
||||
$main_key = $after_key = '';
|
||||
if (self::$_type === 'mysql')
|
||||
{
|
||||
$after_key = ", PRIMARY KEY ($key)";
|
||||
}
|
||||
else
|
||||
{
|
||||
$main_key = ' PRIMARY KEY';
|
||||
}
|
||||
return array($main_key, $after_key);
|
||||
}
|
||||
|
||||
/**
|
||||
* create the paste table
|
||||
*
|
||||
* @access private
|
||||
* @static
|
||||
* @return void
|
||||
*/
|
||||
private static function _createPasteTable()
|
||||
{
|
||||
list($main_key, $after_key) = self::_getPrimaryKeyClauses();
|
||||
self::$_db->exec(
|
||||
'CREATE TABLE ' . self::$_prefix . 'paste ( ' .
|
||||
"dataid CHAR(16) NOT NULL$main_key, " .
|
||||
'data BLOB, ' .
|
||||
'postdate INT, ' .
|
||||
'expiredate INT, ' .
|
||||
'opendiscussion INT, ' .
|
||||
'burnafterreading INT, ' .
|
||||
'meta TEXT, ' .
|
||||
'attachment MEDIUMBLOB, ' .
|
||||
"attachmentname BLOB$after_key );"
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* create the paste table
|
||||
*
|
||||
* @access private
|
||||
* @static
|
||||
* @return void
|
||||
*/
|
||||
private static function _createCommentTable()
|
||||
{
|
||||
list($main_key, $after_key) = self::_getPrimaryKeyClauses();
|
||||
self::$_db->exec(
|
||||
'CREATE TABLE ' . self::$_prefix . 'comment ( ' .
|
||||
"dataid CHAR(16) NOT NULL$main_key, " .
|
||||
'pasteid CHAR(16), ' .
|
||||
'parentid CHAR(16), ' .
|
||||
'data BLOB, ' .
|
||||
'nickname BLOB, ' .
|
||||
'vizhash BLOB, ' .
|
||||
"postdate INT$after_key );"
|
||||
);
|
||||
self::$_db->exec(
|
||||
'CREATE INDEX parent ON ' . self::$_prefix . 'comment(pasteid);'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* create the paste table
|
||||
*
|
||||
* @access private
|
||||
* @static
|
||||
* @return void
|
||||
*/
|
||||
private static function _createConfigTable()
|
||||
{
|
||||
list($main_key, $after_key) = self::_getPrimaryKeyClauses('id');
|
||||
self::$_db->exec(
|
||||
'CREATE TABLE ' . self::$_prefix . 'config ( ' .
|
||||
"id CHAR(16) NOT NULL$main_key, value TEXT$after_key );"
|
||||
);
|
||||
self::_exec(
|
||||
'INSERT INTO ' . self::$_prefix . 'config VALUES(?,?)',
|
||||
array('VERSION', zerobin::VERSION)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* upgrade the database schema from an old version
|
||||
*
|
||||
* @access private
|
||||
* @static
|
||||
* @param string $oldversion
|
||||
* @return void
|
||||
*/
|
||||
private static function _upgradeDatabase($oldversion)
|
||||
{
|
||||
switch ($oldversion)
|
||||
{
|
||||
case '0.21':
|
||||
// create the meta column if necessary (pre 0.21 change)
|
||||
try {
|
||||
self::$_db->exec('SELECT meta FROM ' . self::$_prefix . 'paste LIMIT 1;', array());
|
||||
} catch (PDOException $e) {
|
||||
self::$_db->exec('ALTER TABLE ' . self::$_prefix . 'paste ADD COLUMN meta TEXT;');
|
||||
}
|
||||
// SQLite only allows one ALTER statement at a time...
|
||||
self::$_db->exec(
|
||||
'ALTER TABLE ' . self::$_prefix . 'paste ADD COLUMN attachment MEDIUMBLOB;'
|
||||
);
|
||||
self::$_db->exec(
|
||||
'ALTER TABLE ' . self::$_prefix . 'paste ADD COLUMN attachmentname BLOB;'
|
||||
);
|
||||
// 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::$_prefix . 'paste ' .
|
||||
'ADD PRIMARY KEY (dataid),' .
|
||||
'MODIFY COLUMN data BLOB;'
|
||||
);
|
||||
self::$_db->exec(
|
||||
'ALTER TABLE ' . self::$_prefix . 'comment ' .
|
||||
'ADD PRIMARY KEY (dataid),' .
|
||||
'MODIFY COLUMN data BLOB, ' .
|
||||
'MODIFY COLUMN nickname BLOB, ' .
|
||||
'MODIFY COLUMN vizhash BLOB;'
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
self::$_db->exec(
|
||||
'CREATE UNIQUE INDEX primary ON ' . self::$_prefix . 'paste(dataid);'
|
||||
);
|
||||
self::$_db->exec(
|
||||
'CREATE UNIQUE INDEX primary ON ' . self::$_prefix . 'comment(dataid);'
|
||||
);
|
||||
}
|
||||
self::$_db->exec(
|
||||
'CREATE INDEX parent ON ' . self::$_prefix . 'comment(pasteid);'
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
202
tpl/bootstrap-compact.html
Normal file
202
tpl/bootstrap-compact.html
Normal file
@@ -0,0 +1,202 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="robots" content="noindex" />
|
||||
<title>{function="t('ZeroBin')"}</title>
|
||||
<link type="text/css" rel="stylesheet" href="css/bootstrap/bootstrap-3.3.5.css" />
|
||||
<link type="text/css" rel="stylesheet" href="css/bootstrap/bootstrap-theme-3.3.5.css" />
|
||||
<link type="text/css" rel="stylesheet" href="css/bootstrap/zerobin.css?{$VERSION|rawurlencode}" />{if="$SYNTAXHIGHLIGHTING"}
|
||||
<link type="text/css" rel="stylesheet" href="css/prettify/prettify.css?{$VERSION|rawurlencode}" />{if="strlen($SYNTAXHIGHLIGHTINGTHEME)"}
|
||||
<link type="text/css" rel="stylesheet" href="css/prettify/{$SYNTAXHIGHLIGHTINGTHEME}.css?{$VERSION|rawurlencode}" />{/if}{/if}
|
||||
<script type="text/javascript" src="js/jquery-1.11.3.js"></script>
|
||||
<script type="text/javascript" src="js/sjcl-1.0.2.js"></script>
|
||||
<script type="text/javascript" src="js/base64-{$BASE64JSVERSION}.js"></script>
|
||||
<script type="text/javascript" src="js/rawdeflate-0.5.js"></script>
|
||||
<script type="text/javascript" src="js/rawinflate-0.3.js"></script>
|
||||
<script type="text/javascript" src="js/bootstrap-3.3.5.js"></script>{if="$SYNTAXHIGHLIGHTING"}
|
||||
<script type="text/javascript" src="js/prettify.js?{$VERSION|rawurlencode}"></script>{/if}{if="$MARKDOWN"}
|
||||
<script type="text/javascript" src="js/showdown.js?{$VERSION|rawurlencode}"></script>{/if}
|
||||
<script type="text/javascript" src="js/zerobin.js?{$VERSION|rawurlencode}"></script>
|
||||
<!--[if lt IE 10]>
|
||||
<style type="text/css">#ienotice {display:block !important;} #oldienotice {display:block !important;}</style>
|
||||
<![endif]-->
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="img/favicons/apple-touch-icon.png?{$VERSION|rawurlencode}" />
|
||||
<link rel="icon" type="image/png" href="img/favicons/android-chrome-192x192.png?{$VERSION|rawurlencode}" sizes="192x192" />
|
||||
<link rel="icon" type="image/png" href="img/favicons/favicon-32x32.png?{$VERSION|rawurlencode}" sizes="32x32" />
|
||||
<link rel="icon" type="image/png" href="img/favicons/favicon-96x96.png?{$VERSION|rawurlencode}" sizes="96x96" />
|
||||
<link rel="icon" type="image/png" href="img/favicons/favicon-16x16.png?{$VERSION|rawurlencode}" sizes="16x16" />
|
||||
</head>
|
||||
<body role="document" class="navbar-spacing">
|
||||
<nav class="navbar navbar-default navbar-fixed-top">
|
||||
<div class="container">
|
||||
<div class="navbar-header">
|
||||
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
|
||||
<span class="sr-only">{function="t('Toggle navigation')"}</span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
</button>
|
||||
<a class="reloadlink navbar-brand" href="/">{function="t('ZeroBin')"}</a>
|
||||
</div>
|
||||
<div id="navbar" class="navbar-collapse collapse">
|
||||
<ul class="nav navbar-nav">
|
||||
<li>
|
||||
<button id="sendbutton" type="button" class="hidden btn btn-default navbar-btn">
|
||||
<span class="glyphicon glyphicon-upload" aria-hidden="true"></span> {function="t('Send')"}
|
||||
</button>{if="$EXPIRECLONE"}
|
||||
<button id="clonebutton" type="button" class="hidden btn btn-default navbar-btn">
|
||||
<span class="glyphicon glyphicon-duplicate" aria-hidden="true"></span> {function="t('Clone')"}
|
||||
</button>{/if}
|
||||
<button id="rawtextbutton" type="button" class="hidden btn btn-default navbar-btn">
|
||||
<span class="glyphicon glyphicon-text-background" aria-hidden="true"></span> {function="t('Raw text')"}
|
||||
</button>
|
||||
</li>
|
||||
<li class="dropdown">
|
||||
<select id="pasteExpiration" name="pasteExpiration" class="hidden">
|
||||
{loop="EXPIRE"}
|
||||
<option value="{$key}"{if="$key == $EXPIREDEFAULT"} selected="selected"{/if}>{$value}</option>{/loop}
|
||||
</select>
|
||||
<a id="expiration" href="#" class="hidden dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">{function="t('Expires')"}: <span id="pasteExpirationDisplay">{$EXPIRE[$EXPIREDEFAULT]}</span> <span class="caret"></span></a>
|
||||
<ul class="dropdown-menu">
|
||||
{loop="EXPIRE"}
|
||||
<li>
|
||||
<a href="#" onclick="$('#pasteExpiration').val('{$key}');$('#pasteExpirationDisplay').text('{$value}');return false;">
|
||||
{$value}
|
||||
</a>
|
||||
</li>{/loop}
|
||||
</ul>
|
||||
</li>
|
||||
<li id="formatter" class="dropdown">
|
||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">{function="t('Options')"} <span class="caret"></span></a>
|
||||
<ul class="dropdown-menu">
|
||||
<li id="burnafterreadingoption" class="checkbox hidden">
|
||||
<label>
|
||||
<input type="checkbox" id="burnafterreading" name="burnafterreading" {if="$BURNAFTERREADINGSELECTED"} checked="checked"{/if} />
|
||||
{function="t('Burn after reading')"}
|
||||
</label>
|
||||
</li>{if="$DISCUSSION"}
|
||||
<li id="opendisc" class="checkbox hidden">
|
||||
<label>
|
||||
<input type="checkbox" id="opendiscussion" name="opendiscussion" {if="$OPENDISCUSSION"} checked="checked"{/if} />
|
||||
{function="t('Open discussion')"}
|
||||
</label>
|
||||
</li>{/if}
|
||||
<li role="separator" class="divider"></li>
|
||||
<li>
|
||||
<div>
|
||||
{function="t('Format')"}: <span id="pasteFormatterDisplay">{$FORMATTER[$FORMATTERDEFAULT]}</span> <span class="caret"></span>
|
||||
</div>
|
||||
</li>
|
||||
{loop="FORMATTER"}
|
||||
<li>
|
||||
<a href="#" onclick="$('#pasteFormatter').val('{$key}');$('#pasteFormatterDisplay').text('{$value}');return false;">
|
||||
{$value}
|
||||
</a>
|
||||
</li>{/loop}
|
||||
</ul>
|
||||
<select id="pasteFormatter" name="pasteFormatter" class="hidden">
|
||||
{loop="FORMATTER"}
|
||||
<option value="{$key}"{if="$key == $FORMATTERDEFAULT"} selected="selected"{/if}>{$value}</option>{/loop}
|
||||
</select>
|
||||
</li>{if="$PASSWORD"}
|
||||
<li>
|
||||
<div id="password" class="navbar-form hidden">
|
||||
<input type="password" id="passwordinput" placeholder="{function="t('Password (recommended)')"}" class="form-control" size="19"/>
|
||||
</div>
|
||||
</li>{/if}{if="$FILEUPLOAD"}
|
||||
<li id="attach" class="hidden dropdown">
|
||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">{function="t('Attach a file')"} <span class="caret"></span></a>
|
||||
<ul class="dropdown-menu">
|
||||
<li id="filewrap">
|
||||
<div>
|
||||
<input type="file" id="file" name="file" />
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<a id="fileremovebutton" href="#">
|
||||
{function="t('Remove attachment')"}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>{/if}
|
||||
</ul>
|
||||
<ul class="nav navbar-nav pull-right">{if="strlen($LANGUAGESELECTION)"}
|
||||
<li id="language" class="dropdown">
|
||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false"><span class="glyphicon glyphicon-flag" aria-hidden="true"></span> {$LANGUAGES[$LANGUAGESELECTION][0]} <span class="caret"></span></a>
|
||||
<ul class="dropdown-menu">
|
||||
{loop="LANGUAGES"}
|
||||
<li>
|
||||
<a href="#" class="reloadlink" onclick="document.cookie='lang={$key}';">
|
||||
{$value[0]} ({$value[1]})
|
||||
</a>
|
||||
</li>{/loop}
|
||||
</ul>
|
||||
</li>{/if}
|
||||
<li>
|
||||
<button id="newbutton" type="button" class="reloadlink hidden btn btn-default navbar-btn">
|
||||
<span class="glyphicon glyphicon-file" aria-hidden="true"></span> {function="t('New')"}
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
<header class="container">{if="strlen($NOTICE)"}
|
||||
<div role="alert" class="alert alert-info">
|
||||
<span class="glyphicon glyphicon-info-sign" aria-hidden="true"></span> {$NOTICE|htmlspecialchars}
|
||||
</div>{/if}
|
||||
<div id="remainingtime" role="alert" class="hidden alert alert-info">
|
||||
<span class="glyphicon glyphicon-info-sign" aria-hidden="true"></span>
|
||||
</div>{if="$FILEUPLOAD"}
|
||||
<div id="attachment" role="alert" class="hidden alert alert-info">
|
||||
<span class="glyphicon glyphicon-info-sign" aria-hidden="true"></span> <a>{function="t('Download attachment')"}</a> <span id="clonedfile" class="hidden">{function="t('Cloned file attached.')"}</span>
|
||||
</div>{/if}{if="strlen($STATUS)"}
|
||||
<div id="status" role="alert" class="alert alert-success">
|
||||
<span class="glyphicon glyphicon-ok" aria-hidden="true"></span> {$STATUS|htmlspecialchars}
|
||||
</div>{/if}
|
||||
<div id="errormessage" role="alert" class="{if="!strlen($ERROR)"}hidden {/if}alert alert-danger"><span class="glyphicon glyphicon-alert" aria-hidden="true"></span> {$ERROR|htmlspecialchars}</div>
|
||||
<div id="noscript" role="alert" class="nonworking alert alert-warning"><span class="glyphicon glyphicon-exclamation-sign" aria-hidden="true"></span> {function="t('Javascript is required for ZeroBin to work.<br />Sorry for the inconvenience.')"}</div>
|
||||
<div id="oldienotice" role="alert" class="hidden nonworking alert alert-danger"><span class="glyphicon glyphicon-alert" aria-hidden="true"></span> {function="t('ZeroBin requires a modern browser to work.')"}</div>
|
||||
<div id="ienotice" role="alert" class="hidden alert alert-warning"><span class="glyphicon glyphicon-question-sign" aria-hidden="true"></span> {function="t('Still using Internet Explorer? Do yourself a favor, switch to a modern browser:')"}
|
||||
<a href="http://www.mozilla.org/firefox/">Firefox</a>,
|
||||
<a href="http://www.opera.com/">Opera</a>,
|
||||
<a href="http://www.google.com/chrome">Chrome</a>,
|
||||
<a href="http://www.apple.com/safari">Safari</a>...
|
||||
</div>
|
||||
<div id="pasteresult" role="alert" class="hidden alert alert-success">
|
||||
<span class="glyphicon glyphicon-ok" aria-hidden="true"></span>
|
||||
<div id="deletelink"></div>
|
||||
<div id="pastelink"></div>
|
||||
</div>
|
||||
</header>
|
||||
<section class="container">
|
||||
<article class="row">
|
||||
<div id="image" class="col-md-12 text-center hidden"></div>
|
||||
<div id="prettymessage" class="col-md-12 hidden">
|
||||
<pre id="prettyprint" class="col-md-12 prettyprint linenums:1"></pre>
|
||||
</div>
|
||||
<div id="cleartext" class="col-md-12 hidden"></div>
|
||||
<p class="col-md-12"><textarea id="message" name="message" cols="80" rows="25" class="form-control hidden"></textarea></p>
|
||||
</article>
|
||||
</section>
|
||||
<section class="container">
|
||||
<div id="discussion" class="hidden">
|
||||
<h4>{function="t('Discussion')"}</h4>
|
||||
<div id="comments"></div>
|
||||
</div>
|
||||
</section>
|
||||
<footer class="container">
|
||||
<div class="row">
|
||||
<h4 class="col-md-5 col-xs-8">{function="t('ZeroBin')"} <small>- {function="t('Because ignorance is bliss')"}</small></h4>
|
||||
<p class="col-md-1 col-xs-4 text-center">{$VERSION}</p>
|
||||
<p id="aboutbox" class="col-md-6 col-xs-12">
|
||||
{function="t('ZeroBin is a minimalist, opensource online pastebin where the server has zero knowledge of pasted data. Data is encrypted/decrypted <i>in the browser</i> using 256 bits AES. More information on the <a href="https://github.com/elrido/ZeroBin/wiki">project page</a>.')"}
|
||||
</p>
|
||||
</div>
|
||||
</footer>
|
||||
<div id="cipherdata" class="hidden">{$CIPHERDATA}</div>
|
||||
</body>
|
||||
</html>
|
||||
198
tpl/bootstrap-dark-page.html
Normal file
198
tpl/bootstrap-dark-page.html
Normal file
@@ -0,0 +1,198 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="robots" content="noindex" />
|
||||
<title>{function="t('ZeroBin')"}</title>
|
||||
<link type="text/css" rel="stylesheet" href="css/bootstrap/bootstrap-theme-3.3.5.css" />
|
||||
<link type="text/css" rel="stylesheet" href="css/bootstrap/darkstrap-0.9.3.css" />
|
||||
<link type="text/css" rel="stylesheet" href="css/bootstrap/zerobin.css?{$VERSION|rawurlencode}" />{if="$SYNTAXHIGHLIGHTING"}
|
||||
<link type="text/css" rel="stylesheet" href="css/prettify/prettify.css?{$VERSION|rawurlencode}" />{if="strlen($SYNTAXHIGHLIGHTINGTHEME)"}
|
||||
<link type="text/css" rel="stylesheet" href="css/prettify/{$SYNTAXHIGHLIGHTINGTHEME}.css?{$VERSION|rawurlencode}" />{/if}{/if}
|
||||
<script type="text/javascript" src="js/jquery-1.11.3.js"></script>
|
||||
<script type="text/javascript" src="js/sjcl-1.0.2.js"></script>
|
||||
<script type="text/javascript" src="js/base64-{$BASE64JSVERSION}.js"></script>
|
||||
<script type="text/javascript" src="js/rawdeflate-0.5.js"></script>
|
||||
<script type="text/javascript" src="js/rawinflate-0.3.js"></script>
|
||||
<script type="text/javascript" src="js/bootstrap-3.3.5.js"></script>{if="$SYNTAXHIGHLIGHTING"}
|
||||
<script type="text/javascript" src="js/prettify.js?{$VERSION|rawurlencode}"></script>{/if}{if="$MARKDOWN"}
|
||||
<script type="text/javascript" src="js/showdown.js?{$VERSION|rawurlencode}"></script>{/if}
|
||||
<script type="text/javascript" src="js/zerobin.js?{$VERSION|rawurlencode}"></script>
|
||||
<!--[if lt IE 10]>
|
||||
<style type="text/css">#ienotice {display:block !important;} #oldienotice {display:block !important;}</style>
|
||||
<![endif]-->
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="img/favicons/apple-touch-icon.png?{$VERSION|rawurlencode}" />
|
||||
<link rel="icon" type="image/png" href="img/favicons/android-chrome-192x192.png?{$VERSION|rawurlencode}" sizes="192x192" />
|
||||
<link rel="icon" type="image/png" href="img/favicons/favicon-32x32.png?{$VERSION|rawurlencode}" sizes="32x32" />
|
||||
<link rel="icon" type="image/png" href="img/favicons/favicon-96x96.png?{$VERSION|rawurlencode}" sizes="96x96" />
|
||||
<link rel="icon" type="image/png" href="img/favicons/favicon-16x16.png?{$VERSION|rawurlencode}" sizes="16x16" />
|
||||
</head>
|
||||
<body role="document">
|
||||
<nav class="navbar navbar-inverse navbar-static-top">
|
||||
<div class="navbar-header">
|
||||
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
|
||||
<span class="sr-only">{function="t('Toggle navigation')"}</span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
</button>
|
||||
<a class="reloadlink navbar-brand" href="/">{function="t('ZeroBin')"}</a>
|
||||
</div>
|
||||
<div id="navbar" class="navbar-collapse collapse">
|
||||
<ul class="nav navbar-nav">
|
||||
<li>
|
||||
<button id="sendbutton" type="button" class="hidden btn btn-warning navbar-btn">
|
||||
<span class="glyphicon glyphicon-upload" aria-hidden="true"></span> {function="t('Send')"}
|
||||
</button>{if="$EXPIRECLONE"}
|
||||
<button id="clonebutton" type="button" class="hidden btn btn-warning navbar-btn">
|
||||
<span class="glyphicon glyphicon-duplicate" aria-hidden="true"></span> {function="t('Clone')"}
|
||||
</button>{/if}
|
||||
<button id="rawtextbutton" type="button" class="hidden btn btn-warning navbar-btn">
|
||||
<span class="glyphicon glyphicon-text-background" aria-hidden="true"></span> {function="t('Raw text')"}
|
||||
</button>
|
||||
</li>
|
||||
<li class="dropdown">
|
||||
<select id="pasteExpiration" name="pasteExpiration" class="hidden">
|
||||
{loop="EXPIRE"}
|
||||
<option value="{$key}"{if="$key == $EXPIREDEFAULT"} selected="selected"{/if}>{$value}</option>{/loop}
|
||||
</select>
|
||||
<a id="expiration" href="#" class="hidden dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">{function="t('Expires')"}: <span id="pasteExpirationDisplay">{$EXPIRE[$EXPIREDEFAULT]}</span> <span class="caret"></span></a>
|
||||
<ul class="dropdown-menu">
|
||||
{loop="EXPIRE"}
|
||||
<li>
|
||||
<a href="#" onclick="$('#pasteExpiration').val('{$key}');$('#pasteExpirationDisplay').text('{$value}');return false;">
|
||||
{$value}
|
||||
</a>
|
||||
</li>{/loop}
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<div id="burnafterreadingoption" class="navbar-text checkbox hidden">
|
||||
<label>
|
||||
<input type="checkbox" id="burnafterreading" name="burnafterreading" {if="$BURNAFTERREADINGSELECTED"} checked="checked"{/if} />
|
||||
{function="t('Burn after reading')"}
|
||||
</label>
|
||||
</div>
|
||||
</li>{if="$DISCUSSION"}
|
||||
<li>
|
||||
<div id="opendisc" class="navbar-text checkbox hidden">
|
||||
<label>
|
||||
<input type="checkbox" id="opendiscussion" name="opendiscussion" {if="$OPENDISCUSSION"} checked="checked"{/if} />
|
||||
{function="t('Open discussion')"}
|
||||
</label>
|
||||
</div>
|
||||
</li>{/if}{if="$PASSWORD"}
|
||||
<li>
|
||||
<div id="password" class="navbar-form hidden">
|
||||
<input type="password" id="passwordinput" placeholder="{function="t('Password (recommended)')"}" class="form-control" size="19"/>
|
||||
</div>
|
||||
</li>{/if}{if="$FILEUPLOAD"}
|
||||
<li id="attach" class="hidden dropdown">
|
||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">{function="t('Attach a file')"} <span class="caret"></span></a>
|
||||
<ul class="dropdown-menu">
|
||||
<li id="filewrap">
|
||||
<div>
|
||||
<input type="file" id="file" name="file" />
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<a id="fileremovebutton" href="#">
|
||||
{function="t('Remove attachment')"}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>{/if}
|
||||
<li class="dropdown">
|
||||
<select id="pasteFormatter" name="pasteFormatter" class="hidden">
|
||||
{loop="FORMATTER"}
|
||||
<option value="{$key}"{if="$key == $FORMATTERDEFAULT"} selected="selected"{/if}>{$value}</option>{/loop}
|
||||
</select>
|
||||
<a id="formatter" href="#" class="hidden dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">{function="t('Format')"}: <span id="pasteFormatterDisplay">{$FORMATTER[$FORMATTERDEFAULT]}</span> <span class="caret"></span></a>
|
||||
<ul class="dropdown-menu">
|
||||
{loop="FORMATTER"}
|
||||
<li>
|
||||
<a href="#" onclick="$('#pasteFormatter').val('{$key}');$('#pasteFormatterDisplay').text('{$value}');return false;">
|
||||
{$value}
|
||||
</a>
|
||||
</li>{/loop}
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
<ul class="nav navbar-nav pull-right">{if="strlen($LANGUAGESELECTION)"}
|
||||
<li id="language" class="dropdown">
|
||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false"><span class="glyphicon glyphicon-flag" aria-hidden="true"></span> {$LANGUAGES[$LANGUAGESELECTION][0]} <span class="caret"></span></a>
|
||||
<ul class="dropdown-menu">
|
||||
{loop="LANGUAGES"}
|
||||
<li>
|
||||
<a href="#" class="reloadlink" onclick="document.cookie='lang={$key}';">
|
||||
{$value[0]} ({$value[1]})
|
||||
</a>
|
||||
</li>{/loop}
|
||||
</ul>
|
||||
</li>{/if}
|
||||
<li>
|
||||
<button id="newbutton" type="button" class="reloadlink hidden btn btn-warning navbar-btn">
|
||||
<span class="glyphicon glyphicon-file" aria-hidden="true"></span> {function="t('New')"}
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
<header class="container">{if="strlen($NOTICE)"}
|
||||
<div role="alert" class="alert alert-info">
|
||||
<span class="glyphicon glyphicon-info-sign" aria-hidden="true"></span> {$NOTICE|htmlspecialchars}
|
||||
</div>{/if}
|
||||
<div id="remainingtime" role="alert" class="hidden alert alert-info">
|
||||
<span class="glyphicon glyphicon-info-sign" aria-hidden="true"></span>
|
||||
</div>{if="$FILEUPLOAD"}
|
||||
<div id="attachment" role="alert" class="hidden alert alert-info">
|
||||
<span class="glyphicon glyphicon-info-sign" aria-hidden="true"></span> <a>{function="t('Download attachment')"}</a> <span id="clonedfile" class="hidden">{function="t('Cloned file attached.')"}</span>
|
||||
</div>{/if}{if="strlen($STATUS)"}
|
||||
<div id="status" role="alert" class="alert alert-success">
|
||||
<span class="glyphicon glyphicon-ok" aria-hidden="true"></span> {$STATUS|htmlspecialchars}
|
||||
</div>{/if}
|
||||
<div id="errormessage" role="alert" class="{if="!strlen($ERROR)"}hidden {/if}alert alert-danger"><span class="glyphicon glyphicon-alert" aria-hidden="true"></span> {$ERROR|htmlspecialchars}</div>
|
||||
<div id="noscript" role="alert" class="nonworking alert alert-error"><span class="glyphicon glyphicon-exclamation-sign" aria-hidden="true"></span> {function="t('Javascript is required for ZeroBin to work.<br />Sorry for the inconvenience.')"}</div>
|
||||
<div id="oldienotice" role="alert" class="hidden nonworking alert alert-danger"><span class="glyphicon glyphicon-alert" aria-hidden="true"></span> {function="t('ZeroBin requires a modern browser to work.')"}</div>
|
||||
<div id="ienotice" role="alert" class="hidden alert alert-error"><span class="glyphicon glyphicon-question-sign" aria-hidden="true"></span> {function="t('Still using Internet Explorer? Do yourself a favor, switch to a modern browser:')"}
|
||||
<a href="http://www.mozilla.org/firefox/">Firefox</a>,
|
||||
<a href="http://www.opera.com/">Opera</a>,
|
||||
<a href="http://www.google.com/chrome">Chrome</a>,
|
||||
<a href="http://www.apple.com/safari">Safari</a>...
|
||||
</div>
|
||||
<div id="pasteresult" role="alert" class="hidden alert alert-success">
|
||||
<span class="glyphicon glyphicon-ok" aria-hidden="true"></span>
|
||||
<div id="deletelink"></div>
|
||||
<div id="pastelink"></div>
|
||||
</div>
|
||||
</header>
|
||||
<section class="container">
|
||||
<article class="row">
|
||||
<div id="image" class="col-md-12 text-center hidden"></div>
|
||||
<div id="prettymessage" class="col-md-12 hidden">
|
||||
<pre id="prettyprint" class="col-md-12 prettyprint linenums:1"></pre>
|
||||
</div>
|
||||
<div id="cleartext" class="col-md-12 hidden"></div>
|
||||
<p class="col-md-12"><textarea id="message" name="message" cols="80" rows="25" class="form-control hidden"></textarea></p>
|
||||
</article>
|
||||
</section>
|
||||
<section class="container">
|
||||
<div id="discussion" class="hidden">
|
||||
<h4>{function="t('Discussion')"}</h4>
|
||||
<div id="comments"></div>
|
||||
</div>
|
||||
</section>
|
||||
<footer class="container">
|
||||
<div class="row">
|
||||
<h4 class="col-md-5 col-xs-8">{function="t('ZeroBin')"} <small>- {function="t('Because ignorance is bliss')"}</small></h4>
|
||||
<p class="col-md-1 col-xs-4 text-center">{$VERSION}</p>
|
||||
<p id="aboutbox" class="col-md-6 col-xs-12">
|
||||
{function="t('ZeroBin is a minimalist, opensource online pastebin where the server has zero knowledge of pasted data. Data is encrypted/decrypted <i>in the browser</i> using 256 bits AES. More information on the <a href="https://github.com/elrido/ZeroBin/wiki">project page</a>.')"}
|
||||
</p>
|
||||
</div>
|
||||
</footer>
|
||||
<div id="cipherdata" class="hidden">{$CIPHERDATA}</div>
|
||||
</body>
|
||||
</html>
|
||||
198
tpl/bootstrap-dark.html
Normal file
198
tpl/bootstrap-dark.html
Normal file
@@ -0,0 +1,198 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="robots" content="noindex" />
|
||||
<title>{function="t('ZeroBin')"}</title>
|
||||
<link type="text/css" rel="stylesheet" href="css/bootstrap/bootstrap-theme-3.3.5.css" />
|
||||
<link type="text/css" rel="stylesheet" href="css/bootstrap/darkstrap-0.9.3.css" />
|
||||
<link type="text/css" rel="stylesheet" href="css/bootstrap/zerobin.css?{$VERSION|rawurlencode}" />{if="$SYNTAXHIGHLIGHTING"}
|
||||
<link type="text/css" rel="stylesheet" href="css/prettify/prettify.css?{$VERSION|rawurlencode}" />{if="strlen($SYNTAXHIGHLIGHTINGTHEME)"}
|
||||
<link type="text/css" rel="stylesheet" href="css/prettify/{$SYNTAXHIGHLIGHTINGTHEME}.css?{$VERSION|rawurlencode}" />{/if}{/if}
|
||||
<script type="text/javascript" src="js/jquery-1.11.3.js"></script>
|
||||
<script type="text/javascript" src="js/sjcl-1.0.2.js"></script>
|
||||
<script type="text/javascript" src="js/base64-{$BASE64JSVERSION}.js"></script>
|
||||
<script type="text/javascript" src="js/rawdeflate-0.5.js"></script>
|
||||
<script type="text/javascript" src="js/rawinflate-0.3.js"></script>
|
||||
<script type="text/javascript" src="js/bootstrap-3.3.5.js"></script>{if="$SYNTAXHIGHLIGHTING"}
|
||||
<script type="text/javascript" src="js/prettify.js?{$VERSION|rawurlencode}"></script>{/if}{if="$MARKDOWN"}
|
||||
<script type="text/javascript" src="js/showdown.js?{$VERSION|rawurlencode}"></script>{/if}
|
||||
<script type="text/javascript" src="js/zerobin.js?{$VERSION|rawurlencode}"></script>
|
||||
<!--[if lt IE 10]>
|
||||
<style type="text/css">#ienotice {display:block !important;} #oldienotice {display:block !important;}</style>
|
||||
<![endif]-->
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="img/favicons/apple-touch-icon.png?{$VERSION|rawurlencode}" />
|
||||
<link rel="icon" type="image/png" href="img/favicons/android-chrome-192x192.png?{$VERSION|rawurlencode}" sizes="192x192" />
|
||||
<link rel="icon" type="image/png" href="img/favicons/favicon-32x32.png?{$VERSION|rawurlencode}" sizes="32x32" />
|
||||
<link rel="icon" type="image/png" href="img/favicons/favicon-96x96.png?{$VERSION|rawurlencode}" sizes="96x96" />
|
||||
<link rel="icon" type="image/png" href="img/favicons/favicon-16x16.png?{$VERSION|rawurlencode}" sizes="16x16" />
|
||||
</head>
|
||||
<body role="document">
|
||||
<nav class="navbar navbar-inverse navbar-static-top">
|
||||
<div class="navbar-header">
|
||||
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
|
||||
<span class="sr-only">{function="t('Toggle navigation')"}</span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
</button>
|
||||
<a class="reloadlink navbar-brand" href="/">{function="t('ZeroBin')"}</a>
|
||||
</div>
|
||||
<div id="navbar" class="navbar-collapse collapse">
|
||||
<ul class="nav navbar-nav">
|
||||
<li>
|
||||
<button id="newbutton" type="button" class="reloadlink hidden btn btn-warning navbar-btn">
|
||||
<span class="glyphicon glyphicon-file" aria-hidden="true"></span> {function="t('New')"}
|
||||
</button>{if="$EXPIRECLONE"}
|
||||
<button id="clonebutton" type="button" class="hidden btn btn-warning navbar-btn">
|
||||
<span class="glyphicon glyphicon-duplicate" aria-hidden="true"></span> {function="t('Clone')"}
|
||||
</button>{/if}
|
||||
<button id="rawtextbutton" type="button" class="hidden btn btn-warning navbar-btn">
|
||||
<span class="glyphicon glyphicon-text-background" aria-hidden="true"></span> {function="t('Raw text')"}
|
||||
</button>
|
||||
</li>
|
||||
<li class="dropdown">
|
||||
<select id="pasteExpiration" name="pasteExpiration" class="hidden">
|
||||
{loop="EXPIRE"}
|
||||
<option value="{$key}"{if="$key == $EXPIREDEFAULT"} selected="selected"{/if}>{$value}</option>{/loop}
|
||||
</select>
|
||||
<a id="expiration" href="#" class="hidden dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">{function="t('Expires')"}: <span id="pasteExpirationDisplay">{$EXPIRE[$EXPIREDEFAULT]}</span> <span class="caret"></span></a>
|
||||
<ul class="dropdown-menu">
|
||||
{loop="EXPIRE"}
|
||||
<li>
|
||||
<a href="#" onclick="$('#pasteExpiration').val('{$key}');$('#pasteExpirationDisplay').text('{$value}');return false;">
|
||||
{$value}
|
||||
</a>
|
||||
</li>{/loop}
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<div id="burnafterreadingoption" class="navbar-text checkbox hidden">
|
||||
<label>
|
||||
<input type="checkbox" id="burnafterreading" name="burnafterreading" {if="$BURNAFTERREADINGSELECTED"} checked="checked"{/if} />
|
||||
{function="t('Burn after reading')"}
|
||||
</label>
|
||||
</div>
|
||||
</li>{if="$DISCUSSION"}
|
||||
<li>
|
||||
<div id="opendisc" class="navbar-text checkbox hidden">
|
||||
<label>
|
||||
<input type="checkbox" id="opendiscussion" name="opendiscussion" {if="$OPENDISCUSSION"} checked="checked"{/if} />
|
||||
{function="t('Open discussion')"}
|
||||
</label>
|
||||
</div>
|
||||
</li>{/if}{if="$PASSWORD"}
|
||||
<li>
|
||||
<div id="password" class="navbar-form hidden">
|
||||
<input type="password" id="passwordinput" placeholder="{function="t('Password (recommended)')"}" class="form-control" size="19"/>
|
||||
</div>
|
||||
</li>{/if}{if="$FILEUPLOAD"}
|
||||
<li id="attach" class="hidden dropdown">
|
||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">{function="t('Attach a file')"} <span class="caret"></span></a>
|
||||
<ul class="dropdown-menu">
|
||||
<li id="filewrap">
|
||||
<div>
|
||||
<input type="file" id="file" name="file" />
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<a id="fileremovebutton" href="#">
|
||||
{function="t('Remove attachment')"}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>{/if}
|
||||
<li class="dropdown">
|
||||
<select id="pasteFormatter" name="pasteFormatter" class="hidden">
|
||||
{loop="FORMATTER"}
|
||||
<option value="{$key}"{if="$key == $FORMATTERDEFAULT"} selected="selected"{/if}>{$value}</option>{/loop}
|
||||
</select>
|
||||
<a id="formatter" href="#" class="hidden dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">{function="t('Format')"}: <span id="pasteFormatterDisplay">{$FORMATTER[$FORMATTERDEFAULT]}</span> <span class="caret"></span></a>
|
||||
<ul class="dropdown-menu">
|
||||
{loop="FORMATTER"}
|
||||
<li>
|
||||
<a href="#" onclick="$('#pasteFormatter').val('{$key}');$('#pasteFormatterDisplay').text('{$value}');return false;">
|
||||
{$value}
|
||||
</a>
|
||||
</li>{/loop}
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
<ul class="nav navbar-nav pull-right">{if="strlen($LANGUAGESELECTION)"}
|
||||
<li id="language" class="dropdown">
|
||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false"><span class="glyphicon glyphicon-flag" aria-hidden="true"></span> {$LANGUAGES[$LANGUAGESELECTION][0]} <span class="caret"></span></a>
|
||||
<ul class="dropdown-menu">
|
||||
{loop="LANGUAGES"}
|
||||
<li>
|
||||
<a href="#" class="reloadlink" onclick="document.cookie='lang={$key}';">
|
||||
{$value[0]} ({$value[1]})
|
||||
</a>
|
||||
</li>{/loop}
|
||||
</ul>
|
||||
</li>{/if}
|
||||
<li>
|
||||
<button id="sendbutton" type="button" class="hidden btn btn-warning navbar-btn">
|
||||
<span class="glyphicon glyphicon-upload" aria-hidden="true"></span> {function="t('Send')"}
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
<header class="container">{if="strlen($NOTICE)"}
|
||||
<div role="alert" class="alert alert-info">
|
||||
<span class="glyphicon glyphicon-info-sign" aria-hidden="true"></span> {$NOTICE|htmlspecialchars}
|
||||
</div>{/if}
|
||||
<div id="remainingtime" role="alert" class="hidden alert alert-info">
|
||||
<span class="glyphicon glyphicon-info-sign" aria-hidden="true"></span>
|
||||
</div>{if="$FILEUPLOAD"}
|
||||
<div id="attachment" role="alert" class="hidden alert alert-info">
|
||||
<span class="glyphicon glyphicon-info-sign" aria-hidden="true"></span> <a>{function="t('Download attachment')"}</a> <span id="clonedfile" class="hidden">{function="t('Cloned file attached.')"}</span>
|
||||
</div>{/if}{if="strlen($STATUS)"}
|
||||
<div id="status" role="alert" class="alert alert-success">
|
||||
<span class="glyphicon glyphicon-ok" aria-hidden="true"></span> {$STATUS|htmlspecialchars}
|
||||
</div>{/if}
|
||||
<div id="errormessage" role="alert" class="{if="!strlen($ERROR)"}hidden {/if}alert alert-danger"><span class="glyphicon glyphicon-alert" aria-hidden="true"></span> {$ERROR|htmlspecialchars}</div>
|
||||
<div id="noscript" role="alert" class="nonworking alert alert-error"><span class="glyphicon glyphicon-exclamation-sign" aria-hidden="true"></span> {function="t('Javascript is required for ZeroBin to work.<br />Sorry for the inconvenience.')"}</div>
|
||||
<div id="oldienotice" role="alert" class="hidden nonworking alert alert-danger"><span class="glyphicon glyphicon-alert" aria-hidden="true"></span> {function="t('ZeroBin requires a modern browser to work.')"}</div>
|
||||
<div id="ienotice" role="alert" class="hidden alert alert-error"><span class="glyphicon glyphicon-question-sign" aria-hidden="true"></span> {function="t('Still using Internet Explorer? Do yourself a favor, switch to a modern browser:')"}
|
||||
<a href="http://www.mozilla.org/firefox/">Firefox</a>,
|
||||
<a href="http://www.opera.com/">Opera</a>,
|
||||
<a href="http://www.google.com/chrome">Chrome</a>,
|
||||
<a href="http://www.apple.com/safari">Safari</a>...
|
||||
</div>
|
||||
<div id="pasteresult" role="alert" class="hidden alert alert-success">
|
||||
<span class="glyphicon glyphicon-ok" aria-hidden="true"></span>
|
||||
<div id="deletelink"></div>
|
||||
<div id="pastelink"></div>
|
||||
</div>
|
||||
</header>
|
||||
<section class="container">
|
||||
<article class="row">
|
||||
<div id="image" class="col-md-12 text-center hidden"></div>
|
||||
<div id="prettymessage" class="col-md-12 hidden">
|
||||
<pre id="prettyprint" class="col-md-12 prettyprint linenums:1"></pre>
|
||||
</div>
|
||||
<div id="cleartext" class="col-md-12 hidden"></div>
|
||||
<p class="col-md-12"><textarea id="message" name="message" cols="80" rows="25" class="form-control hidden"></textarea></p>
|
||||
</article>
|
||||
</section>
|
||||
<section class="container">
|
||||
<div id="discussion" class="hidden">
|
||||
<h4>{function="t('Discussion')"}</h4>
|
||||
<div id="comments"></div>
|
||||
</div>
|
||||
</section>
|
||||
<footer class="container">
|
||||
<div class="row">
|
||||
<h4 class="col-md-5 col-xs-8">{function="t('ZeroBin')"} <small>- {function="t('Because ignorance is bliss')"}</small></h4>
|
||||
<p class="col-md-1 col-xs-4 text-center">{$VERSION}</p>
|
||||
<p id="aboutbox" class="col-md-6 col-xs-12">
|
||||
{function="t('ZeroBin is a minimalist, opensource online pastebin where the server has zero knowledge of pasted data. Data is encrypted/decrypted <i>in the browser</i> using 256 bits AES. More information on the <a href="https://github.com/elrido/ZeroBin/wiki">project page</a>.')"}
|
||||
</p>
|
||||
</div>
|
||||
</footer>
|
||||
<div id="cipherdata" class="hidden">{$CIPHERDATA}</div>
|
||||
</body>
|
||||
</html>
|
||||
198
tpl/bootstrap-page.html
Normal file
198
tpl/bootstrap-page.html
Normal file
@@ -0,0 +1,198 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="robots" content="noindex" />
|
||||
<title>{function="t('ZeroBin')"}</title>
|
||||
<link type="text/css" rel="stylesheet" href="css/bootstrap/bootstrap-3.3.5.css" />
|
||||
<link type="text/css" rel="stylesheet" href="css/bootstrap/bootstrap-theme-3.3.5.css" />
|
||||
<link type="text/css" rel="stylesheet" href="css/bootstrap/zerobin.css?{$VERSION|rawurlencode}" />{if="$SYNTAXHIGHLIGHTING"}
|
||||
<link type="text/css" rel="stylesheet" href="css/prettify/prettify.css?{$VERSION|rawurlencode}" />{if="strlen($SYNTAXHIGHLIGHTINGTHEME)"}
|
||||
<link type="text/css" rel="stylesheet" href="css/prettify/{$SYNTAXHIGHLIGHTINGTHEME}.css?{$VERSION|rawurlencode}" />{/if}{/if}
|
||||
<script type="text/javascript" src="js/jquery-1.11.3.js"></script>
|
||||
<script type="text/javascript" src="js/sjcl-1.0.2.js"></script>
|
||||
<script type="text/javascript" src="js/base64-{$BASE64JSVERSION}.js"></script>
|
||||
<script type="text/javascript" src="js/rawdeflate-0.5.js"></script>
|
||||
<script type="text/javascript" src="js/rawinflate-0.3.js"></script>
|
||||
<script type="text/javascript" src="js/bootstrap-3.3.5.js"></script>{if="$SYNTAXHIGHLIGHTING"}
|
||||
<script type="text/javascript" src="js/prettify.js?{$VERSION|rawurlencode}"></script>{/if}{if="$MARKDOWN"}
|
||||
<script type="text/javascript" src="js/showdown.js?{$VERSION|rawurlencode}"></script>{/if}
|
||||
<script type="text/javascript" src="js/zerobin.js?{$VERSION|rawurlencode}"></script>
|
||||
<!--[if lt IE 10]>
|
||||
<style type="text/css">#ienotice {display:block !important;} #oldienotice {display:block !important;}</style>
|
||||
<![endif]-->
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="img/favicons/apple-touch-icon.png?{$VERSION|rawurlencode}" />
|
||||
<link rel="icon" type="image/png" href="img/favicons/android-chrome-192x192.png?{$VERSION|rawurlencode}" sizes="192x192" />
|
||||
<link rel="icon" type="image/png" href="img/favicons/favicon-32x32.png?{$VERSION|rawurlencode}" sizes="32x32" />
|
||||
<link rel="icon" type="image/png" href="img/favicons/favicon-96x96.png?{$VERSION|rawurlencode}" sizes="96x96" />
|
||||
<link rel="icon" type="image/png" href="img/favicons/favicon-16x16.png?{$VERSION|rawurlencode}" sizes="16x16" />
|
||||
</head>
|
||||
<body role="document">
|
||||
<nav class="navbar navbar-default navbar-static-top">
|
||||
<div class="navbar-header">
|
||||
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
|
||||
<span class="sr-only">{function="t('Toggle navigation')"}</span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
</button>
|
||||
<a class="reloadlink navbar-brand" href="/">{function="t('ZeroBin')"}</a>
|
||||
</div>
|
||||
<div id="navbar" class="navbar-collapse collapse">
|
||||
<ul class="nav navbar-nav">
|
||||
<li>
|
||||
<button id="sendbutton" type="button" class="hidden btn btn-default navbar-btn">
|
||||
<span class="glyphicon glyphicon-upload" aria-hidden="true"></span> {function="t('Send')"}
|
||||
</button>{if="$EXPIRECLONE"}
|
||||
<button id="clonebutton" type="button" class="hidden btn btn-default navbar-btn">
|
||||
<span class="glyphicon glyphicon-duplicate" aria-hidden="true"></span> {function="t('Clone')"}
|
||||
</button>{/if}
|
||||
<button id="rawtextbutton" type="button" class="hidden btn btn-default navbar-btn">
|
||||
<span class="glyphicon glyphicon-text-background" aria-hidden="true"></span> {function="t('Raw text')"}
|
||||
</button>
|
||||
</li>
|
||||
<li class="dropdown">
|
||||
<select id="pasteExpiration" name="pasteExpiration" class="hidden">
|
||||
{loop="EXPIRE"}
|
||||
<option value="{$key}"{if="$key == $EXPIREDEFAULT"} selected="selected"{/if}>{$value}</option>{/loop}
|
||||
</select>
|
||||
<a id="expiration" href="#" class="hidden dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">{function="t('Expires')"}: <span id="pasteExpirationDisplay">{$EXPIRE[$EXPIREDEFAULT]}</span> <span class="caret"></span></a>
|
||||
<ul class="dropdown-menu">
|
||||
{loop="EXPIRE"}
|
||||
<li>
|
||||
<a href="#" onclick="$('#pasteExpiration').val('{$key}');$('#pasteExpirationDisplay').text('{$value}');return false;">
|
||||
{$value}
|
||||
</a>
|
||||
</li>{/loop}
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<div id="burnafterreadingoption" class="navbar-text checkbox hidden">
|
||||
<label>
|
||||
<input type="checkbox" id="burnafterreading" name="burnafterreading" {if="$BURNAFTERREADINGSELECTED"} checked="checked"{/if} />
|
||||
{function="t('Burn after reading')"}
|
||||
</label>
|
||||
</div>
|
||||
</li>{if="$DISCUSSION"}
|
||||
<li>
|
||||
<div id="opendisc" class="navbar-text checkbox hidden">
|
||||
<label>
|
||||
<input type="checkbox" id="opendiscussion" name="opendiscussion" {if="$OPENDISCUSSION"} checked="checked"{/if} />
|
||||
{function="t('Open discussion')"}
|
||||
</label>
|
||||
</div>
|
||||
</li>{/if}{if="$PASSWORD"}
|
||||
<li>
|
||||
<div id="password" class="navbar-form hidden">
|
||||
<input type="password" id="passwordinput" placeholder="{function="t('Password (recommended)')"}" class="form-control" size="19"/>
|
||||
</div>
|
||||
</li>{/if}{if="$FILEUPLOAD"}
|
||||
<li id="attach" class="hidden dropdown">
|
||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">{function="t('Attach a file')"} <span class="caret"></span></a>
|
||||
<ul class="dropdown-menu">
|
||||
<li id="filewrap">
|
||||
<div>
|
||||
<input type="file" id="file" name="file" />
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<a id="fileremovebutton" href="#">
|
||||
{function="t('Remove attachment')"}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>{/if}
|
||||
<li class="dropdown">
|
||||
<select id="pasteFormatter" name="pasteFormatter" class="hidden">
|
||||
{loop="FORMATTER"}
|
||||
<option value="{$key}"{if="$key == $FORMATTERDEFAULT"} selected="selected"{/if}>{$value}</option>{/loop}
|
||||
</select>
|
||||
<a id="formatter" href="#" class="hidden dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">{function="t('Format')"}: <span id="pasteFormatterDisplay">{$FORMATTER[$FORMATTERDEFAULT]}</span> <span class="caret"></span></a>
|
||||
<ul class="dropdown-menu">
|
||||
{loop="FORMATTER"}
|
||||
<li>
|
||||
<a href="#" onclick="$('#pasteFormatter').val('{$key}');$('#pasteFormatterDisplay').text('{$value}');return false;">
|
||||
{$value}
|
||||
</a>
|
||||
</li>{/loop}
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
<ul class="nav navbar-nav pull-right">{if="strlen($LANGUAGESELECTION)"}
|
||||
<li id="language" class="dropdown">
|
||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false"><span class="glyphicon glyphicon-flag" aria-hidden="true"></span> {$LANGUAGES[$LANGUAGESELECTION][0]} <span class="caret"></span></a>
|
||||
<ul class="dropdown-menu">
|
||||
{loop="LANGUAGES"}
|
||||
<li>
|
||||
<a href="#" class="reloadlink" onclick="document.cookie='lang={$key}';">
|
||||
{$value[0]} ({$value[1]})
|
||||
</a>
|
||||
</li>{/loop}
|
||||
</ul>
|
||||
</li>{/if}
|
||||
<li>
|
||||
<button id="newbutton" type="button" class="reloadlink hidden btn btn-default navbar-btn">
|
||||
<span class="glyphicon glyphicon-file" aria-hidden="true"></span> {function="t('New')"}
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
<header class="container">{if="strlen($NOTICE)"}
|
||||
<div role="alert" class="alert alert-info">
|
||||
<span class="glyphicon glyphicon-info-sign" aria-hidden="true"></span> {$NOTICE|htmlspecialchars}
|
||||
</div>{/if}
|
||||
<div id="remainingtime" role="alert" class="hidden alert alert-info">
|
||||
<span class="glyphicon glyphicon-info-sign" aria-hidden="true"></span>
|
||||
</div>{if="$FILEUPLOAD"}
|
||||
<div id="attachment" role="alert" class="hidden alert alert-info">
|
||||
<span class="glyphicon glyphicon-info-sign" aria-hidden="true"></span> <a>{function="t('Download attachment')"}</a> <span id="clonedfile" class="hidden">{function="t('Cloned file attached.')"}</span>
|
||||
</div>{/if}{if="strlen($STATUS)"}
|
||||
<div id="status" role="alert" class="alert alert-success">
|
||||
<span class="glyphicon glyphicon-ok" aria-hidden="true"></span> {$STATUS|htmlspecialchars}
|
||||
</div>{/if}
|
||||
<div id="errormessage" role="alert" class="{if="!strlen($ERROR)"}hidden {/if}alert alert-danger"><span class="glyphicon glyphicon-alert" aria-hidden="true"></span> {$ERROR|htmlspecialchars}</div>
|
||||
<div id="noscript" role="alert" class="nonworking alert alert-warning"><span class="glyphicon glyphicon-exclamation-sign" aria-hidden="true"></span> {function="t('Javascript is required for ZeroBin to work.<br />Sorry for the inconvenience.')"}</div>
|
||||
<div id="oldienotice" role="alert" class="hidden nonworking alert alert-danger"><span class="glyphicon glyphicon-alert" aria-hidden="true"></span> {function="t('ZeroBin requires a modern browser to work.')"}</div>
|
||||
<div id="ienotice" role="alert" class="hidden alert alert-warning"><span class="glyphicon glyphicon-question-sign" aria-hidden="true"></span> {function="t('Still using Internet Explorer? Do yourself a favor, switch to a modern browser:')"}
|
||||
<a href="http://www.mozilla.org/firefox/">Firefox</a>,
|
||||
<a href="http://www.opera.com/">Opera</a>,
|
||||
<a href="http://www.google.com/chrome">Chrome</a>,
|
||||
<a href="http://www.apple.com/safari">Safari</a>...
|
||||
</div>
|
||||
<div id="pasteresult" role="alert" class="hidden alert alert-success">
|
||||
<span class="glyphicon glyphicon-ok" aria-hidden="true"></span>
|
||||
<div id="deletelink"></div>
|
||||
<div id="pastelink"></div>
|
||||
</div>
|
||||
</header>
|
||||
<section class="container">
|
||||
<article class="row">
|
||||
<div id="image" class="col-md-12 text-center hidden"></div>
|
||||
<div id="prettymessage" class="col-md-12 hidden">
|
||||
<pre id="prettyprint" class="col-md-12 prettyprint linenums:1"></pre>
|
||||
</div>
|
||||
<div id="cleartext" class="col-md-12 hidden"></div>
|
||||
<p class="col-md-12"><textarea id="message" name="message" cols="80" rows="25" class="form-control hidden"></textarea></p>
|
||||
</article>
|
||||
</section>
|
||||
<section class="container">
|
||||
<div id="discussion" class="hidden">
|
||||
<h4>{function="t('Discussion')"}</h4>
|
||||
<div id="comments"></div>
|
||||
</div>
|
||||
</section>
|
||||
<footer class="container">
|
||||
<div class="row">
|
||||
<h4 class="col-md-5 col-xs-8">{function="t('ZeroBin')"} <small>- {function="t('Because ignorance is bliss')"}</small></h4>
|
||||
<p class="col-md-1 col-xs-4 text-center">{$VERSION}</p>
|
||||
<p id="aboutbox" class="col-md-6 col-xs-12">
|
||||
{function="t('ZeroBin is a minimalist, opensource online pastebin where the server has zero knowledge of pasted data. Data is encrypted/decrypted <i>in the browser</i> using 256 bits AES. More information on the <a href="https://github.com/elrido/ZeroBin/wiki">project page</a>.')"}
|
||||
</p>
|
||||
</div>
|
||||
</footer>
|
||||
<div id="cipherdata" class="hidden">{$CIPHERDATA}</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -5,7 +5,7 @@
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="robots" content="noindex" />
|
||||
<title>ZeroBin</title>
|
||||
<title>{function="t('ZeroBin')"}</title>
|
||||
<link type="text/css" rel="stylesheet" href="css/bootstrap/bootstrap-3.3.5.css" />
|
||||
<link type="text/css" rel="stylesheet" href="css/bootstrap/bootstrap-theme-3.3.5.css" />
|
||||
<link type="text/css" rel="stylesheet" href="css/bootstrap/zerobin.css?{$VERSION|rawurlencode}" />{if="$SYNTAXHIGHLIGHTING"}
|
||||
@@ -17,42 +17,40 @@
|
||||
<script type="text/javascript" src="js/rawdeflate-0.5.js"></script>
|
||||
<script type="text/javascript" src="js/rawinflate-0.3.js"></script>
|
||||
<script type="text/javascript" src="js/bootstrap-3.3.5.js"></script>{if="$SYNTAXHIGHLIGHTING"}
|
||||
<script type="text/javascript" src="js/prettify.js?{$VERSION|rawurlencode}"></script>{/if}
|
||||
<script type="text/javascript" src="js/prettify.js?{$VERSION|rawurlencode}"></script>{/if}{if="$MARKDOWN"}
|
||||
<script type="text/javascript" src="js/showdown.js?{$VERSION|rawurlencode}"></script>{/if}
|
||||
<script type="text/javascript" src="js/zerobin.js?{$VERSION|rawurlencode}"></script>
|
||||
<!--[if lt IE 10]>
|
||||
<style type="text/css">#ienotice {display:block !important;} #oldienotice {display:block !important;}</style>
|
||||
<![endif]-->
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="img/favicons/apple-touch-icon.png?{$VERSION|rawurlencode}" />
|
||||
<link rel="icon" type="image/png" href="img/favicons/android-chrome-192x192.png?{$VERSION|rawurlencode}" sizes="192x192" />
|
||||
<link rel="icon" type="image/png" href="img/favicons/favicon-32x32.png?{$VERSION|rawurlencode}" sizes="32x32" />
|
||||
<link rel="icon" type="image/png" href="img/favicons/favicon-96x96.png?{$VERSION|rawurlencode}" sizes="96x96" />
|
||||
<link rel="icon" type="image/png" href="img/favicons/favicon-16x16.png?{$VERSION|rawurlencode}" sizes="16x16" />
|
||||
</head>
|
||||
<body role="document">
|
||||
<nav class="navbar navbar-default navbar-fixed-top">
|
||||
<div class="container">
|
||||
<nav class="navbar navbar-default navbar-static-top">
|
||||
<div class="navbar-header">
|
||||
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
|
||||
<span class="sr-only">Toggle navigation</span>
|
||||
<span class="sr-only">{function="t('Toggle navigation')"}</span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
</button>
|
||||
<a class="navbar-brand" href="/" onclick="window.location.href=scriptLocation();return false;">ZeroBin</a>
|
||||
<a class="reloadlink navbar-brand" href="/">{function="t('ZeroBin')"}</a>
|
||||
</div>
|
||||
<div id="navbar" class="navbar-collapse collapse">
|
||||
<ul class="nav navbar-nav pull-right">
|
||||
<li>
|
||||
<button id="newbutton" type="button" class="hidden btn btn-default navbar-btn" onclick="window.location.href=scriptLocation();return false;">
|
||||
<span class="glyphicon glyphicon-file" aria-hidden="true"></span> New
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
<ul class="nav navbar-nav">
|
||||
<li class="pr">
|
||||
<button id="sendbutton" type="button" class="hidden btn btn-default navbar-btn" onclick="send_data();return false;">
|
||||
<span class="glyphicon glyphicon-upload" aria-hidden="true"></span> Send
|
||||
</button>
|
||||
<button id="clonebutton" type="button" class="hidden btn btn-default navbar-btn" onclick="clonePaste();return false;">
|
||||
<span class="glyphicon glyphicon-duplicate" aria-hidden="true"></span> Clone
|
||||
</button>
|
||||
<button id="rawtextbutton" type="button" class="hidden btn btn-default navbar-btn" onclick="rawText();return false;">
|
||||
<span class="glyphicon glyphicon-text-background" aria-hidden="true"></span> Raw text
|
||||
<li>
|
||||
<button id="newbutton" type="button" class="reloadlink hidden btn btn-default navbar-btn">
|
||||
<span class="glyphicon glyphicon-file" aria-hidden="true"></span> {function="t('New')"}
|
||||
</button>{if="$EXPIRECLONE"}
|
||||
<button id="clonebutton" type="button" class="hidden btn btn-default navbar-btn">
|
||||
<span class="glyphicon glyphicon-duplicate" aria-hidden="true"></span> {function="t('Clone')"}
|
||||
</button>{/if}
|
||||
<button id="rawtextbutton" type="button" class="hidden btn btn-default navbar-btn">
|
||||
<span class="glyphicon glyphicon-text-background" aria-hidden="true"></span> {function="t('Raw text')"}
|
||||
</button>
|
||||
</li>
|
||||
<li class="dropdown">
|
||||
@@ -60,17 +58,21 @@
|
||||
{loop="EXPIRE"}
|
||||
<option value="{$key}"{if="$key == $EXPIREDEFAULT"} selected="selected"{/if}>{$value}</option>{/loop}
|
||||
</select>
|
||||
<a id="expiration" href="#" class="hidden dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">Expires: <span id="pasteExpirationDisplay">{$EXPIREDEFAULT}</span> <span class="caret"></span></a>
|
||||
<a id="expiration" href="#" class="hidden dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">{function="t('Expires')"}: <span id="pasteExpirationDisplay">{$EXPIRE[$EXPIREDEFAULT]}</span> <span class="caret"></span></a>
|
||||
<ul class="dropdown-menu">
|
||||
{loop="EXPIRE"}
|
||||
<li><a href="#" onclick="$('#pasteExpiration').val('{$key}');$('#pasteExpirationDisplay').text('{$value}');return false;">{$value}</a></li>{/loop}
|
||||
<li>
|
||||
<a href="#" onclick="$('#pasteExpiration').val('{$key}');$('#pasteExpirationDisplay').text('{$value}');return false;">
|
||||
{$value}
|
||||
</a>
|
||||
</li>{/loop}
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<div id="burnafterreadingoption" class="navbar-text checkbox hidden">
|
||||
<label>
|
||||
<input type="checkbox" id="burnafterreading" name="burnafterreading" {if="$BURNAFTERREADINGSELECTED"} checked="checked"{/if} />
|
||||
Burn after reading
|
||||
{function="t('Burn after reading')"}
|
||||
</label>
|
||||
</div>
|
||||
</li>{if="$DISCUSSION"}
|
||||
@@ -78,31 +80,83 @@
|
||||
<div id="opendisc" class="navbar-text checkbox hidden">
|
||||
<label>
|
||||
<input type="checkbox" id="opendiscussion" name="opendiscussion" {if="$OPENDISCUSSION"} checked="checked"{/if} />
|
||||
Open discussion
|
||||
{function="t('Open discussion')"}
|
||||
</label>
|
||||
</div>
|
||||
</li>{/if}{if="$PASSWORD"}
|
||||
<li>
|
||||
<div id="password" class="navbar-form hidden">
|
||||
<input type="password" id="passwordinput" placeholder="Password (optional)" class="form-control" size="19"/>
|
||||
<input type="password" id="passwordinput" placeholder="{function="t('Password (recommended)')"}" class="form-control" size="19"/>
|
||||
</div>
|
||||
</li>{/if}{if="$FILEUPLOAD"}
|
||||
<li id="attach" class="hidden dropdown">
|
||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">{function="t('Attach a file')"} <span class="caret"></span></a>
|
||||
<ul class="dropdown-menu">
|
||||
<li id="filewrap">
|
||||
<div>
|
||||
<input type="file" id="file" name="file" />
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<a id="fileremovebutton" href="#">
|
||||
{function="t('Remove attachment')"}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>{/if}
|
||||
<li>
|
||||
<p id="remainingtime" class="hidden navbar-text"></p>
|
||||
<li class="dropdown">
|
||||
<select id="pasteFormatter" name="pasteFormatter" class="hidden">
|
||||
{loop="FORMATTER"}
|
||||
<option value="{$key}"{if="$key == $FORMATTERDEFAULT"} selected="selected"{/if}>{$value}</option>{/loop}
|
||||
</select>
|
||||
<a id="formatter" href="#" class="hidden dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">{function="t('Format')"}: <span id="pasteFormatterDisplay">{$FORMATTER[$FORMATTERDEFAULT]}</span> <span class="caret"></span></a>
|
||||
<ul class="dropdown-menu">
|
||||
{loop="FORMATTER"}
|
||||
<li>
|
||||
<a href="#" onclick="$('#pasteFormatter').val('{$key}');$('#pasteFormatterDisplay').text('{$value}');return false;">
|
||||
{$value}
|
||||
</a>
|
||||
</li>{/loop}
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<ul class="nav navbar-nav pull-right">{if="strlen($LANGUAGESELECTION)"}
|
||||
<li id="language" class="dropdown">
|
||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false"><span class="glyphicon glyphicon-flag" aria-hidden="true"></span> {$LANGUAGES[$LANGUAGESELECTION][0]} <span class="caret"></span></a>
|
||||
<ul class="dropdown-menu">
|
||||
{loop="LANGUAGES"}
|
||||
<li>
|
||||
<a href="#" class="reloadlink" onclick="document.cookie='lang={$key}';">
|
||||
{$value[0]} ({$value[1]})
|
||||
</a>
|
||||
</li>{/loop}
|
||||
</ul>
|
||||
</li>{/if}
|
||||
<li>
|
||||
<button id="sendbutton" type="button" class="hidden btn btn-primary navbar-btn">
|
||||
<span class="glyphicon glyphicon-upload" aria-hidden="true"></span> {function="t('Send')"}
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
<header class="container">{if="strlen($NOTICE)"}
|
||||
<div role="alert" class="alert alert-info">
|
||||
<span class="glyphicon glyphicon-info-sign" aria-hidden="true"></span> {$NOTICE|htmlspecialchars}
|
||||
</div>{/if}
|
||||
<div id="remainingtime" role="alert" class="hidden alert alert-info">
|
||||
<span class="glyphicon glyphicon-info-sign" aria-hidden="true"></span>
|
||||
</div>{if="$FILEUPLOAD"}
|
||||
<div id="attachment" role="alert" class="hidden alert alert-info">
|
||||
<span class="glyphicon glyphicon-info-sign" aria-hidden="true"></span> <a>{function="t('Download attachment')"}</a> <span id="clonedfile" class="hidden">{function="t('Cloned file attached.')"}</span>
|
||||
</div>{/if}{if="strlen($STATUS)"}
|
||||
<div id="status" role="alert" class="alert alert-success"><span class="glyphicon glyphicon-ok" aria-hidden="true"></span> {$STATUS|htmlspecialchars}</div>{/if}
|
||||
<div id="status" role="alert" class="alert alert-success">
|
||||
<span class="glyphicon glyphicon-ok" aria-hidden="true"></span> {$STATUS|htmlspecialchars}
|
||||
</div>{/if}
|
||||
<div id="errormessage" role="alert" class="{if="!strlen($ERROR)"}hidden {/if}alert alert-danger"><span class="glyphicon glyphicon-alert" aria-hidden="true"></span> {$ERROR|htmlspecialchars}</div>
|
||||
<div id="noscript" role="alert" class="nonworking alert alert-warning"><span class="glyphicon glyphicon-exclamation-sign" aria-hidden="true"></span> Javascript is required for ZeroBin to work.<br />Sorry for the inconvenience.</div>
|
||||
<div id="oldienotice" role="alert" class="hidden nonworking alert alert-danger"><span class="glyphicon glyphicon-alert" aria-hidden="true"></span> ZeroBin requires a modern browser to work.</div>
|
||||
<div id="ienotice" role="alert" class="hidden alert alert-warning"><span class="glyphicon glyphicon-question-sign" aria-hidden="true"></span> Still using Internet Explorer? Do yourself a favor, switch to a modern browser:
|
||||
<div id="noscript" role="alert" class="nonworking alert alert-warning"><span class="glyphicon glyphicon-exclamation-sign" aria-hidden="true"></span> {function="t('Javascript is required for ZeroBin to work.<br />Sorry for the inconvenience.')"}</div>
|
||||
<div id="oldienotice" role="alert" class="hidden nonworking alert alert-danger"><span class="glyphicon glyphicon-alert" aria-hidden="true"></span> {function="t('ZeroBin requires a modern browser to work.')"}</div>
|
||||
<div id="ienotice" role="alert" class="hidden alert alert-warning"><span class="glyphicon glyphicon-question-sign" aria-hidden="true"></span> {function="t('Still using Internet Explorer? Do yourself a favor, switch to a modern browser:')"}
|
||||
<a href="http://www.mozilla.org/firefox/">Firefox</a>,
|
||||
<a href="http://www.opera.com/">Opera</a>,
|
||||
<a href="http://www.google.com/chrome">Chrome</a>,
|
||||
@@ -116,6 +170,7 @@
|
||||
</header>
|
||||
<section class="container">
|
||||
<article class="row">
|
||||
<div id="image" class="col-md-12 text-center hidden"></div>
|
||||
<div id="prettymessage" class="col-md-12 hidden">
|
||||
<pre id="prettyprint" class="col-md-12 prettyprint linenums:1"></pre>
|
||||
</div>
|
||||
@@ -125,18 +180,16 @@
|
||||
</section>
|
||||
<section class="container">
|
||||
<div id="discussion" class="hidden">
|
||||
<h4>Discussion</h4>
|
||||
<h4>{function="t('Discussion')"}</h4>
|
||||
<div id="comments"></div>
|
||||
</div>
|
||||
</section>
|
||||
<footer class="container">
|
||||
<div class="row">
|
||||
<h4 class="col-md-3 col-xs-8">ZeroBin <small>- Because ignorance is bliss</small></h4>
|
||||
<h4 class="col-md-5 col-xs-8">{function="t('ZeroBin')"} <small>- {function="t('Because ignorance is bliss')"}</small></h4>
|
||||
<p class="col-md-1 col-xs-4 text-center">{$VERSION}</p>
|
||||
<p id="aboutbox" class="col-md-8 col-xs-12">
|
||||
ZeroBin is a minimalist, opensource online pastebin where the server has zero knowledge of pasted data.<br />
|
||||
Data is encrypted/decrypted <em>in the browser</em> using 256 bits AES.<br />
|
||||
More information on the <a href="http://sebsauvage.net/wiki/doku.php?id=php:zerobin" target="_blank">project page</a>.
|
||||
<p id="aboutbox" class="col-md-6 col-xs-12">
|
||||
{function="t('ZeroBin is a minimalist, opensource online pastebin where the server has zero knowledge of pasted data. Data is encrypted/decrypted <i>in the browser</i> using 256 bits AES. More information on the <a href="https://github.com/elrido/ZeroBin/wiki">project page</a>.')"}
|
||||
</p>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="robots" content="noindex" />
|
||||
<title>ZeroBin</title>
|
||||
<title>{function="t('ZeroBin')"}</title>
|
||||
<link type="text/css" rel="stylesheet" href="css/zerobin.css?{$VERSION|rawurlencode}" />{if="$SYNTAXHIGHLIGHTING"}
|
||||
<link type="text/css" rel="stylesheet" href="css/prettify/prettify.css?{$VERSION|rawurlencode}" />{if="strlen($SYNTAXHIGHLIGHTINGTHEME)"}
|
||||
<link type="text/css" rel="stylesheet" href="css/prettify/{$SYNTAXHIGHLIGHTINGTHEME}.css?{$VERSION|rawurlencode}" />{/if}{/if}
|
||||
@@ -12,26 +12,30 @@
|
||||
<script type="text/javascript" src="js/base64-{$BASE64JSVERSION}.js"></script>
|
||||
<script type="text/javascript" src="js/rawdeflate-0.5.js"></script>
|
||||
<script type="text/javascript" src="js/rawinflate-0.3.js"></script>{if="$SYNTAXHIGHLIGHTING"}
|
||||
<script type="text/javascript" src="js/prettify.js?{$VERSION|rawurlencode}"></script>{/if}
|
||||
<script type="text/javascript" src="js/prettify.js?{$VERSION|rawurlencode}"></script>{/if}{if="$MARKDOWN"}
|
||||
<script type="text/javascript" src="js/showdown.js?{$VERSION|rawurlencode}"></script>{/if}
|
||||
<script type="text/javascript" src="js/zerobin.js?{$VERSION|rawurlencode}"></script>
|
||||
<!--[if lt IE 10]>
|
||||
<style type="text/css">body {padding-left:60px;padding-right:60px;} #ienotice {display:block;} #oldienotice {display:block;}</style>
|
||||
<![endif]-->
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="img/favicons/apple-touch-icon.png?{$VERSION|rawurlencode}" />
|
||||
<link rel="icon" type="image/png" href="img/favicons/android-chrome-192x192.png?{$VERSION|rawurlencode}" sizes="192x192" />
|
||||
<link rel="icon" type="image/png" href="img/favicons/favicon-32x32.png?{$VERSION|rawurlencode}" sizes="32x32" />
|
||||
<link rel="icon" type="image/png" href="img/favicons/favicon-96x96.png?{$VERSION|rawurlencode}" sizes="96x96" />
|
||||
<link rel="icon" type="image/png" href="img/favicons/favicon-16x16.png?{$VERSION|rawurlencode}" sizes="16x16" />
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<div id="aboutbox">
|
||||
ZeroBin is a minimalist, opensource online pastebin where the server has zero knowledge of pasted data.
|
||||
Data is encrypted/decrypted <i>in the browser</i> using 256 bits AES.
|
||||
More information on the <a href="http://sebsauvage.net/wiki/doku.php?id=php:zerobin">project page</a>.<br />{if="strlen($NOTICE)"}
|
||||
{function="t('ZeroBin is a minimalist, opensource online pastebin where the server has zero knowledge of pasted data. Data is encrypted/decrypted <i>in the browser</i> using 256 bits AES. More information on the <a href="https://github.com/elrido/ZeroBin/wiki">project page</a>.')"}<br />{if="strlen($NOTICE)"}
|
||||
<span class="blink">▶</span> {$NOTICE}{/if}
|
||||
</div>
|
||||
<h1 title="ZeroBin" onclick="window.location.href=scriptLocation();return false;">ZeroBin</h1><br />
|
||||
<h2>Because ignorance is bliss</h2><br />
|
||||
<h3>{$VERSION}</h3>
|
||||
<div id="noscript" class="nonworking">Javascript is required for ZeroBin to work.<br />Sorry for the inconvenience.</div>
|
||||
<div id="oldienotice" class="nonworking">ZeroBin requires a modern browser to work.</div>
|
||||
<div id="ienotice">Still using Internet Explorer ? Do yourself a favor, switch to a modern browser:
|
||||
<h1 class="title reloadlink">{function="t('ZeroBin')"}</h1><br />
|
||||
<h2 class="title">{function="t('Because ignorance is bliss')"}</h2><br />
|
||||
<h3 class="title">{$VERSION}</h3>
|
||||
<div id="noscript" class="nonworking">{function="t('Javascript is required for ZeroBin to work.<br />Sorry for the inconvenience.')"}</div>
|
||||
<div id="oldienotice" class="nonworking">{function="t('ZeroBin requires a modern browser to work.')"}</div>
|
||||
<div id="ienotice">{function="t('Still using Internet Explorer? Do yourself a favor, switch to a modern browser:')"}
|
||||
<a href="http://www.mozilla.org/firefox/">Firefox</a>,
|
||||
<a href="http://www.opera.com/">Opera</a>,
|
||||
<a href="http://www.google.com/chrome">Chrome</a>,
|
||||
@@ -43,11 +47,11 @@
|
||||
<div id="status">{$STATUS|htmlspecialchars}</div>
|
||||
<div id="errormessage" class="hidden">{$ERROR|htmlspecialchars}</div>
|
||||
<div id="toolbar">
|
||||
<button id="newbutton" onclick="window.location.href=scriptLocation();return false;" class="hidden"><img src="img/icon_new.png" width="11" height="15" alt="" />New</button>
|
||||
<button id="sendbutton" onclick="send_data();return false;" class="hidden"><img src="img/icon_send.png" width="18" height="15" alt="" />Send</button>
|
||||
<button id="clonebutton" onclick="clonePaste();return false;" class="hidden"><img src="img/icon_clone.png" width="15" height="17" alt="" />Clone</button>
|
||||
<button id="rawtextbutton" onclick="rawText();return false;" class="hidden"><img src="img/icon_raw.png" width="15" height="15" alt="" />Raw text</button>
|
||||
<div id="expiration" class="hidden">Expires:
|
||||
<button id="newbutton" class="reloadlink hidden"><img src="img/icon_new.png" width="11" height="15" alt="" />{function="t('New')"}</button>
|
||||
<button id="sendbutton" class="hidden"><img src="img/icon_send.png" width="18" height="15" alt="" />{function="t('Send')"}</button>{if="$EXPIRECLONE"}
|
||||
<button id="clonebutton" class="hidden"><img src="img/icon_clone.png" width="15" height="17" alt="" />{function="t('Clone')"}</button>{/if}
|
||||
<button id="rawtextbutton" class="hidden"><img src="img/icon_raw.png" width="15" height="15" alt="" />{function="t('Raw text')"}</button>
|
||||
<div id="expiration" class="hidden button">{function="t('Expires')"}:
|
||||
<select id="pasteExpiration" name="pasteExpiration">
|
||||
{loop="EXPIRE"}
|
||||
<option value="{$key}"{if="$key == $EXPIREDEFAULT"} selected="selected"{/if}>{$value}</option>{/loop}
|
||||
@@ -56,20 +60,39 @@
|
||||
<div id="remainingtime" class="hidden"></div>
|
||||
<div id="burnafterreadingoption" class="button hidden">
|
||||
<input type="checkbox" id="burnafterreading" name="burnafterreading" {if="$BURNAFTERREADINGSELECTED"} checked="checked"{/if} />
|
||||
<label for="burnafterreading">Burn after reading</label>
|
||||
<label for="burnafterreading">{function="t('Burn after reading')"}</label>
|
||||
</div>{if="$DISCUSSION"}
|
||||
<div id="opendisc" class="button hidden">
|
||||
<input type="checkbox" id="opendiscussion" name="opendiscussion" {if="$OPENDISCUSSION"} checked="checked"{/if} />
|
||||
<label for="opendiscussion" {if="!$OPENDISCUSSION"} style="color: #BBBBBB;"{/if}>Open discussion</label>
|
||||
<label for="opendiscussion" {if="!$OPENDISCUSSION"} style="color: #BBBBBB;"{/if}>{function="t('Open discussion')"}</label>
|
||||
</div>{/if}{if="$PASSWORD"}
|
||||
<div id="password" class="hidden">
|
||||
<input id="passwordinput" placeholder="Optional password (recommended)" size="32" />
|
||||
<input type="password" id="passwordinput" placeholder="{function="t('Password (recommended)')"}" size="32" />
|
||||
</div>{/if}
|
||||
<div id="formatter" class="button hidden">{function="t('Format')"}:
|
||||
<select id="pasteFormatter" name="pasteFormatter">
|
||||
{loop="FORMATTER"}
|
||||
<option value="{$key}"{if="$key == $FORMATTERDEFAULT"} selected="selected"{/if}>{$value}</option>{/loop}
|
||||
</select>
|
||||
</div>{if="strlen($LANGUAGESELECTION)"}
|
||||
<div id="language" class="button">
|
||||
<select name="lang">
|
||||
{loop="LANGUAGES"}
|
||||
<option class="reloadlink" onclick="document.cookie='lang={$key}';" value="{$key}"{if="$key == $LANGUAGESELECTION"} selected="selected"{/if}>{$value[0]} ({$value[1]})</option>{/loop}
|
||||
</select>
|
||||
</div>{/if}
|
||||
</div>
|
||||
<div id="pasteresult" class="hidden">
|
||||
<div id="deletelink"></div>
|
||||
<div id="pastelink"></div>
|
||||
</div>
|
||||
</div>{if="$FILEUPLOAD"}
|
||||
<div id="attachment" class="hidden"><a>{function="t('Download attachment')"}</a></div>
|
||||
<div id="attach" class="hidden">
|
||||
<span id="clonedfile" class="hidden">{function="t('Cloned file attached.')"}</span>
|
||||
<span id="filewrap">{function="t('Attach a file')"}: <input type="file" id="file" name="file" /></span>
|
||||
<button id="fileremovebutton">{function="t('Remove attachment')"}</button>
|
||||
</div>{/if}
|
||||
<div id="image" class="hidden"></div>
|
||||
<div id="prettymessage" class="hidden">
|
||||
<pre id="prettyprint" class="prettyprint linenums:1"></pre>
|
||||
</div>
|
||||
@@ -79,7 +102,7 @@
|
||||
</section>
|
||||
<section>
|
||||
<div id="discussion" class="hidden">
|
||||
<h4>Discussion</h4>
|
||||
<h4 class="title">{function="t('Discussion')"}</h4>
|
||||
<div id="comments"></div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
2
tst/.gitignore
vendored
2
tst/.gitignore
vendored
@@ -1 +1 @@
|
||||
/configuration.php
|
||||
/configurationCombinations.php
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
<?php
|
||||
class RainTPLTest extends PHPUnit_Framework_TestCase
|
||||
{
|
||||
private static $data = '{"iv":"EN39/wd5Nk8HAiSG2K5AsQ","salt":"QKN1DBXe5PI","ct":"8hA83xDdXjD7K2qfmw5NdA"}';
|
||||
|
||||
private static $error = 'foo bar';
|
||||
|
||||
private static $status = '!*#@?$+';
|
||||
@@ -27,20 +25,25 @@ class RainTPLTest extends PHPUnit_Framework_TestCase
|
||||
$page::$path_replace = false;
|
||||
|
||||
// We escape it here because ENT_NOQUOTES can't be used in RainTPL templates.
|
||||
$page->assign('CIPHERDATA', htmlspecialchars(self::$data, ENT_NOQUOTES));
|
||||
$page->assign('CIPHERDATA', htmlspecialchars(helper::getPaste()['data'], ENT_NOQUOTES));
|
||||
$page->assign('ERROR', self::$error);
|
||||
$page->assign('STATUS', self::$status);
|
||||
$page->assign('VERSION', self::$version);
|
||||
$page->assign('DISCUSSION', true);
|
||||
$page->assign('OPENDISCUSSION', true);
|
||||
$page->assign('MARKDOWN', true);
|
||||
$page->assign('SYNTAXHIGHLIGHTING', true);
|
||||
$page->assign('SYNTAXHIGHLIGHTINGTHEME', 'sons-of-obsidian');
|
||||
$page->assign('BURNAFTERREADINGSELECTED', false);
|
||||
$page->assign('PASSWORD', true);
|
||||
$page->assign('FILEUPLOAD', false);
|
||||
$page->assign('BASE64JSVERSION', '2.1.9');
|
||||
$page->assign('NOTICE', 'example');
|
||||
$page->assign('LANGUAGESELECTION', '');
|
||||
$page->assign('LANGUAGES', i18n::getLanguageLabels(i18n::getAvailableLanguages()));
|
||||
$page->assign('EXPIRE', self::$expire);
|
||||
$page->assign('EXPIREDEFAULT', self::$expire_default);
|
||||
$page->assign('EXPIRECLONE', true);
|
||||
ob_start();
|
||||
$page->draw('page');
|
||||
$this->_content = ob_get_contents();
|
||||
@@ -61,7 +64,7 @@ class RainTPLTest extends PHPUnit_Framework_TestCase
|
||||
$this->assertTag(
|
||||
array(
|
||||
'id' => 'cipherdata',
|
||||
'content' => htmlspecialchars(self::$data, ENT_NOQUOTES)
|
||||
'content' => htmlspecialchars(helper::getPaste()['data'], ENT_NOQUOTES)
|
||||
),
|
||||
$this->_content,
|
||||
'outputs data correctly'
|
||||
|
||||
@@ -3,11 +3,143 @@ error_reporting( E_ALL | E_STRICT );
|
||||
|
||||
// change this, if your php files and data is outside of your webservers document root
|
||||
if (!defined('PATH')) define('PATH', '..' . DIRECTORY_SEPARATOR);
|
||||
if (!defined('CONF')) define('CONF', PATH . 'cfg' . DIRECTORY_SEPARATOR . 'conf.ini');
|
||||
if (!defined('PUBLIC_PATH')) define('PUBLIC_PATH', '..');
|
||||
|
||||
require PATH . 'lib/auto.php';
|
||||
|
||||
class helper
|
||||
{
|
||||
/**
|
||||
* example ID of a paste
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private static $pasteid = '5e9bc25c89fb3bf9';
|
||||
|
||||
/**
|
||||
* example paste
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private static $paste = array(
|
||||
'data' => '{"iv":"EN39/wd5Nk8HAiSG2K5AsQ","v":1,"iter":1000,"ks":128,"ts":64,"mode":"ccm","adata":"","cipher":"aes","salt":"QKN1DBXe5PI","ct":"8hA83xDdXjD7K2qfmw5NdA"}',
|
||||
'attachment' => '{"iv":"Pd4pOKWkmDTT9uPwVwd5Ag","v":1,"iter":1000,"ks":128,"ts":64,"mode":"ccm","adata":"","cipher":"aes","salt":"ZIUhFTliVz4","ct":"6nOCU3peNDclDDpFtJEBKA"}',
|
||||
'attachmentname' => '{"iv":"76MkAtOGC4oFogX/aSMxRA","v":1,"iter":1000,"ks":128,"ts":64,"mode":"ccm","adata":"","cipher":"aes","salt":"ZIUhFTliVz4","ct":"b6Ae/U1xJdsX/+lATud4sQ"}',
|
||||
'meta' => array(
|
||||
'formatter' => 'plaintext',
|
||||
'postdate' => 1344803344,
|
||||
'opendiscussion' => true,
|
||||
),
|
||||
);
|
||||
|
||||
/**
|
||||
* example ID of a comment
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private static $commentid = '5a52eebf11c4c94b';
|
||||
|
||||
/**
|
||||
* example comment
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private static $comment = array(
|
||||
'data' => '{"iv":"Pd4pOKWkmDTT9uPwVwd5Ag","v":1,"iter":1000,"ks":128,"ts":64,"mode":"ccm","adata":"","cipher":"aes","salt":"ZIUhFTliVz4","ct":"6nOCU3peNDclDDpFtJEBKA"}',
|
||||
'meta' => array(
|
||||
'nickname' => '{"iv":"76MkAtOGC4oFogX/aSMxRA","v":1,"iter":1000,"ks":128,"ts":64,"mode":"ccm","adata":"","cipher":"aes","salt":"ZIUhFTliVz4","ct":"b6Ae/U1xJdsX/+lATud4sQ"}',
|
||||
'vizhash' => 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAABGUlEQVQokWOsl5/94983CNKQMjnxaOePf98MeKwPfNjkLZ3AgARab6b9+PeNEVnDj3/ff/z7ZiHnzsDA8Pv7H2TVPJw8EAYLAwb48OaVgIgYKycLsrYv378wMDB8//qdCVMDRA9EKSsnCwRBxNsepaLboMFlyMDAICAi9uHNK24GITQ/MDAwoNhgIGMLtwGrzegaLjw5jMz9+vUdnN17uwDCQDhJgk0O07yvX9+teDX1x79v6DYIsIjgcgMaYGFgYOBg4kJx2JejkAiBxAw+PzAwMNz4dp6wDXDw4MdNNOl0rWYsNkD89OLXI/xmo9sgzatJjAYmBgYGDiauD3/ePP18nVgb4MF89+M5ZX6js293wUMpnr8KTQMAxsCJnJ30apMAAAAASUVORK5CYII=',
|
||||
'postdate' => 1344803528,
|
||||
),
|
||||
);
|
||||
|
||||
/**
|
||||
* get example paste ID
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function getPasteId()
|
||||
{
|
||||
return self::$pasteid;
|
||||
}
|
||||
|
||||
/**
|
||||
* get example paste
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function getPaste($meta = array())
|
||||
{
|
||||
$example = self::getPasteWithAttachment($meta);
|
||||
unset($example['attachment'], $example['attachmentname']);
|
||||
return $example;
|
||||
}
|
||||
|
||||
/**
|
||||
* get example paste
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function getPasteWithAttachment($meta = array())
|
||||
{
|
||||
$example = self::$paste;
|
||||
$example['meta'] = array_merge($example['meta'], $meta);
|
||||
return $example;
|
||||
}
|
||||
|
||||
/**
|
||||
* get example paste
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function getPasteAsJson($meta = array())
|
||||
{
|
||||
$example = self::getPaste();
|
||||
if (count($meta))
|
||||
$example['meta'] = $meta;
|
||||
$example['comments'] = array();
|
||||
$example['comment_count'] = 0;
|
||||
$example['comment_offset'] = 0;
|
||||
$example['@context'] = 'js/paste.jsonld';
|
||||
return json_encode($example);
|
||||
}
|
||||
|
||||
/**
|
||||
* get example paste ID
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function getCommentId()
|
||||
{
|
||||
return self::$commentid;
|
||||
}
|
||||
|
||||
/**
|
||||
* get example comment
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function getComment($meta = array())
|
||||
{
|
||||
$example = self::$comment;
|
||||
$example['meta'] = array_merge($example['meta'], $meta);
|
||||
return $example;
|
||||
}
|
||||
|
||||
/**
|
||||
* get example comment
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function getCommentPost($meta = array())
|
||||
{
|
||||
$example = self::getComment($meta);
|
||||
$example['nickname'] = $example['meta']['nickname'];
|
||||
unset($example['meta']['nickname']);
|
||||
return $example;
|
||||
}
|
||||
|
||||
/**
|
||||
* delete directory and all its contents recursively
|
||||
*
|
||||
@@ -35,6 +167,28 @@ class helper
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* create a backup of the config file
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function confBackup()
|
||||
{
|
||||
if (!is_file(CONF . '.bak') && is_file(CONF))
|
||||
rename(CONF, CONF . '.bak');
|
||||
}
|
||||
|
||||
/**
|
||||
* restor backup of the config file
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function confRestore()
|
||||
{
|
||||
if (is_file(CONF . '.bak'))
|
||||
rename(CONF . '.bak', CONF);
|
||||
}
|
||||
|
||||
/**
|
||||
* create ini file
|
||||
*
|
||||
@@ -53,6 +207,18 @@ class helper
|
||||
continue;
|
||||
} elseif (is_string($setting)) {
|
||||
$setting = '"' . $setting . '"';
|
||||
} elseif (is_array($setting)) {
|
||||
foreach ($setting as $key => $value) {
|
||||
if (is_null($value)) {
|
||||
$value = 'null';
|
||||
} elseif (is_string($value)) {
|
||||
$value = '"' . $value . '"';
|
||||
} else {
|
||||
$value = var_export($value, true);
|
||||
}
|
||||
fwrite($ini, $option . "[$key] = $value" . PHP_EOL);
|
||||
}
|
||||
continue;
|
||||
} else {
|
||||
$setting = var_export($setting, true);
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@ new configurationTestGenerator(array(
|
||||
'settings' => array('$_POST["opendiscussion"] = "neither 1 nor 0"'),
|
||||
'type' => 'False',
|
||||
'args' => array(
|
||||
'$this->_model->exists(self::$pasteid)',
|
||||
'$this->_model->exists(helper::getPasteId())',
|
||||
'when discussions are enabled, but invalid flag posted, paste is not created'
|
||||
),
|
||||
),
|
||||
@@ -108,134 +108,6 @@ new configurationTestGenerator(array(
|
||||
'affects' => $vrd
|
||||
),
|
||||
),
|
||||
'main/syntaxhighlighting' => array(
|
||||
array(
|
||||
'setting' => true,
|
||||
'tests' => array(
|
||||
array(
|
||||
'type' => 'Tag',
|
||||
'args' => array(
|
||||
array(
|
||||
'tag' => 'link',
|
||||
'attributes' => array(
|
||||
'type' => 'text/css',
|
||||
'rel' => 'stylesheet',
|
||||
'href' => 'regexp:#css/prettify/prettify\.css#',
|
||||
),
|
||||
),
|
||||
'$content',
|
||||
'outputs prettify stylesheet correctly',
|
||||
),
|
||||
), array(
|
||||
'type' => 'Tag',
|
||||
'args' => array(
|
||||
array(
|
||||
'tag' => 'script',
|
||||
'attributes' => array(
|
||||
'type' => 'text/javascript',
|
||||
'src' => 'regexp:#js/prettify\.js#'
|
||||
),
|
||||
),
|
||||
'$content',
|
||||
'outputs prettify javascript correctly',
|
||||
),
|
||||
),
|
||||
),
|
||||
'affects' => $vrd,
|
||||
), array(
|
||||
'setting' => false,
|
||||
'tests' => array(
|
||||
array(
|
||||
'type' => 'NotTag',
|
||||
'args' => array(
|
||||
array(
|
||||
'tag' => 'link',
|
||||
'attributes' => array(
|
||||
'type' => 'text/css',
|
||||
'rel' => 'stylesheet',
|
||||
'href' => 'regexp:#css/prettify/prettify\.css#',
|
||||
),
|
||||
),
|
||||
'$content',
|
||||
'removes prettify stylesheet correctly',
|
||||
),
|
||||
), array(
|
||||
'type' => 'NotTag',
|
||||
'args' => array(
|
||||
array(
|
||||
'tag' => 'script',
|
||||
'attributes' => array(
|
||||
'type' => 'text/javascript',
|
||||
'src' => 'regexp:#js/prettify\.js#',
|
||||
),
|
||||
),
|
||||
'$content',
|
||||
'removes prettify javascript correctly',
|
||||
),
|
||||
),
|
||||
),
|
||||
'affects' => $vrd,
|
||||
),
|
||||
),
|
||||
'main/syntaxhighlightingtheme' => array(
|
||||
array(
|
||||
'setting' => 'sons-of-obsidian',
|
||||
'tests' => array(
|
||||
array(
|
||||
'conditions' => array('main/syntaxhighlighting' => true),
|
||||
'type' => 'Tag',
|
||||
'args' => array(
|
||||
array(
|
||||
'tag' => 'link',
|
||||
'attributes' => array(
|
||||
'type' => 'text/css',
|
||||
'rel' => 'stylesheet',
|
||||
'href' => 'regexp:#css/prettify/sons-of-obsidian\.css#',
|
||||
),
|
||||
),
|
||||
'$content',
|
||||
'outputs prettify theme stylesheet correctly',
|
||||
),
|
||||
), array(
|
||||
'conditions' => array('main/syntaxhighlighting' => false),
|
||||
'type' => 'NotTag',
|
||||
'args' => array(
|
||||
array(
|
||||
'tag' => 'link',
|
||||
'attributes' => array(
|
||||
'type' => 'text/css',
|
||||
'rel' => 'stylesheet',
|
||||
'href' => 'regexp:#css/prettify/sons-of-obsidian\.css#',
|
||||
),
|
||||
),
|
||||
'$content',
|
||||
'removes prettify theme stylesheet correctly',
|
||||
),
|
||||
),
|
||||
),
|
||||
'affects' => $vrd,
|
||||
), array(
|
||||
'setting' => null, // option not set
|
||||
'tests' => array(
|
||||
array(
|
||||
'type' => 'NotTag',
|
||||
'args' => array(
|
||||
array(
|
||||
'tag' => 'link',
|
||||
'attributes' => array(
|
||||
'type' => 'text/css',
|
||||
'rel' => 'stylesheet',
|
||||
'href' => 'regexp:#css/prettify/sons-of-obsidian\.css#',
|
||||
),
|
||||
),
|
||||
'$content',
|
||||
'removes prettify theme stylesheet correctly',
|
||||
),
|
||||
),
|
||||
),
|
||||
'affects' => $vrd,
|
||||
),
|
||||
),
|
||||
'main/burnafterreadingselected' => array(
|
||||
array(
|
||||
'setting' => true,
|
||||
@@ -528,7 +400,7 @@ class configurationTestGenerator
|
||||
*/
|
||||
private function _writeConfigurationTest()
|
||||
{
|
||||
$defaultOptions = parse_ini_file(PATH . 'cfg' . DIRECTORY_SEPARATOR . 'conf.ini', true);
|
||||
$defaultOptions = parse_ini_file(CONF, true);
|
||||
$code = $this->_getHeader();
|
||||
foreach ($this->_configurations as $key => $conf) {
|
||||
$fullOptions = array_replace_recursive($defaultOptions, $conf['options']);
|
||||
@@ -576,7 +448,7 @@ class configurationTestGenerator
|
||||
}
|
||||
}
|
||||
$code .= '}' . PHP_EOL;
|
||||
file_put_contents('configuration.php', $code);
|
||||
file_put_contents('configurationCombinations.php', $code);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -589,20 +461,10 @@ class configurationTestGenerator
|
||||
return <<<'EOT'
|
||||
<?php
|
||||
/**
|
||||
* DO NOT EDIT: This file is automatically generated by configGenerator.php
|
||||
* DO NOT EDIT: This file is generated automatically using configGenerator.php
|
||||
*/
|
||||
class configurationTest extends PHPUnit_Framework_TestCase
|
||||
class configurationCombinationsTest extends PHPUnit_Framework_TestCase
|
||||
{
|
||||
private static $pasteid = '5e9bc25c89fb3bf9';
|
||||
|
||||
private static $paste = array(
|
||||
'data' => '{"iv":"EN39/wd5Nk8HAiSG2K5AsQ","v":1,"iter":1000,"ks":128,"ts":64,"mode":"ccm","adata":"","cipher":"aes","salt":"QKN1DBXe5PI","ct":"8hA83xDdXjD7K2qfmw5NdA"}',
|
||||
'meta' => array(
|
||||
'postdate' => 1344803344,
|
||||
'opendiscussion' => true,
|
||||
),
|
||||
);
|
||||
|
||||
private $_model;
|
||||
|
||||
private $_conf;
|
||||
@@ -610,9 +472,7 @@ class configurationTest extends PHPUnit_Framework_TestCase
|
||||
public function setUp()
|
||||
{
|
||||
/* Setup Routine */
|
||||
$this->_conf = PATH . 'cfg' . DIRECTORY_SEPARATOR . 'conf.ini';
|
||||
if (!is_file($this->_conf . '.bak') && is_file($this->_conf))
|
||||
rename($this->_conf, $this->_conf . '.bak');
|
||||
helper::confBackup();
|
||||
|
||||
$this->_model = zerobin_data::getInstance(array('dir' => PATH . 'data'));
|
||||
serversalt::setPath(PATH . 'data');
|
||||
@@ -622,7 +482,7 @@ class configurationTest extends PHPUnit_Framework_TestCase
|
||||
public function tearDown()
|
||||
{
|
||||
/* Tear Down Routine */
|
||||
rename($this->_conf . '.bak', $this->_conf);
|
||||
helper::confRestore();
|
||||
}
|
||||
|
||||
public function reset($configuration = array())
|
||||
@@ -630,9 +490,9 @@ class configurationTest extends PHPUnit_Framework_TestCase
|
||||
$_POST = array();
|
||||
$_GET = array();
|
||||
$_SERVER = array();
|
||||
if ($this->_model->exists(self::$pasteid))
|
||||
$this->_model->delete(self::$pasteid);
|
||||
helper::createIniFile($this->_conf, $configuration);
|
||||
if ($this->_model->exists(helper::getPasteId()))
|
||||
$this->_model->delete(helper::getPasteId());
|
||||
helper::createIniFile(CONF, $configuration);
|
||||
}
|
||||
|
||||
|
||||
@@ -678,22 +538,24 @@ EOT;
|
||||
switch ($step) {
|
||||
case 'Create':
|
||||
$code .= PHP_EOL . <<<'EOT'
|
||||
$_POST = self::$paste;
|
||||
$_POST = helper::getPaste();
|
||||
$_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
|
||||
$_SERVER['REQUEST_METHOD'] = 'POST';
|
||||
$_SERVER['REMOTE_ADDR'] = '::1';
|
||||
EOT;
|
||||
break;
|
||||
case 'Read':
|
||||
$code .= PHP_EOL . <<<'EOT'
|
||||
$this->_model->create(self::$pasteid, self::$paste);
|
||||
$_SERVER['QUERY_STRING'] = self::$pasteid;
|
||||
$this->_model->create(helper::getPasteId(), helper::getPaste());
|
||||
$_SERVER['QUERY_STRING'] = helper::getPasteId();
|
||||
EOT;
|
||||
break;
|
||||
case 'Delete':
|
||||
$code .= PHP_EOL . <<<'EOT'
|
||||
$this->_model->create(self::$pasteid, self::$paste);
|
||||
$this->assertTrue($this->_model->exists(self::$pasteid), 'paste exists before deleting data');
|
||||
$_GET['pasteid'] = self::$pasteid;
|
||||
$_GET['deletetoken'] = hash_hmac('sha1', self::$pasteid, serversalt::get());
|
||||
$this->_model->create(helper::getPasteId(), helper::getPaste());
|
||||
$this->assertTrue($this->_model->exists(helper::getPasteId()), 'paste exists before deleting data');
|
||||
$_GET['pasteid'] = helper::getPasteId();
|
||||
$_GET['deletetoken'] = hash_hmac('sha1', helper::getPasteId(), serversalt::get());
|
||||
EOT;
|
||||
break;
|
||||
}
|
||||
@@ -720,7 +582,7 @@ EOT;
|
||||
$this->assertTag(
|
||||
array(
|
||||
'id' => 'cipherdata',
|
||||
'content' => htmlspecialchars(json_encode(self::$paste), ENT_NOQUOTES)
|
||||
'content' => htmlspecialchars(helper::getPasteAsJson(), ENT_NOQUOTES)
|
||||
),
|
||||
$content,
|
||||
'outputs data correctly'
|
||||
@@ -738,7 +600,7 @@ EOT;
|
||||
$content,
|
||||
'outputs deleted status correctly'
|
||||
);
|
||||
$this->assertFalse($this->_model->exists(self::$pasteid), 'paste successfully deleted');
|
||||
$this->assertFalse($this->_model->exists(helper::getPasteId()), 'paste successfully deleted');
|
||||
EOT;
|
||||
break;
|
||||
}
|
||||
|
||||
161
tst/configuration.php
Normal file
161
tst/configuration.php
Normal file
@@ -0,0 +1,161 @@
|
||||
<?php
|
||||
class configurationTest extends PHPUnit_Framework_TestCase
|
||||
{
|
||||
private $_options = array(
|
||||
'main' => array(
|
||||
'discussion' => true,
|
||||
'opendiscussion' => false,
|
||||
'password' => true,
|
||||
'fileupload' => false,
|
||||
'burnafterreadingselected' => false,
|
||||
'defaultformatter' => 'plaintext',
|
||||
'syntaxhighlightingtheme' => null,
|
||||
'sizelimit' => 2097152,
|
||||
'template' => 'bootstrap',
|
||||
'notice' => '',
|
||||
'base64version' => '2.1.9',
|
||||
'languageselection' => false,
|
||||
'languagedefault' => '',
|
||||
),
|
||||
'expire' => array(
|
||||
'default' => '1week',
|
||||
'clone' => true,
|
||||
),
|
||||
'expire_options' => array(
|
||||
'5min' => 300,
|
||||
'10min' => 600,
|
||||
'1hour' => 3600,
|
||||
'1day' => 86400,
|
||||
'1week' => 604800,
|
||||
'1month' => 2592000,
|
||||
'1year' => 31536000,
|
||||
'never' => 0,
|
||||
),
|
||||
'formatter_options' => array(
|
||||
'plaintext' => 'Plain Text',
|
||||
'syntaxhighlighting' => 'Source Code',
|
||||
'markdown' => 'Markdown',
|
||||
),
|
||||
'traffic' => array(
|
||||
'limit' => 10,
|
||||
'header' => null,
|
||||
'dir' => '../data',
|
||||
),
|
||||
'model' => array(
|
||||
'class' => 'zerobin_data',
|
||||
),
|
||||
'model_options' => array(
|
||||
'dir' => '../data',
|
||||
),
|
||||
);
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
/* Setup Routine */
|
||||
helper::confBackup();
|
||||
}
|
||||
|
||||
public function tearDown()
|
||||
{
|
||||
/* Tear Down Routine */
|
||||
helper::confRestore();
|
||||
}
|
||||
|
||||
public function testDefaultConfigFile()
|
||||
{
|
||||
$this->assertTrue(copy(CONF . '.bak', CONF), 'copy default configuration file');
|
||||
$conf = new configuration;
|
||||
$this->assertEquals($this->_options, $conf->get(), 'default configuration is correct');
|
||||
}
|
||||
|
||||
public function testHandleFreshConfigFile()
|
||||
{
|
||||
helper::createIniFile(CONF, $this->_options);
|
||||
$conf = new configuration;
|
||||
$this->assertEquals($this->_options, $conf->get(), 'newly generated configuration is correct');
|
||||
}
|
||||
|
||||
public function testHandleMissingConfigFile()
|
||||
{
|
||||
@unlink(CONF);
|
||||
$conf = new configuration;
|
||||
$this->assertEquals($this->_options, $conf->get(), 'returns correct defaults on missing file');
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException Exception
|
||||
* @expectedExceptionCode 2
|
||||
*/
|
||||
public function testHandleBlankConfigFile()
|
||||
{
|
||||
file_put_contents(CONF, '');
|
||||
$conf = new configuration;
|
||||
}
|
||||
|
||||
public function testHandleMinimalConfigFile()
|
||||
{
|
||||
file_put_contents(CONF, '[main]' . PHP_EOL . '[model]');
|
||||
$conf = new configuration;
|
||||
$this->assertEquals($this->_options, $conf->get(), 'returns correct defaults on empty file');
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException Exception
|
||||
* @expectedExceptionCode 3
|
||||
*/
|
||||
public function testHandleInvalidSection()
|
||||
{
|
||||
file_put_contents(CONF, '[main]' . PHP_EOL . '[model]');
|
||||
$conf = new configuration;
|
||||
$conf->getKey('foo', 'bar');
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException Exception
|
||||
* @expectedExceptionCode 4
|
||||
*/
|
||||
public function testHandleInvalidKey()
|
||||
{
|
||||
file_put_contents(CONF, '[main]' . PHP_EOL . '[model]');
|
||||
$conf = new configuration;
|
||||
$conf->getKey('foo');
|
||||
}
|
||||
|
||||
public function testHandleGetKey()
|
||||
{
|
||||
file_put_contents(CONF, '[main]' . PHP_EOL . '[model]');
|
||||
$conf = new configuration;
|
||||
$this->assertEquals($this->_options['main']['sizelimit'], $conf->getKey('sizelimit'), 'get default size');
|
||||
}
|
||||
|
||||
public function testHandleWrongTypes()
|
||||
{
|
||||
$original_options = $this->_options;
|
||||
$original_options['main']['syntaxhighlightingtheme'] = 'foo';
|
||||
$options = $original_options;
|
||||
$options['main']['discussion'] = 'true';
|
||||
$options['main']['opendiscussion'] = 0;
|
||||
$options['main']['password'] = -1; // evaluates to TRUE
|
||||
$options['main']['fileupload'] = 'false';
|
||||
$options['expire_options']['foo'] = 'bar';
|
||||
$options['formatter_options'][] = 'foo';
|
||||
helper::createIniFile(CONF, $options);
|
||||
$conf = new configuration;
|
||||
$original_options['expire_options']['foo'] = intval('bar');
|
||||
$original_options['formatter_options'][0] = 'foo';
|
||||
$this->assertEquals($original_options, $conf->get(), 'incorrect types are corrected');
|
||||
}
|
||||
|
||||
public function testHandleMissingSubKeys()
|
||||
{
|
||||
$options = $this->_options;
|
||||
unset($options['expire_options']['1week']);
|
||||
unset($options['expire_options']['1year']);
|
||||
unset($options['expire_options']['never']);
|
||||
helper::createIniFile(CONF, $options);
|
||||
$conf = new configuration;
|
||||
$options['expire']['default'] = '5min';
|
||||
$this->assertEquals($options, $conf->get(), 'not overriding "missing" subkeys');
|
||||
}
|
||||
|
||||
}
|
||||
@@ -9,14 +9,31 @@ class filterTest extends PHPUnit_Framework_TestCase
|
||||
);
|
||||
}
|
||||
|
||||
public function testFilterMakesTimesHumanlyReadable()
|
||||
{
|
||||
$this->assertEquals('5 minutes', filter::time_humanreadable('5min'));
|
||||
$this->assertEquals('90 seconds', filter::time_humanreadable('90sec'));
|
||||
$this->assertEquals('1 week', filter::time_humanreadable('1week'));
|
||||
$this->assertEquals('6 months', filter::time_humanreadable('6months'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException Exception
|
||||
* @expectedExceptionCode 30
|
||||
*/
|
||||
public function testFilterFailTimesHumanlyReadable()
|
||||
{
|
||||
filter::time_humanreadable('five_minutes');
|
||||
}
|
||||
|
||||
public function testFilterMakesSizesHumanlyReadable()
|
||||
{
|
||||
$this->assertEquals('1 B', filter::size_humanreadable(1));
|
||||
$this->assertEquals('1 000 B', filter::size_humanreadable(1000));
|
||||
$this->assertEquals('1.00 kiB', filter::size_humanreadable(1024));
|
||||
$this->assertEquals('1.21 kiB', filter::size_humanreadable(1234));
|
||||
$this->assertEquals('1.00 KiB', filter::size_humanreadable(1024));
|
||||
$this->assertEquals('1.21 KiB', filter::size_humanreadable(1234));
|
||||
$exponent = 1024;
|
||||
$this->assertEquals('1 000.00 kiB', filter::size_humanreadable(1000 * $exponent));
|
||||
$this->assertEquals('1 000.00 KiB', filter::size_humanreadable(1000 * $exponent));
|
||||
$this->assertEquals('1.00 MiB', filter::size_humanreadable(1024 * $exponent));
|
||||
$this->assertEquals('1.21 MiB', filter::size_humanreadable(1234 * $exponent));
|
||||
$exponent *= 1024;
|
||||
@@ -45,13 +62,6 @@ class filterTest extends PHPUnit_Framework_TestCase
|
||||
$this->assertEquals('1.21 YiB', filter::size_humanreadable(1234 * $exponent));
|
||||
}
|
||||
|
||||
public function testPasteIdValidation()
|
||||
{
|
||||
$this->assertTrue(filter::is_valid_paste_id('a242ab7bdfb2581a'), 'valid paste id');
|
||||
$this->assertFalse(filter::is_valid_paste_id('foo'), 'invalid hex values');
|
||||
$this->assertFalse(filter::is_valid_paste_id('../bar/baz'), 'path attack');
|
||||
}
|
||||
|
||||
public function testSlowEquals()
|
||||
{
|
||||
$this->assertTrue(filter::slow_equals('foo', 'foo'), 'same string');
|
||||
|
||||
79
tst/i18n.php
Normal file
79
tst/i18n.php
Normal file
@@ -0,0 +1,79 @@
|
||||
<?php
|
||||
class i18nTest extends PHPUnit_Framework_TestCase
|
||||
{
|
||||
private $_translations = array();
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
/* Setup Routine */
|
||||
$this->_translations = json_decode(
|
||||
file_get_contents(PATH . 'i18n' . DIRECTORY_SEPARATOR . 'de.json'),
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
public function tearDown()
|
||||
{
|
||||
/* Tear Down Routine */
|
||||
}
|
||||
|
||||
public function testTranslationFallback()
|
||||
{
|
||||
$_SERVER['HTTP_ACCEPT_LANGUAGE'] = 'foobar';
|
||||
$messageId = 'It does not matter if the message ID exists';
|
||||
i18n::loadTranslations();
|
||||
$this->assertEquals($messageId, i18n::_($messageId), 'fallback to en');
|
||||
}
|
||||
|
||||
public function testCookieLanguageDeDetection()
|
||||
{
|
||||
$_COOKIE['lang'] = 'de';
|
||||
i18n::loadTranslations();
|
||||
$this->assertEquals($this->_translations['en'], i18n::_('en'), 'browser language de');
|
||||
$this->assertEquals('0 Stunden', i18n::_('%d hours', 0), '0 hours in german');
|
||||
$this->assertEquals('1 Stunde', i18n::_('%d hours', 1), '1 hour in german');
|
||||
$this->assertEquals('2 Stunden', i18n::_('%d hours', 2), '2 hours in french');
|
||||
}
|
||||
|
||||
public function testBrowserLanguageDeDetection()
|
||||
{
|
||||
$_SERVER['HTTP_ACCEPT_LANGUAGE'] = 'de-CH,de;q=0.8,en-GB;q=0.6,en-US;q=0.4,en;q=0.2';
|
||||
i18n::loadTranslations();
|
||||
$this->assertEquals($this->_translations['en'], i18n::_('en'), 'browser language de');
|
||||
$this->assertEquals('0 Stunden', i18n::_('%d hours', 0), '0 hours in german');
|
||||
$this->assertEquals('1 Stunde', i18n::_('%d hours', 1), '1 hour in german');
|
||||
$this->assertEquals('2 Stunden', i18n::_('%d hours', 2), '2 hours in french');
|
||||
}
|
||||
|
||||
public function testBrowserLanguageFrDetection()
|
||||
{
|
||||
$_SERVER['HTTP_ACCEPT_LANGUAGE'] = 'fr-CH,fr;q=0.8,en-GB;q=0.6,en-US;q=0.4,en;q=0.2';
|
||||
i18n::loadTranslations();
|
||||
$this->assertEquals('fr', i18n::_('en'), 'browser language fr');
|
||||
$this->assertEquals('0 heure', i18n::_('%d hours', 0), '0 hours in french');
|
||||
$this->assertEquals('1 heure', i18n::_('%d hours', 1), '1 hour in french');
|
||||
$this->assertEquals('2 heures', i18n::_('%d hours', 2), '2 hours in french');
|
||||
}
|
||||
|
||||
public function testBrowserLanguagePlDetection()
|
||||
{
|
||||
$_SERVER['HTTP_ACCEPT_LANGUAGE'] = 'pl;q=0.8,en-GB;q=0.6,en-US;q=0.4,en;q=0.2';
|
||||
i18n::loadTranslations();
|
||||
$this->assertEquals('pl', i18n::_('en'), 'browser language pl');
|
||||
$this->assertEquals('2 godzina', i18n::_('%d hours', 2), 'hours in polish');
|
||||
}
|
||||
|
||||
public function testBrowserLanguageAnyDetection()
|
||||
{
|
||||
$_SERVER['HTTP_ACCEPT_LANGUAGE'] = '*';
|
||||
i18n::loadTranslations();
|
||||
$this->assertTrue(strlen(i18n::_('en')) == 2, 'browser language any');
|
||||
}
|
||||
|
||||
public function testVariableInjection()
|
||||
{
|
||||
$_SERVER['HTTP_ACCEPT_LANGUAGE'] = 'foobar';
|
||||
i18n::loadTranslations();
|
||||
$this->assertEquals('some string + 1', i18n::_('some %s + %d', 'string', 1), 'browser language en');
|
||||
}
|
||||
}
|
||||
257
tst/jsonApi.php
Normal file
257
tst/jsonApi.php
Normal file
@@ -0,0 +1,257 @@
|
||||
<?php
|
||||
class jsonApiTest extends PHPUnit_Framework_TestCase
|
||||
{
|
||||
protected $_model;
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
/* Setup Routine */
|
||||
$this->_model = zerobin_data::getInstance(array('dir' => PATH . 'data'));
|
||||
serversalt::setPath(PATH . 'data');
|
||||
$this->reset();
|
||||
}
|
||||
|
||||
public function tearDown()
|
||||
{
|
||||
/* Tear Down Routine */
|
||||
helper::confRestore();
|
||||
}
|
||||
|
||||
public function reset()
|
||||
{
|
||||
$_POST = array();
|
||||
$_GET = array();
|
||||
$_SERVER = array();
|
||||
if ($this->_model->exists(helper::getPasteId()))
|
||||
$this->_model->delete(helper::getPasteId());
|
||||
helper::confRestore();
|
||||
}
|
||||
|
||||
/**
|
||||
* @runInSeparateProcess
|
||||
*/
|
||||
public function testCreate()
|
||||
{
|
||||
$this->reset();
|
||||
$options = parse_ini_file(CONF, true);
|
||||
$options['traffic']['limit'] = 0;
|
||||
helper::confBackup();
|
||||
helper::createIniFile(CONF, $options);
|
||||
$_POST = helper::getPaste();
|
||||
$_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
|
||||
$_SERVER['REQUEST_METHOD'] = 'POST';
|
||||
$_SERVER['REMOTE_ADDR'] = '::1';
|
||||
ob_start();
|
||||
new zerobin;
|
||||
$content = ob_get_contents();
|
||||
$response = json_decode($content, true);
|
||||
$this->assertEquals(0, $response['status'], 'outputs status');
|
||||
$this->assertEquals(
|
||||
hash_hmac('sha1', $response['id'], serversalt::get()),
|
||||
$response['deletetoken'],
|
||||
'outputs valid delete token'
|
||||
);
|
||||
$this->assertStringEndsWith('?' . $response['id'], $response['url'], 'returned URL points to new paste');
|
||||
$this->assertTrue($this->_model->exists($response['id']), 'paste exists after posting data');
|
||||
}
|
||||
|
||||
/**
|
||||
* @runInSeparateProcess
|
||||
*/
|
||||
public function testPut()
|
||||
{
|
||||
$this->reset();
|
||||
$options = parse_ini_file(CONF, true);
|
||||
$options['traffic']['limit'] = 0;
|
||||
helper::confBackup();
|
||||
helper::createIniFile(CONF, $options);
|
||||
$paste = helper::getPaste();
|
||||
unset($paste['meta']);
|
||||
$file = tempnam(sys_get_temp_dir(), 'FOO');
|
||||
file_put_contents($file, http_build_query($paste));
|
||||
request::setInputStream($file);
|
||||
$_SERVER['QUERY_STRING'] = helper::getPasteId();
|
||||
$_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
|
||||
$_SERVER['REQUEST_METHOD'] = 'PUT';
|
||||
$_SERVER['REMOTE_ADDR'] = '::1';
|
||||
ob_start();
|
||||
new zerobin;
|
||||
$content = ob_get_contents();
|
||||
$response = json_decode($content, true);
|
||||
$this->assertEquals(0, $response['status'], 'outputs status');
|
||||
$this->assertEquals(helper::getPasteId(), $response['id'], 'outputted paste ID matches input');
|
||||
$this->assertEquals(
|
||||
hash_hmac('sha1', $response['id'], serversalt::get()),
|
||||
$response['deletetoken'],
|
||||
'outputs valid delete token'
|
||||
);
|
||||
$this->assertStringEndsWith('?' . $response['id'], $response['url'], 'returned URL points to new paste');
|
||||
$this->assertTrue($this->_model->exists($response['id']), 'paste exists after posting data');
|
||||
}
|
||||
|
||||
/**
|
||||
* @runInSeparateProcess
|
||||
*/
|
||||
public function testDelete()
|
||||
{
|
||||
$this->reset();
|
||||
$this->_model->create(helper::getPasteId(), helper::getPaste());
|
||||
$this->assertTrue($this->_model->exists(helper::getPasteId()), 'paste exists before deleting data');
|
||||
$file = tempnam(sys_get_temp_dir(), 'FOO');
|
||||
file_put_contents($file, http_build_query(array(
|
||||
'deletetoken' => hash_hmac('sha1', helper::getPasteId(), serversalt::get()),
|
||||
)));
|
||||
request::setInputStream($file);
|
||||
$_SERVER['QUERY_STRING'] = helper::getPasteId();
|
||||
$_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
|
||||
$_SERVER['REQUEST_METHOD'] = 'DELETE';
|
||||
ob_start();
|
||||
new zerobin;
|
||||
$content = ob_get_contents();
|
||||
$response = json_decode($content, true);
|
||||
$this->assertEquals(0, $response['status'], 'outputs status');
|
||||
$this->assertFalse($this->_model->exists(helper::getPasteId()), 'paste successfully deleted');
|
||||
}
|
||||
|
||||
/**
|
||||
* @runInSeparateProcess
|
||||
*/
|
||||
public function testDeleteWithPost()
|
||||
{
|
||||
$this->reset();
|
||||
$this->_model->create(helper::getPasteId(), helper::getPaste());
|
||||
$this->assertTrue($this->_model->exists(helper::getPasteId()), 'paste exists before deleting data');
|
||||
$_POST = array(
|
||||
'action' => 'delete',
|
||||
'deletetoken' => hash_hmac('sha1', helper::getPasteId(), serversalt::get()),
|
||||
);
|
||||
$_SERVER['QUERY_STRING'] = helper::getPasteId();
|
||||
$_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
|
||||
$_SERVER['REQUEST_METHOD'] = 'POST';
|
||||
ob_start();
|
||||
new zerobin;
|
||||
$content = ob_get_contents();
|
||||
$response = json_decode($content, true);
|
||||
$this->assertEquals(0, $response['status'], 'outputs status');
|
||||
$this->assertFalse($this->_model->exists(helper::getPasteId()), 'paste successfully deleted');
|
||||
}
|
||||
|
||||
/**
|
||||
* @runInSeparateProcess
|
||||
*/
|
||||
public function testRead()
|
||||
{
|
||||
$this->reset();
|
||||
$paste = helper::getPasteWithAttachment();
|
||||
$this->_model->create(helper::getPasteId(), $paste);
|
||||
$_SERVER['QUERY_STRING'] = helper::getPasteId();
|
||||
$_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
|
||||
ob_start();
|
||||
new zerobin;
|
||||
$content = ob_get_contents();
|
||||
$response = json_decode($content, true);
|
||||
$this->assertEquals(0, $response['status'], 'outputs success status');
|
||||
$this->assertEquals(helper::getPasteId(), $response['id'], 'outputs data correctly');
|
||||
$this->assertStringEndsWith('?' . $response['id'], $response['url'], 'returned URL points to new paste');
|
||||
$this->assertEquals($paste['data'], $response['data'], 'outputs data correctly');
|
||||
$this->assertEquals($paste['attachment'], $response['attachment'], 'outputs attachment correctly');
|
||||
$this->assertEquals($paste['attachmentname'], $response['attachmentname'], 'outputs attachmentname correctly');
|
||||
$this->assertEquals($paste['meta']['formatter'], $response['meta']['formatter'], 'outputs format correctly');
|
||||
$this->assertEquals($paste['meta']['postdate'], $response['meta']['postdate'], 'outputs postdate correctly');
|
||||
$this->assertEquals($paste['meta']['opendiscussion'], $response['meta']['opendiscussion'], 'outputs opendiscussion correctly');
|
||||
$this->assertEquals(0, $response['comment_count'], 'outputs comment_count correctly');
|
||||
$this->assertEquals(0, $response['comment_offset'], 'outputs comment_offset correctly');
|
||||
}
|
||||
|
||||
/**
|
||||
* @runInSeparateProcess
|
||||
*/
|
||||
public function testJsonLdPaste()
|
||||
{
|
||||
$this->reset();
|
||||
$paste = helper::getPasteWithAttachment();
|
||||
$this->_model->create(helper::getPasteId(), $paste);
|
||||
$_GET['jsonld'] = 'paste';
|
||||
ob_start();
|
||||
new zerobin;
|
||||
$content = ob_get_contents();
|
||||
$this->assertEquals(str_replace(
|
||||
'?jsonld=',
|
||||
'/?jsonld=',
|
||||
file_get_contents(PUBLIC_PATH . '/js/paste.jsonld')
|
||||
), $content, 'outputs data correctly');
|
||||
}
|
||||
|
||||
/**
|
||||
* @runInSeparateProcess
|
||||
*/
|
||||
public function testJsonLdComment()
|
||||
{
|
||||
$this->reset();
|
||||
$paste = helper::getPasteWithAttachment();
|
||||
$this->_model->create(helper::getPasteId(), $paste);
|
||||
$_GET['jsonld'] = 'comment';
|
||||
ob_start();
|
||||
new zerobin;
|
||||
$content = ob_get_contents();
|
||||
$this->assertEquals(str_replace(
|
||||
'?jsonld=',
|
||||
'/?jsonld=',
|
||||
file_get_contents(PUBLIC_PATH . '/js/comment.jsonld')
|
||||
), $content, 'outputs data correctly');
|
||||
}
|
||||
|
||||
/**
|
||||
* @runInSeparateProcess
|
||||
*/
|
||||
public function testJsonLdPasteMeta()
|
||||
{
|
||||
$this->reset();
|
||||
$paste = helper::getPasteWithAttachment();
|
||||
$this->_model->create(helper::getPasteId(), $paste);
|
||||
$_GET['jsonld'] = 'pastemeta';
|
||||
ob_start();
|
||||
new zerobin;
|
||||
$content = ob_get_contents();
|
||||
$this->assertEquals(str_replace(
|
||||
'?jsonld=',
|
||||
'/?jsonld=',
|
||||
file_get_contents(PUBLIC_PATH . '/js/pastemeta.jsonld')
|
||||
), $content, 'outputs data correctly');
|
||||
}
|
||||
|
||||
/**
|
||||
* @runInSeparateProcess
|
||||
*/
|
||||
public function testJsonLdCommentMeta()
|
||||
{
|
||||
$this->reset();
|
||||
$paste = helper::getPasteWithAttachment();
|
||||
$this->_model->create(helper::getPasteId(), $paste);
|
||||
$_GET['jsonld'] = 'commentmeta';
|
||||
ob_start();
|
||||
new zerobin;
|
||||
$content = ob_get_contents();
|
||||
$this->assertEquals(str_replace(
|
||||
'?jsonld=',
|
||||
'/?jsonld=',
|
||||
file_get_contents(PUBLIC_PATH . '/js/commentmeta.jsonld')
|
||||
), $content, 'outputs data correctly');
|
||||
}
|
||||
|
||||
/**
|
||||
* @runInSeparateProcess
|
||||
*/
|
||||
public function testJsonLdInvalid()
|
||||
{
|
||||
$this->reset();
|
||||
$paste = helper::getPasteWithAttachment();
|
||||
$this->_model->create(helper::getPasteId(), $paste);
|
||||
$_GET['jsonld'] = '../cfg/conf.ini';
|
||||
ob_start();
|
||||
new zerobin;
|
||||
$content = ob_get_contents();
|
||||
$this->assertEquals('{}', $content, 'does not output nasty data');
|
||||
}
|
||||
|
||||
}
|
||||
211
tst/model.php
Normal file
211
tst/model.php
Normal file
@@ -0,0 +1,211 @@
|
||||
<?php
|
||||
class modelTest extends PHPUnit_Framework_TestCase
|
||||
{
|
||||
private $_conf;
|
||||
|
||||
private $_model;
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
/* Setup Routine */
|
||||
$options = parse_ini_file(CONF, true);
|
||||
$options['model'] = array(
|
||||
'class' => 'zerobin_db',
|
||||
);
|
||||
$options['model_options'] = array(
|
||||
'dsn' => 'sqlite::memory:',
|
||||
'usr' => null,
|
||||
'pwd' => null,
|
||||
'opt' => array(PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION),
|
||||
);
|
||||
helper::confBackup();
|
||||
helper::createIniFile(CONF, $options);
|
||||
$this->_conf = new configuration;
|
||||
$this->_model = new model($this->_conf);
|
||||
$_SERVER['REMOTE_ADDR'] = '::1';
|
||||
}
|
||||
|
||||
public function tearDown()
|
||||
{
|
||||
/* Tear Down Routine */
|
||||
}
|
||||
|
||||
public function testBasicWorkflow()
|
||||
{
|
||||
// storing pastes
|
||||
$pasteData = helper::getPaste();
|
||||
$this->_model->getPaste(helper::getPasteId())->delete();
|
||||
$paste = $this->_model->getPaste(helper::getPasteId());
|
||||
$this->assertFalse($paste->exists(), 'paste does not yet exist');
|
||||
|
||||
$paste = $this->_model->getPaste();
|
||||
$paste->setData($pasteData['data']);
|
||||
$paste->setOpendiscussion();
|
||||
$paste->setFormatter($pasteData['meta']['formatter']);
|
||||
$paste->store();
|
||||
|
||||
$paste = $this->_model->getPaste(helper::getPasteId());
|
||||
$this->assertTrue($paste->exists(), 'paste exists after storing it');
|
||||
$paste = $paste->get();
|
||||
$this->assertEquals($pasteData['data'], $paste->data);
|
||||
foreach (array('opendiscussion', 'formatter') as $key) {
|
||||
$this->assertEquals($pasteData['meta'][$key], $paste->meta->$key);
|
||||
}
|
||||
|
||||
// storing comments
|
||||
$commentData = helper::getComment();
|
||||
$paste = $this->_model->getPaste(helper::getPasteId());
|
||||
$comment = $paste->getComment(helper::getPasteId(), helper::getCommentId());
|
||||
$this->assertFalse($comment->exists(), 'comment does not yet exist');
|
||||
|
||||
$comment = $paste->getComment(helper::getPasteId());
|
||||
$comment->setData($commentData['data']);
|
||||
$comment->setNickname($commentData['meta']['nickname']);
|
||||
$comment->store();
|
||||
|
||||
$comment = $paste->getComment(helper::getPasteId(), helper::getCommentId());
|
||||
$this->assertTrue($comment->exists(), 'comment exists after storing it');
|
||||
$comment = $comment->get();
|
||||
$this->assertEquals($commentData['data'], $comment->data);
|
||||
$this->assertEquals($commentData['meta']['nickname'], $comment->meta->nickname);
|
||||
|
||||
// deleting pastes
|
||||
$this->_model->getPaste(helper::getPasteId())->delete();
|
||||
$paste = $this->_model->getPaste(helper::getPasteId());
|
||||
$this->assertFalse($paste->exists(), 'paste successfully deleted');
|
||||
$this->assertEquals(array(), $paste->getComments(), 'comment was deleted with paste');
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException Exception
|
||||
* @expectedExceptionCode 75
|
||||
*/
|
||||
public function testPasteDuplicate()
|
||||
{
|
||||
$pasteData = helper::getPaste();
|
||||
|
||||
$this->_model->getPaste(helper::getPasteId())->delete();
|
||||
$paste = $this->_model->getPaste();
|
||||
$paste->setData($pasteData['data']);
|
||||
$paste->setOpendiscussion();
|
||||
$paste->setFormatter($pasteData['meta']['formatter']);
|
||||
$paste->store();
|
||||
|
||||
$paste = $this->_model->getPaste();
|
||||
$paste->setData($pasteData['data']);
|
||||
$paste->setOpendiscussion();
|
||||
$paste->setFormatter($pasteData['meta']['formatter']);
|
||||
$paste->store();
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException Exception
|
||||
* @expectedExceptionCode 69
|
||||
*/
|
||||
public function testCommentDuplicate()
|
||||
{
|
||||
$pasteData = helper::getPaste();
|
||||
$commentData = helper::getComment();
|
||||
$this->_model->getPaste(helper::getPasteId())->delete();
|
||||
|
||||
$paste = $this->_model->getPaste();
|
||||
$paste->setData($pasteData['data']);
|
||||
$paste->setOpendiscussion();
|
||||
$paste->setFormatter($pasteData['meta']['formatter']);
|
||||
$paste->store();
|
||||
|
||||
$comment = $paste->getComment(helper::getPasteId());
|
||||
$comment->setData($commentData['data']);
|
||||
$comment->setNickname($commentData['meta']['nickname']);
|
||||
$comment->store();
|
||||
|
||||
$comment = $paste->getComment(helper::getPasteId());
|
||||
$comment->setData($commentData['data']);
|
||||
$comment->setNickname($commentData['meta']['nickname']);
|
||||
$comment->store();
|
||||
}
|
||||
|
||||
public function testImplicitDefaults()
|
||||
{
|
||||
$pasteData = helper::getPaste();
|
||||
$commentData = helper::getComment();
|
||||
$this->_model->getPaste(helper::getPasteId())->delete();
|
||||
|
||||
$paste = $this->_model->getPaste();
|
||||
$paste->setData($pasteData['data']);
|
||||
$paste->setBurnafterreading();
|
||||
$paste->setOpendiscussion();
|
||||
// not setting a formatter, should use default one
|
||||
$paste->store();
|
||||
|
||||
$paste = $this->_model->getPaste(helper::getPasteId())->get(); // ID was set based on data
|
||||
$this->assertEquals(true, property_exists($paste->meta, 'burnafterreading') && $paste->meta->burnafterreading, 'burn after reading takes precendence');
|
||||
$this->assertEquals(false, property_exists($paste->meta, 'opendiscussion') && $paste->meta->opendiscussion, 'opendiscussion is disabled');
|
||||
$this->assertEquals($this->_conf->getKey('defaultformatter'), $paste->meta->formatter, 'default formatter is set');
|
||||
|
||||
$this->_model->getPaste(helper::getPasteId())->delete();
|
||||
$paste = $this->_model->getPaste();
|
||||
$paste->setData($pasteData['data']);
|
||||
$paste->setBurnafterreading('0');
|
||||
$paste->setOpendiscussion();
|
||||
$paste->store();
|
||||
|
||||
$vz = new vizhash16x16();
|
||||
$pngdata = 'data:image/png;base64,' . base64_encode($vz->generate($_SERVER['REMOTE_ADDR']));
|
||||
$comment = $paste->getComment(helper::getPasteId());
|
||||
$comment->setData($commentData['data']);
|
||||
$comment->setNickname($commentData['meta']['nickname']);
|
||||
$comment->store();
|
||||
|
||||
$comment = $paste->getComment(helper::getPasteId(), helper::getCommentId())->get();
|
||||
$this->assertEquals($pngdata, $comment->meta->vizhash, 'nickname triggers vizhash to be set');
|
||||
}
|
||||
|
||||
public function testPasteIdValidation()
|
||||
{
|
||||
$this->assertTrue(model_paste::isValidId('a242ab7bdfb2581a'), 'valid paste id');
|
||||
$this->assertFalse(model_paste::isValidId('foo'), 'invalid hex values');
|
||||
$this->assertFalse(model_paste::isValidId('../bar/baz'), 'path attack');
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException Exception
|
||||
* @expectedExceptionCode 62
|
||||
*/
|
||||
public function testInvalidComment()
|
||||
{
|
||||
$paste = $this->_model->getPaste();
|
||||
$comment = $paste->getComment(helper::getPasteId());
|
||||
}
|
||||
|
||||
public function testExpiration()
|
||||
{
|
||||
$pasteData = helper::getPaste();
|
||||
$this->_model->getPaste(helper::getPasteId())->delete();
|
||||
$paste = $this->_model->getPaste(helper::getPasteId());
|
||||
$this->assertFalse($paste->exists(), 'paste does not yet exist');
|
||||
|
||||
$paste = $this->_model->getPaste();
|
||||
$paste->setData($pasteData['data']);
|
||||
$paste->setExpiration('5min'); // = 300 seconds
|
||||
$paste->store();
|
||||
|
||||
$paste = $paste->get();
|
||||
$this->assertEquals(300, $paste->meta->remaining_time, 'remaining time is set correctly');
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException Exception
|
||||
* @expectedExceptionCode 64
|
||||
*/
|
||||
public function testCommentDeletion()
|
||||
{
|
||||
$pasteData = helper::getPaste();
|
||||
$this->_model->getPaste(helper::getPasteId())->delete();
|
||||
|
||||
$paste = $this->_model->getPaste();
|
||||
$paste->setData($pasteData['data']);
|
||||
$paste->store();
|
||||
$paste->getComment(helper::getPasteId())->delete();
|
||||
}
|
||||
}
|
||||
107
tst/request.php
Normal file
107
tst/request.php
Normal file
@@ -0,0 +1,107 @@
|
||||
<?php
|
||||
class requestTest extends PHPUnit_Framework_TestCase
|
||||
{
|
||||
private $_request;
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
/* Setup Routine */
|
||||
}
|
||||
|
||||
public function tearDown()
|
||||
{
|
||||
/* Tear Down Routine */
|
||||
}
|
||||
|
||||
public function reset()
|
||||
{
|
||||
$_SERVER = array();
|
||||
$_GET = array();
|
||||
$_POST = array();
|
||||
}
|
||||
|
||||
public function testView()
|
||||
{
|
||||
$this->reset();
|
||||
$_SERVER['REQUEST_METHOD'] = 'GET';
|
||||
$request = new request;
|
||||
$this->assertFalse($request->isJsonApiCall(), 'is HTML call');
|
||||
$this->assertEquals('view', $request->getOperation());
|
||||
}
|
||||
|
||||
public function testRead()
|
||||
{
|
||||
$this->reset();
|
||||
$_SERVER['REQUEST_METHOD'] = 'GET';
|
||||
$_SERVER['QUERY_STRING'] = 'foo';
|
||||
$request = new request;
|
||||
$this->assertFalse($request->isJsonApiCall(), 'is HTML call');
|
||||
$this->assertEquals('foo', $request->getParam('pasteid'));
|
||||
$this->assertEquals('read', $request->getOperation());
|
||||
}
|
||||
|
||||
public function testDelete()
|
||||
{
|
||||
$this->reset();
|
||||
$_SERVER['REQUEST_METHOD'] = 'GET';
|
||||
$_GET['pasteid'] = 'foo';
|
||||
$_GET['deletetoken'] = 'bar';
|
||||
$request = new request;
|
||||
$this->assertFalse($request->isJsonApiCall(), 'is HTML call');
|
||||
$this->assertEquals('delete', $request->getOperation());
|
||||
$this->assertEquals('foo', $request->getParam('pasteid'));
|
||||
$this->assertEquals('bar', $request->getParam('deletetoken'));
|
||||
}
|
||||
|
||||
public function testApiCreate()
|
||||
{
|
||||
$this->reset();
|
||||
$_SERVER['REQUEST_METHOD'] = 'PUT';
|
||||
$_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
|
||||
$file = tempnam(sys_get_temp_dir(), 'FOO');
|
||||
file_put_contents($file, 'data=foo');
|
||||
request::setInputStream($file);
|
||||
$request = new request;
|
||||
$this->assertTrue($request->isJsonApiCall(), 'is JSON Api call');
|
||||
$this->assertEquals('create', $request->getOperation());
|
||||
$this->assertEquals('foo', $request->getParam('data'));
|
||||
}
|
||||
|
||||
public function testApiCreateAlternative()
|
||||
{
|
||||
$this->reset();
|
||||
$_SERVER['REQUEST_METHOD'] = 'POST';
|
||||
$_SERVER['HTTP_ACCEPT'] = 'application/json, text/javascript, */*; q=0.01';
|
||||
$_POST['attachment'] = 'foo';
|
||||
$request = new request;
|
||||
$this->assertTrue($request->isJsonApiCall(), 'is JSON Api call');
|
||||
$this->assertEquals('create', $request->getOperation());
|
||||
$this->assertEquals('foo', $request->getParam('attachment'));
|
||||
}
|
||||
|
||||
public function testApiRead()
|
||||
{
|
||||
$this->reset();
|
||||
$_SERVER['REQUEST_METHOD'] = 'GET';
|
||||
$_SERVER['HTTP_ACCEPT'] = 'application/json, text/javascript, */*; q=0.01';
|
||||
$_SERVER['QUERY_STRING'] = 'foo';
|
||||
$request = new request;
|
||||
$this->assertTrue($request->isJsonApiCall(), 'is JSON Api call');
|
||||
$this->assertEquals('foo', $request->getParam('pasteid'));
|
||||
$this->assertEquals('read', $request->getOperation());
|
||||
}
|
||||
|
||||
public function testApiDelete()
|
||||
{
|
||||
$this->reset();
|
||||
$_SERVER['REQUEST_METHOD'] = 'POST';
|
||||
$_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
|
||||
$_SERVER['QUERY_STRING'] = 'foo';
|
||||
$_POST['deletetoken'] = 'bar';
|
||||
$request = new request;
|
||||
$this->assertTrue($request->isJsonApiCall(), 'is JSON Api call');
|
||||
$this->assertEquals('delete', $request->getOperation());
|
||||
$this->assertEquals('foo', $request->getParam('pasteid'));
|
||||
$this->assertEquals('bar', $request->getParam('deletetoken'));
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,12 @@ class sjclTest extends PHPUnit_Framework_TestCase
|
||||
{
|
||||
public function testSjclValidatorValidatesCorrectly()
|
||||
{
|
||||
$paste = helper::getPasteWithAttachment();
|
||||
$this->assertTrue(sjcl::isValid($paste['data']), 'valid sjcl');
|
||||
$this->assertTrue(sjcl::isValid($paste['attachment']), 'valid sjcl');
|
||||
$this->assertTrue(sjcl::isValid($paste['attachmentname']), 'valid sjcl');
|
||||
$this->assertTrue(sjcl::isValid(helper::getComment()['data']), 'valid sjcl');
|
||||
|
||||
$this->assertTrue(sjcl::isValid('{"iv":"83Ax/OdUav3SanDW9dcQPg","v":1,"iter":1000,"ks":128,"ts":64,"mode":"ccm","adata":"","cipher":"aes","salt":"Gx1vA2/gQ3U","ct":"j7ImByuE5xCqD2YXm6aSyA"}'), 'valid sjcl');
|
||||
$this->assertFalse(sjcl::isValid('{"iv":"$","v":1,"iter":1000,"ks":128,"ts":64,"mode":"ccm","adata":"","cipher":"aes","salt":"Gx1vA2/gQ3U","ct":"j7ImByuE5xCqD2YXm6aSyA"}'), 'invalid base64 encoding of iv');
|
||||
$this->assertFalse(sjcl::isValid('{"iv":"83Ax/OdUav3SanDW9dcQPg","v":1,"iter":1000,"ks":128,"ts":64,"mode":"ccm","adata":"","cipher":"aes","salt":"$","ct":"j7ImByuE5xCqD2YXm6aSyA"}'), 'invalid base64 encoding of salt');
|
||||
|
||||
@@ -22,12 +22,15 @@ class trafficlimiterTest extends PHPUnit_Framework_TestCase
|
||||
$file = 'baz';
|
||||
$this->assertEquals($this->_path . DIRECTORY_SEPARATOR . $file, trafficlimiter::getPath($file));
|
||||
trafficlimiter::setLimit(4);
|
||||
$this->assertTrue(trafficlimiter::canPass('127.0.0.1'), 'first request may pass');
|
||||
$_SERVER['REMOTE_ADDR'] = '127.0.0.1';
|
||||
$this->assertTrue(trafficlimiter::canPass(), 'first request may pass');
|
||||
sleep(2);
|
||||
$this->assertFalse(trafficlimiter::canPass('127.0.0.1'), 'second request is to fast, may not pass');
|
||||
$this->assertFalse(trafficlimiter::canPass(), 'second request is to fast, may not pass');
|
||||
sleep(3);
|
||||
$this->assertTrue(trafficlimiter::canPass('127.0.0.1'), 'third request waited long enough and may pass');
|
||||
$this->assertTrue(trafficlimiter::canPass('2001:1620:2057:dead:beef::cafe:babe'), 'fourth request has different ip and may pass');
|
||||
$this->assertFalse(trafficlimiter::canPass('127.0.0.1'), 'fifth request is to fast, may not pass');
|
||||
$this->assertTrue(trafficlimiter::canPass(), 'third request waited long enough and may pass');
|
||||
$_SERVER['REMOTE_ADDR'] = '2001:1620:2057:dead:beef::cafe:babe';
|
||||
$this->assertTrue(trafficlimiter::canPass(), 'fourth request has different ip and may pass');
|
||||
$_SERVER['REMOTE_ADDR'] = '127.0.0.1';
|
||||
$this->assertFalse(trafficlimiter::canPass(), 'fifth request is to fast, may not pass');
|
||||
}
|
||||
}
|
||||
|
||||
544
tst/zerobin.php
544
tst/zerobin.php
@@ -1,43 +1,20 @@
|
||||
<?php
|
||||
class zerobinTest extends PHPUnit_Framework_TestCase
|
||||
{
|
||||
private static $pasteid = '5e9bc25c89fb3bf9';
|
||||
|
||||
private static $paste = array(
|
||||
'data' => '{"iv":"EN39/wd5Nk8HAiSG2K5AsQ","v":1,"iter":1000,"ks":128,"ts":64,"mode":"ccm","adata":"","cipher":"aes","salt":"QKN1DBXe5PI","ct":"8hA83xDdXjD7K2qfmw5NdA"}',
|
||||
'meta' => array(
|
||||
'postdate' => 1344803344,
|
||||
'opendiscussion' => true,
|
||||
),
|
||||
);
|
||||
|
||||
private static $commentid = '5a52eebf11c4c94b';
|
||||
|
||||
private static $comment = array(
|
||||
'data' => '{"iv":"Pd4pOKWkmDTT9uPwVwd5Ag","v":1,"iter":1000,"ks":128,"ts":64,"mode":"ccm","adata":"","cipher":"aes","salt":"ZIUhFTliVz4","ct":"6nOCU3peNDclDDpFtJEBKA"}',
|
||||
'meta' => array(
|
||||
'nickname' => '{"iv":"76MkAtOGC4oFogX/aSMxRA","v":1,"iter":1000,"ks":128,"ts":64,"mode":"ccm","adata":"","cipher":"aes","salt":"ZIUhFTliVz4","ct":"b6Ae/U1xJdsX/+lATud4sQ"}',
|
||||
'vizhash' => 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAABGUlEQVQokWOsl5/94983CNKQMjnxaOePf98MeKwPfNjkLZ3AgARab6b9+PeNEVnDj3/ff/z7ZiHnzsDA8Pv7H2TVPJw8EAYLAwb48OaVgIgYKycLsrYv378wMDB8//qdCVMDRA9EKSsnCwRBxNsepaLboMFlyMDAICAi9uHNK24GITQ/MDAwoNhgIGMLtwGrzegaLjw5jMz9+vUdnN17uwDCQDhJgk0O07yvX9+teDX1x79v6DYIsIjgcgMaYGFgYOBg4kJx2JejkAiBxAw+PzAwMNz4dp6wDXDw4MdNNOl0rWYsNkD89OLXI/xmo9sgzatJjAYmBgYGDiauD3/ePP18nVgb4MF89+M5ZX6js293wUMpnr8KTQMAxsCJnJ30apMAAAAASUVORK5CYII=',
|
||||
'postdate' => 1344803528,
|
||||
),
|
||||
);
|
||||
|
||||
private $_conf;
|
||||
|
||||
private $_model;
|
||||
protected $_model;
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
/* Setup Routine */
|
||||
$this->_model = zerobin_data::getInstance(array('dir' => PATH . 'data'));
|
||||
serversalt::setPath(PATH . 'data');
|
||||
$this->_conf = PATH . 'cfg' . DIRECTORY_SEPARATOR . 'conf.ini';
|
||||
$this->reset();
|
||||
}
|
||||
|
||||
public function tearDown()
|
||||
{
|
||||
/* Tear Down Routine */
|
||||
helper::confRestore();
|
||||
}
|
||||
|
||||
public function reset()
|
||||
@@ -45,10 +22,9 @@ class zerobinTest extends PHPUnit_Framework_TestCase
|
||||
$_POST = array();
|
||||
$_GET = array();
|
||||
$_SERVER = array();
|
||||
if ($this->_model->exists(self::$pasteid))
|
||||
$this->_model->delete(self::$pasteid);
|
||||
if (is_file($this->_conf . '.bak'))
|
||||
rename($this->_conf . '.bak', $this->_conf);
|
||||
if ($this->_model->exists(helper::getPasteId()))
|
||||
$this->_model->delete(helper::getPasteId());
|
||||
helper::confRestore();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -70,6 +46,30 @@ class zerobinTest extends PHPUnit_Framework_TestCase
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @runInSeparateProcess
|
||||
*/
|
||||
public function testViewLanguageSelection()
|
||||
{
|
||||
$this->reset();
|
||||
$options = parse_ini_file(CONF, true);
|
||||
$options['main']['languageselection'] = true;
|
||||
helper::confBackup();
|
||||
helper::createIniFile(CONF, $options);
|
||||
$_COOKIE['lang'] = 'de';
|
||||
ob_start();
|
||||
new zerobin;
|
||||
$content = ob_get_contents();
|
||||
$this->assertTag(
|
||||
array(
|
||||
'tag' => 'title',
|
||||
'content' => 'ZeroBin'
|
||||
),
|
||||
$content,
|
||||
'outputs title correctly'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @runInSeparateProcess
|
||||
*/
|
||||
@@ -100,26 +100,8 @@ class zerobinTest extends PHPUnit_Framework_TestCase
|
||||
public function testConf()
|
||||
{
|
||||
$this->reset();
|
||||
if (!is_file($this->_conf . '.bak') && is_file($this->_conf))
|
||||
rename($this->_conf, $this->_conf . '.bak');
|
||||
file_put_contents($this->_conf, '');
|
||||
ob_start();
|
||||
new zerobin;
|
||||
$content = ob_get_contents();
|
||||
}
|
||||
|
||||
/**
|
||||
* @runInSeparateProcess
|
||||
*/
|
||||
public function testConfMissingExpireLabel()
|
||||
{
|
||||
$this->reset();
|
||||
$options = parse_ini_file($this->_conf, true);
|
||||
$options['expire_options']['foobar123'] = 10;
|
||||
if (!is_file($this->_conf . '.bak') && is_file($this->_conf))
|
||||
rename($this->_conf, $this->_conf . '.bak');
|
||||
helper::createIniFile($this->_conf, $options);
|
||||
ini_set('magic_quotes_gpc', 1);
|
||||
helper::confBackup();
|
||||
file_put_contents(CONF, '');
|
||||
ob_start();
|
||||
new zerobin;
|
||||
$content = ob_get_contents();
|
||||
@@ -131,7 +113,13 @@ class zerobinTest extends PHPUnit_Framework_TestCase
|
||||
public function testCreate()
|
||||
{
|
||||
$this->reset();
|
||||
$_POST = self::$paste;
|
||||
$options = parse_ini_file(CONF, true);
|
||||
$options['traffic']['limit'] = 0;
|
||||
helper::confBackup();
|
||||
helper::createIniFile(CONF, $options);
|
||||
$_POST = helper::getPaste();
|
||||
$_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
|
||||
$_SERVER['REQUEST_METHOD'] = 'POST';
|
||||
$_SERVER['REMOTE_ADDR'] = '::1';
|
||||
ob_start();
|
||||
new zerobin;
|
||||
@@ -152,14 +140,17 @@ class zerobinTest extends PHPUnit_Framework_TestCase
|
||||
public function testCreateInvalidTimelimit()
|
||||
{
|
||||
$this->reset();
|
||||
$_POST = self::$paste;
|
||||
$_POST = helper::getPaste();
|
||||
$_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
|
||||
$_SERVER['REQUEST_METHOD'] = 'POST';
|
||||
$_SERVER['REMOTE_ADDR'] = '::1';
|
||||
trafficlimiter::canPass();
|
||||
ob_start();
|
||||
new zerobin;
|
||||
$content = ob_get_contents();
|
||||
$response = json_decode($content, true);
|
||||
$this->assertEquals(1, $response['status'], 'outputs error status');
|
||||
$this->assertFalse($this->_model->exists(self::$pasteid), 'paste exists after posting data');
|
||||
$this->assertFalse($this->_model->exists(helper::getPasteId()), 'paste exists after posting data');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -168,20 +159,44 @@ class zerobinTest extends PHPUnit_Framework_TestCase
|
||||
public function testCreateInvalidSize()
|
||||
{
|
||||
$this->reset();
|
||||
$options = parse_ini_file($this->_conf, true);
|
||||
$options = parse_ini_file(CONF, true);
|
||||
$options['main']['sizelimit'] = 10;
|
||||
$options['traffic']['limit'] = 0;
|
||||
if (!is_file($this->_conf . '.bak') && is_file($this->_conf))
|
||||
rename($this->_conf, $this->_conf . '.bak');
|
||||
helper::createIniFile($this->_conf, $options);
|
||||
$_POST = self::$paste;
|
||||
helper::confBackup();
|
||||
helper::createIniFile(CONF, $options);
|
||||
$_POST = helper::getPaste();
|
||||
$_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
|
||||
$_SERVER['REQUEST_METHOD'] = 'POST';
|
||||
$_SERVER['REMOTE_ADDR'] = '::1';
|
||||
ob_start();
|
||||
new zerobin;
|
||||
$content = ob_get_contents();
|
||||
$response = json_decode($content, true);
|
||||
$this->assertEquals(1, $response['status'], 'outputs error status');
|
||||
$this->assertFalse($this->_model->exists(self::$pasteid), 'paste exists after posting data');
|
||||
$this->assertFalse($this->_model->exists(helper::getPasteId()), 'paste exists after posting data');
|
||||
}
|
||||
|
||||
/**
|
||||
* @runInSeparateProcess
|
||||
*/
|
||||
public function testCreateProxyHeader()
|
||||
{
|
||||
$this->reset();
|
||||
$options = parse_ini_file(CONF, true);
|
||||
$options['traffic']['header'] = 'X_FORWARDED_FOR';
|
||||
$options['traffic']['limit'] = 100;
|
||||
helper::confBackup();
|
||||
helper::createIniFile(CONF, $options);
|
||||
$_POST = helper::getPaste();
|
||||
$_SERVER['HTTP_X_FORWARDED_FOR'] = '::1';
|
||||
$_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
|
||||
$_SERVER['REQUEST_METHOD'] = 'POST';
|
||||
ob_start();
|
||||
new zerobin;
|
||||
$content = ob_get_contents();
|
||||
$response = json_decode($content, true);
|
||||
$this->assertEquals(1, $response['status'], 'outputs error status');
|
||||
$this->assertFalse($this->_model->exists(helper::getPasteId()), 'paste exists after posting data');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -190,20 +205,21 @@ class zerobinTest extends PHPUnit_Framework_TestCase
|
||||
public function testCreateDuplicateId()
|
||||
{
|
||||
$this->reset();
|
||||
$options = parse_ini_file($this->_conf, true);
|
||||
$options = parse_ini_file(CONF, true);
|
||||
$options['traffic']['limit'] = 0;
|
||||
if (!is_file($this->_conf . '.bak') && is_file($this->_conf))
|
||||
rename($this->_conf, $this->_conf . '.bak');
|
||||
helper::createIniFile($this->_conf, $options);
|
||||
$this->_model->create(self::$pasteid, self::$paste);
|
||||
$_POST = self::$paste;
|
||||
helper::confBackup();
|
||||
helper::createIniFile(CONF, $options);
|
||||
$this->_model->create(helper::getPasteId(), helper::getPaste());
|
||||
$_POST = helper::getPaste();
|
||||
$_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
|
||||
$_SERVER['REQUEST_METHOD'] = 'POST';
|
||||
$_SERVER['REMOTE_ADDR'] = '::1';
|
||||
ob_start();
|
||||
new zerobin;
|
||||
$content = ob_get_contents();
|
||||
$response = json_decode($content, true);
|
||||
$this->assertEquals(1, $response['status'], 'outputs error status');
|
||||
$this->assertTrue($this->_model->exists(self::$pasteid), 'paste exists after posting data');
|
||||
$this->assertTrue($this->_model->exists(helper::getPasteId()), 'paste exists after posting data');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -212,13 +228,15 @@ class zerobinTest extends PHPUnit_Framework_TestCase
|
||||
public function testCreateValidExpire()
|
||||
{
|
||||
$this->reset();
|
||||
$options = parse_ini_file($this->_conf, true);
|
||||
$options = parse_ini_file(CONF, true);
|
||||
$options['traffic']['limit'] = 0;
|
||||
if (!is_file($this->_conf . '.bak') && is_file($this->_conf))
|
||||
rename($this->_conf, $this->_conf . '.bak');
|
||||
helper::createIniFile($this->_conf, $options);
|
||||
$_POST = self::$paste;
|
||||
helper::confBackup();
|
||||
helper::createIniFile(CONF, $options);
|
||||
$_POST = helper::getPaste();
|
||||
$_POST['expire'] = '5min';
|
||||
$_POST['formatter'] = 'foo';
|
||||
$_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
|
||||
$_SERVER['REQUEST_METHOD'] = 'POST';
|
||||
$_SERVER['REMOTE_ADDR'] = '::1';
|
||||
ob_start();
|
||||
new zerobin;
|
||||
@@ -231,6 +249,40 @@ class zerobinTest extends PHPUnit_Framework_TestCase
|
||||
'outputs valid delete token'
|
||||
);
|
||||
$this->assertTrue($this->_model->exists($response['id']), 'paste exists after posting data');
|
||||
$paste = $this->_model->read($response['id']);
|
||||
$this->assertEquals(time() + 300, $paste->meta->expire_date, 'time is set correctly');
|
||||
}
|
||||
|
||||
/**
|
||||
* @runInSeparateProcess
|
||||
*/
|
||||
public function testCreateValidExpireWithDiscussion()
|
||||
{
|
||||
$this->reset();
|
||||
$options = parse_ini_file(CONF, true);
|
||||
$options['traffic']['limit'] = 0;
|
||||
helper::confBackup();
|
||||
helper::createIniFile(CONF, $options);
|
||||
$_POST = helper::getPaste();
|
||||
$_POST['expire'] = '5min';
|
||||
$_POST['opendiscussion'] = '1';
|
||||
$_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
|
||||
$_SERVER['REQUEST_METHOD'] = 'POST';
|
||||
$_SERVER['REMOTE_ADDR'] = '::1';
|
||||
ob_start();
|
||||
new zerobin;
|
||||
$content = ob_get_contents();
|
||||
$response = json_decode($content, true);
|
||||
$this->assertEquals(0, $response['status'], 'outputs status');
|
||||
$this->assertEquals(
|
||||
hash_hmac('sha1', $response['id'], serversalt::get()),
|
||||
$response['deletetoken'],
|
||||
'outputs valid delete token'
|
||||
);
|
||||
$this->assertTrue($this->_model->exists($response['id']), 'paste exists after posting data');
|
||||
$paste = $this->_model->read($response['id']);
|
||||
$this->assertEquals(time() + 300, $paste->meta->expire_date, 'time is set correctly');
|
||||
$this->assertEquals(1, $paste->meta->opendiscussion, 'time is set correctly');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -239,13 +291,14 @@ class zerobinTest extends PHPUnit_Framework_TestCase
|
||||
public function testCreateInvalidExpire()
|
||||
{
|
||||
$this->reset();
|
||||
$options = parse_ini_file($this->_conf, true);
|
||||
$options = parse_ini_file(CONF, true);
|
||||
$options['traffic']['limit'] = 0;
|
||||
if (!is_file($this->_conf . '.bak') && is_file($this->_conf))
|
||||
rename($this->_conf, $this->_conf . '.bak');
|
||||
helper::createIniFile($this->_conf, $options);
|
||||
$_POST = self::$paste;
|
||||
helper::confBackup();
|
||||
helper::createIniFile(CONF, $options);
|
||||
$_POST = helper::getPaste();
|
||||
$_POST['expire'] = 'foo';
|
||||
$_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
|
||||
$_SERVER['REQUEST_METHOD'] = 'POST';
|
||||
$_SERVER['REMOTE_ADDR'] = '::1';
|
||||
ob_start();
|
||||
new zerobin;
|
||||
@@ -266,20 +319,21 @@ class zerobinTest extends PHPUnit_Framework_TestCase
|
||||
public function testCreateInvalidBurn()
|
||||
{
|
||||
$this->reset();
|
||||
$options = parse_ini_file($this->_conf, true);
|
||||
$options = parse_ini_file(CONF, true);
|
||||
$options['traffic']['limit'] = 0;
|
||||
if (!is_file($this->_conf . '.bak') && is_file($this->_conf))
|
||||
rename($this->_conf, $this->_conf . '.bak');
|
||||
helper::createIniFile($this->_conf, $options);
|
||||
$_POST = self::$paste;
|
||||
helper::confBackup();
|
||||
helper::createIniFile(CONF, $options);
|
||||
$_POST = helper::getPaste();
|
||||
$_POST['burnafterreading'] = 'neither 1 nor 0';
|
||||
$_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
|
||||
$_SERVER['REQUEST_METHOD'] = 'POST';
|
||||
$_SERVER['REMOTE_ADDR'] = '::1';
|
||||
ob_start();
|
||||
new zerobin;
|
||||
$content = ob_get_contents();
|
||||
$response = json_decode($content, true);
|
||||
$this->assertEquals(1, $response['status'], 'outputs error status');
|
||||
$this->assertFalse($this->_model->exists(self::$pasteid), 'paste exists after posting data');
|
||||
$this->assertFalse($this->_model->exists(helper::getPasteId()), 'paste exists after posting data');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -288,20 +342,55 @@ class zerobinTest extends PHPUnit_Framework_TestCase
|
||||
public function testCreateInvalidOpenDiscussion()
|
||||
{
|
||||
$this->reset();
|
||||
$options = parse_ini_file($this->_conf, true);
|
||||
$options = parse_ini_file(CONF, true);
|
||||
$options['traffic']['limit'] = 0;
|
||||
if (!is_file($this->_conf . '.bak') && is_file($this->_conf))
|
||||
rename($this->_conf, $this->_conf . '.bak');
|
||||
helper::createIniFile($this->_conf, $options);
|
||||
$_POST = self::$paste;
|
||||
helper::confBackup();
|
||||
helper::createIniFile(CONF, $options);
|
||||
$_POST = helper::getPaste();
|
||||
$_POST['opendiscussion'] = 'neither 1 nor 0';
|
||||
$_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
|
||||
$_SERVER['REQUEST_METHOD'] = 'POST';
|
||||
$_SERVER['REMOTE_ADDR'] = '::1';
|
||||
ob_start();
|
||||
new zerobin;
|
||||
$content = ob_get_contents();
|
||||
$response = json_decode($content, true);
|
||||
$this->assertEquals(1, $response['status'], 'outputs error status');
|
||||
$this->assertFalse($this->_model->exists(self::$pasteid), 'paste exists after posting data');
|
||||
$this->assertFalse($this->_model->exists(helper::getPasteId()), 'paste exists after posting data');
|
||||
}
|
||||
|
||||
/**
|
||||
* @runInSeparateProcess
|
||||
*/
|
||||
public function testCreateAttachment()
|
||||
{
|
||||
$this->reset();
|
||||
$options = parse_ini_file(CONF, true);
|
||||
$options['traffic']['limit'] = 0;
|
||||
$options['main']['fileupload'] = true;
|
||||
helper::confBackup();
|
||||
helper::createIniFile(CONF, $options);
|
||||
$_POST = helper::getPasteWithAttachment();
|
||||
$_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
|
||||
$_SERVER['REQUEST_METHOD'] = 'POST';
|
||||
$_SERVER['REMOTE_ADDR'] = '::1';
|
||||
$this->assertFalse($this->_model->exists(helper::getPasteId()), 'paste does not exists before posting data');
|
||||
ob_start();
|
||||
new zerobin;
|
||||
$content = ob_get_contents();
|
||||
$response = json_decode($content, true);
|
||||
$this->assertEquals(0, $response['status'], 'outputs status');
|
||||
$this->assertEquals(
|
||||
hash_hmac('sha1', $response['id'], serversalt::get()),
|
||||
$response['deletetoken'],
|
||||
'outputs valid delete token'
|
||||
);
|
||||
$this->assertTrue($this->_model->exists($response['id']), 'paste exists after posting data');
|
||||
$original = json_decode(json_encode($_POST));
|
||||
$stored = $this->_model->read($response['id']);
|
||||
foreach (array('data', 'attachment', 'attachmentname') as $key) {
|
||||
$this->assertEquals($original->$key, $stored->$key);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -310,13 +399,14 @@ class zerobinTest extends PHPUnit_Framework_TestCase
|
||||
public function testCreateValidNick()
|
||||
{
|
||||
$this->reset();
|
||||
$options = parse_ini_file($this->_conf, true);
|
||||
$options = parse_ini_file(CONF, true);
|
||||
$options['traffic']['limit'] = 0;
|
||||
if (!is_file($this->_conf . '.bak') && is_file($this->_conf))
|
||||
rename($this->_conf, $this->_conf . '.bak');
|
||||
helper::createIniFile($this->_conf, $options);
|
||||
$_POST = self::$paste;
|
||||
$_POST['nickname'] = self::$comment['meta']['nickname'];
|
||||
helper::confBackup();
|
||||
helper::createIniFile(CONF, $options);
|
||||
$_POST = helper::getPaste();
|
||||
$_POST['nickname'] = helper::getComment()['meta']['nickname'];
|
||||
$_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
|
||||
$_SERVER['REQUEST_METHOD'] = 'POST';
|
||||
$_SERVER['REMOTE_ADDR'] = '::1';
|
||||
ob_start();
|
||||
new zerobin;
|
||||
@@ -337,20 +427,24 @@ class zerobinTest extends PHPUnit_Framework_TestCase
|
||||
public function testCreateInvalidNick()
|
||||
{
|
||||
$this->reset();
|
||||
$options = parse_ini_file($this->_conf, true);
|
||||
$options = parse_ini_file(CONF, true);
|
||||
$options['traffic']['limit'] = 0;
|
||||
if (!is_file($this->_conf . '.bak') && is_file($this->_conf))
|
||||
rename($this->_conf, $this->_conf . '.bak');
|
||||
helper::createIniFile($this->_conf, $options);
|
||||
$_POST = self::$paste;
|
||||
helper::confBackup();
|
||||
helper::createIniFile(CONF, $options);
|
||||
$_POST = helper::getCommentPost();
|
||||
$_POST['pasteid'] = helper::getPasteId();
|
||||
$_POST['parentid'] = helper::getPasteId();
|
||||
$_POST['nickname'] = 'foo';
|
||||
$_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
|
||||
$_SERVER['REQUEST_METHOD'] = 'POST';
|
||||
$_SERVER['REMOTE_ADDR'] = '::1';
|
||||
$this->_model->create(helper::getPasteId(), helper::getPaste());
|
||||
ob_start();
|
||||
new zerobin;
|
||||
$content = ob_get_contents();
|
||||
$response = json_decode($content, true);
|
||||
$this->assertEquals(1, $response['status'], 'outputs error status');
|
||||
$this->assertFalse($this->_model->exists(self::$pasteid), 'paste exists after posting data');
|
||||
$this->assertTrue($this->_model->exists(helper::getPasteId()), 'paste exists after posting data');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -359,22 +453,23 @@ class zerobinTest extends PHPUnit_Framework_TestCase
|
||||
public function testCreateComment()
|
||||
{
|
||||
$this->reset();
|
||||
$options = parse_ini_file($this->_conf, true);
|
||||
$options = parse_ini_file(CONF, true);
|
||||
$options['traffic']['limit'] = 0;
|
||||
if (!is_file($this->_conf . '.bak') && is_file($this->_conf))
|
||||
rename($this->_conf, $this->_conf . '.bak');
|
||||
helper::createIniFile($this->_conf, $options);
|
||||
$_POST = self::$comment;
|
||||
$_POST['pasteid'] = self::$pasteid;
|
||||
$_POST['parentid'] = self::$pasteid;
|
||||
helper::confBackup();
|
||||
helper::createIniFile(CONF, $options);
|
||||
$_POST = helper::getCommentPost();
|
||||
$_POST['pasteid'] = helper::getPasteId();
|
||||
$_POST['parentid'] = helper::getPasteId();
|
||||
$_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
|
||||
$_SERVER['REQUEST_METHOD'] = 'POST';
|
||||
$_SERVER['REMOTE_ADDR'] = '::1';
|
||||
$this->_model->create(self::$pasteid, self::$paste);
|
||||
$this->_model->create(helper::getPasteId(), helper::getPaste());
|
||||
ob_start();
|
||||
new zerobin;
|
||||
$content = ob_get_contents();
|
||||
$response = json_decode($content, true);
|
||||
$this->assertEquals(0, $response['status'], 'outputs status');
|
||||
$this->assertTrue($this->_model->existsComment(self::$pasteid, self::$pasteid, $response['id']), 'paste exists after posting data');
|
||||
$this->assertTrue($this->_model->existsComment(helper::getPasteId(), helper::getPasteId(), $response['id']), 'paste exists after posting data');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -383,22 +478,23 @@ class zerobinTest extends PHPUnit_Framework_TestCase
|
||||
public function testCreateInvalidComment()
|
||||
{
|
||||
$this->reset();
|
||||
$options = parse_ini_file($this->_conf, true);
|
||||
$options = parse_ini_file(CONF, true);
|
||||
$options['traffic']['limit'] = 0;
|
||||
if (!is_file($this->_conf . '.bak') && is_file($this->_conf))
|
||||
rename($this->_conf, $this->_conf . '.bak');
|
||||
helper::createIniFile($this->_conf, $options);
|
||||
$_POST = self::$comment;
|
||||
$_POST['pasteid'] = self::$pasteid;
|
||||
helper::confBackup();
|
||||
helper::createIniFile(CONF, $options);
|
||||
$_POST = helper::getCommentPost();
|
||||
$_POST['pasteid'] = helper::getPasteId();
|
||||
$_POST['parentid'] = 'foo';
|
||||
$_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
|
||||
$_SERVER['REQUEST_METHOD'] = 'POST';
|
||||
$_SERVER['REMOTE_ADDR'] = '::1';
|
||||
$this->_model->create(self::$pasteid, self::$paste);
|
||||
$this->_model->create(helper::getPasteId(), helper::getPaste());
|
||||
ob_start();
|
||||
new zerobin;
|
||||
$content = ob_get_contents();
|
||||
$response = json_decode($content, true);
|
||||
$this->assertEquals(1, $response['status'], 'outputs error status');
|
||||
$this->assertFalse($this->_model->existsComment(self::$pasteid, self::$pasteid, self::$commentid), 'paste exists after posting data');
|
||||
$this->assertFalse($this->_model->existsComment(helper::getPasteId(), helper::getPasteId(), helper::getCommentId()), 'paste exists after posting data');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -407,24 +503,24 @@ class zerobinTest extends PHPUnit_Framework_TestCase
|
||||
public function testCreateCommentDiscussionDisabled()
|
||||
{
|
||||
$this->reset();
|
||||
$options = parse_ini_file($this->_conf, true);
|
||||
$options = parse_ini_file(CONF, true);
|
||||
$options['traffic']['limit'] = 0;
|
||||
if (!is_file($this->_conf . '.bak') && is_file($this->_conf))
|
||||
rename($this->_conf, $this->_conf . '.bak');
|
||||
helper::createIniFile($this->_conf, $options);
|
||||
$_POST = self::$comment;
|
||||
$_POST['pasteid'] = self::$pasteid;
|
||||
$_POST['parentid'] = self::$pasteid;
|
||||
helper::confBackup();
|
||||
helper::createIniFile(CONF, $options);
|
||||
$_POST = helper::getCommentPost();
|
||||
$_POST['pasteid'] = helper::getPasteId();
|
||||
$_POST['parentid'] = helper::getPasteId();
|
||||
$_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
|
||||
$_SERVER['REQUEST_METHOD'] = 'POST';
|
||||
$_SERVER['REMOTE_ADDR'] = '::1';
|
||||
$paste = self::$paste;
|
||||
$paste['meta']['opendiscussion'] = false;
|
||||
$this->_model->create(self::$pasteid, $paste);
|
||||
$paste = helper::getPaste(array('opendiscussion' => false));
|
||||
$this->_model->create(helper::getPasteId(), $paste);
|
||||
ob_start();
|
||||
new zerobin;
|
||||
$content = ob_get_contents();
|
||||
$response = json_decode($content, true);
|
||||
$this->assertEquals(1, $response['status'], 'outputs error status');
|
||||
$this->assertFalse($this->_model->existsComment(self::$pasteid, self::$pasteid, self::$commentid), 'paste exists after posting data');
|
||||
$this->assertFalse($this->_model->existsComment(helper::getPasteId(), helper::getPasteId(), helper::getCommentId()), 'paste exists after posting data');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -433,21 +529,22 @@ class zerobinTest extends PHPUnit_Framework_TestCase
|
||||
public function testCreateCommentInvalidPaste()
|
||||
{
|
||||
$this->reset();
|
||||
$options = parse_ini_file($this->_conf, true);
|
||||
$options = parse_ini_file(CONF, true);
|
||||
$options['traffic']['limit'] = 0;
|
||||
if (!is_file($this->_conf . '.bak') && is_file($this->_conf))
|
||||
rename($this->_conf, $this->_conf . '.bak');
|
||||
helper::createIniFile($this->_conf, $options);
|
||||
$_POST = self::$comment;
|
||||
$_POST['pasteid'] = self::$pasteid;
|
||||
$_POST['parentid'] = self::$pasteid;
|
||||
helper::confBackup();
|
||||
helper::createIniFile(CONF, $options);
|
||||
$_POST = helper::getCommentPost();
|
||||
$_POST['pasteid'] = helper::getPasteId();
|
||||
$_POST['parentid'] = helper::getPasteId();
|
||||
$_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
|
||||
$_SERVER['REQUEST_METHOD'] = 'POST';
|
||||
$_SERVER['REMOTE_ADDR'] = '::1';
|
||||
ob_start();
|
||||
new zerobin;
|
||||
$content = ob_get_contents();
|
||||
$response = json_decode($content, true);
|
||||
$this->assertEquals(1, $response['status'], 'outputs error status');
|
||||
$this->assertFalse($this->_model->existsComment(self::$pasteid, self::$pasteid, self::$commentid), 'paste exists after posting data');
|
||||
$this->assertFalse($this->_model->existsComment(helper::getPasteId(), helper::getPasteId(), helper::getCommentId()), 'paste exists after posting data');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -456,22 +553,25 @@ class zerobinTest extends PHPUnit_Framework_TestCase
|
||||
public function testCreateDuplicateComment()
|
||||
{
|
||||
$this->reset();
|
||||
$options = parse_ini_file($this->_conf, true);
|
||||
$options = parse_ini_file(CONF, true);
|
||||
$options['traffic']['limit'] = 0;
|
||||
if (!is_file($this->_conf . '.bak') && is_file($this->_conf))
|
||||
rename($this->_conf, $this->_conf . '.bak');
|
||||
helper::createIniFile($this->_conf, $options);
|
||||
$this->_model->createComment(self::$pasteid, self::$pasteid, self::$commentid, self::$comment);
|
||||
$_POST = self::$comment;
|
||||
$_POST['pasteid'] = self::$pasteid;
|
||||
$_POST['parentid'] = self::$pasteid;
|
||||
helper::confBackup();
|
||||
helper::createIniFile(CONF, $options);
|
||||
$this->_model->create(helper::getPasteId(), helper::getPaste());
|
||||
$this->_model->createComment(helper::getPasteId(), helper::getPasteId(), helper::getCommentId(), helper::getComment());
|
||||
$this->assertTrue($this->_model->existsComment(helper::getPasteId(), helper::getPasteId(), helper::getCommentId()), 'comment exists before posting data');
|
||||
$_POST = helper::getCommentPost();
|
||||
$_POST['pasteid'] = helper::getPasteId();
|
||||
$_POST['parentid'] = helper::getPasteId();
|
||||
$_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
|
||||
$_SERVER['REQUEST_METHOD'] = 'POST';
|
||||
$_SERVER['REMOTE_ADDR'] = '::1';
|
||||
ob_start();
|
||||
new zerobin;
|
||||
$content = ob_get_contents();
|
||||
$response = json_decode($content, true);
|
||||
$this->assertEquals(1, $response['status'], 'outputs error status');
|
||||
$this->assertTrue($this->_model->existsComment(self::$pasteid, self::$pasteid, self::$commentid), 'paste exists after posting data');
|
||||
$this->assertTrue($this->_model->existsComment(helper::getPasteId(), helper::getPasteId(), helper::getCommentId()), 'paste exists after posting data');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -480,15 +580,15 @@ class zerobinTest extends PHPUnit_Framework_TestCase
|
||||
public function testRead()
|
||||
{
|
||||
$this->reset();
|
||||
$this->_model->create(self::$pasteid, self::$paste);
|
||||
$_SERVER['QUERY_STRING'] = self::$pasteid;
|
||||
$this->_model->create(helper::getPasteId(), helper::getPaste());
|
||||
$_SERVER['QUERY_STRING'] = helper::getPasteId();
|
||||
ob_start();
|
||||
new zerobin;
|
||||
$content = ob_get_contents();
|
||||
$this->assertTag(
|
||||
array(
|
||||
'id' => 'cipherdata',
|
||||
'content' => htmlspecialchars(json_encode(self::$paste), ENT_NOQUOTES)
|
||||
'content' => htmlspecialchars(helper::getPasteAsJson(), ENT_NOQUOTES)
|
||||
),
|
||||
$content,
|
||||
'outputs data correctly'
|
||||
@@ -521,7 +621,7 @@ class zerobinTest extends PHPUnit_Framework_TestCase
|
||||
public function testReadNonexisting()
|
||||
{
|
||||
$this->reset();
|
||||
$_SERVER['QUERY_STRING'] = self::$pasteid;
|
||||
$_SERVER['QUERY_STRING'] = helper::getPasteId();
|
||||
ob_start();
|
||||
new zerobin;
|
||||
$content = ob_get_contents();
|
||||
@@ -541,10 +641,9 @@ class zerobinTest extends PHPUnit_Framework_TestCase
|
||||
public function testReadExpired()
|
||||
{
|
||||
$this->reset();
|
||||
$expiredPaste = self::$paste;
|
||||
$expiredPaste['meta']['expire_date'] = $expiredPaste['meta']['postdate'];
|
||||
$this->_model->create(self::$pasteid, $expiredPaste);
|
||||
$_SERVER['QUERY_STRING'] = self::$pasteid;
|
||||
$expiredPaste = helper::getPaste(array('expire_date' => 1344803344));
|
||||
$this->_model->create(helper::getPasteId(), $expiredPaste);
|
||||
$_SERVER['QUERY_STRING'] = helper::getPasteId();
|
||||
ob_start();
|
||||
new zerobin;
|
||||
$content = ob_get_contents();
|
||||
@@ -564,17 +663,16 @@ class zerobinTest extends PHPUnit_Framework_TestCase
|
||||
public function testReadBurn()
|
||||
{
|
||||
$this->reset();
|
||||
$burnPaste = self::$paste;
|
||||
$burnPaste['meta']['burnafterreading'] = true;
|
||||
$this->_model->create(self::$pasteid, $burnPaste);
|
||||
$_SERVER['QUERY_STRING'] = self::$pasteid;
|
||||
$burnPaste = helper::getPaste(array('burnafterreading' => true));
|
||||
$this->_model->create(helper::getPasteId(), $burnPaste);
|
||||
$_SERVER['QUERY_STRING'] = helper::getPasteId();
|
||||
ob_start();
|
||||
new zerobin;
|
||||
$content = ob_get_contents();
|
||||
$this->assertTag(
|
||||
array(
|
||||
'id' => 'cipherdata',
|
||||
'content' => htmlspecialchars(json_encode($burnPaste), ENT_NOQUOTES)
|
||||
'content' => htmlspecialchars(helper::getPasteAsJson($burnPaste['meta']), ENT_NOQUOTES)
|
||||
),
|
||||
$content,
|
||||
'outputs data correctly'
|
||||
@@ -587,14 +685,23 @@ class zerobinTest extends PHPUnit_Framework_TestCase
|
||||
public function testReadJson()
|
||||
{
|
||||
$this->reset();
|
||||
$this->_model->create(self::$pasteid, self::$paste);
|
||||
$_SERVER['QUERY_STRING'] = self::$pasteid . '&json';
|
||||
$paste = helper::getPaste();
|
||||
$this->_model->create(helper::getPasteId(), $paste);
|
||||
$_SERVER['QUERY_STRING'] = helper::getPasteId();
|
||||
$_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
|
||||
ob_start();
|
||||
new zerobin;
|
||||
$content = ob_get_contents();
|
||||
$response = json_decode($content, true);
|
||||
$this->assertEquals(0, $response['status'], 'outputs success status');
|
||||
$this->assertEquals(array(self::$paste), $response['messages'], 'outputs data correctly');
|
||||
$this->assertEquals(helper::getPasteId(), $response['id'], 'outputs data correctly');
|
||||
$this->assertStringEndsWith('?' . $response['id'], $response['url'], 'returned URL points to new paste');
|
||||
$this->assertEquals($paste['data'], $response['data'], 'outputs data correctly');
|
||||
$this->assertEquals($paste['meta']['formatter'], $response['meta']['formatter'], 'outputs format correctly');
|
||||
$this->assertEquals($paste['meta']['postdate'], $response['meta']['postdate'], 'outputs postdate correctly');
|
||||
$this->assertEquals($paste['meta']['opendiscussion'], $response['meta']['opendiscussion'], 'outputs opendiscussion correctly');
|
||||
$this->assertEquals(0, $response['comment_count'], 'outputs comment_count correctly');
|
||||
$this->assertEquals(0, $response['comment_offset'], 'outputs comment_offset correctly');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -603,7 +710,8 @@ class zerobinTest extends PHPUnit_Framework_TestCase
|
||||
public function testReadInvalidJson()
|
||||
{
|
||||
$this->reset();
|
||||
$_SERVER['QUERY_STRING'] = self::$pasteid . '&json';
|
||||
$_SERVER['QUERY_STRING'] = helper::getPasteId();
|
||||
$_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
|
||||
ob_start();
|
||||
new zerobin;
|
||||
$content = ob_get_contents();
|
||||
@@ -611,16 +719,69 @@ class zerobinTest extends PHPUnit_Framework_TestCase
|
||||
$this->assertEquals(1, $response['status'], 'outputs error status');
|
||||
}
|
||||
|
||||
/**
|
||||
* @runInSeparateProcess
|
||||
*/
|
||||
public function testReadOldSyntax()
|
||||
{
|
||||
$this->reset();
|
||||
$oldPaste = helper::getPaste();
|
||||
$meta = array(
|
||||
'syntaxcoloring' => true,
|
||||
'postdate' => $oldPaste['meta']['postdate'],
|
||||
'opendiscussion' => $oldPaste['meta']['opendiscussion'],
|
||||
);
|
||||
$oldPaste['meta'] = $meta;
|
||||
$this->_model->create(helper::getPasteId(), $oldPaste);
|
||||
$_SERVER['QUERY_STRING'] = helper::getPasteId();
|
||||
ob_start();
|
||||
new zerobin;
|
||||
$content = ob_get_contents();
|
||||
$meta['formatter'] = 'syntaxhighlighting';
|
||||
$this->assertTag(
|
||||
array(
|
||||
'id' => 'cipherdata',
|
||||
'content' => htmlspecialchars(helper::getPasteAsJson($meta), ENT_NOQUOTES)
|
||||
),
|
||||
$content,
|
||||
'outputs data correctly'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @runInSeparateProcess
|
||||
*/
|
||||
public function testReadOldFormat()
|
||||
{
|
||||
$this->reset();
|
||||
$oldPaste = helper::getPaste();
|
||||
unset($oldPaste['meta']['formatter']);
|
||||
$this->_model->create(helper::getPasteId(), $oldPaste);
|
||||
$_SERVER['QUERY_STRING'] = helper::getPasteId();
|
||||
ob_start();
|
||||
new zerobin;
|
||||
$content = ob_get_contents();
|
||||
$oldPaste['meta']['formatter'] = 'plaintext';
|
||||
$this->assertTag(
|
||||
array(
|
||||
'id' => 'cipherdata',
|
||||
'content' => htmlspecialchars(helper::getPasteAsJson($oldPaste['meta']), ENT_NOQUOTES)
|
||||
),
|
||||
$content,
|
||||
'outputs data correctly'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @runInSeparateProcess
|
||||
*/
|
||||
public function testDelete()
|
||||
{
|
||||
$this->reset();
|
||||
$this->_model->create(self::$pasteid, self::$paste);
|
||||
$this->assertTrue($this->_model->exists(self::$pasteid), 'paste exists before deleting data');
|
||||
$_GET['pasteid'] = self::$pasteid;
|
||||
$_GET['deletetoken'] = hash_hmac('sha1', self::$pasteid, serversalt::get());
|
||||
$this->_model->create(helper::getPasteId(), helper::getPaste());
|
||||
$this->assertTrue($this->_model->exists(helper::getPasteId()), 'paste exists before deleting data');
|
||||
$_GET['pasteid'] = helper::getPasteId();
|
||||
$_GET['deletetoken'] = hash_hmac('sha1', helper::getPasteId(), serversalt::get());
|
||||
ob_start();
|
||||
new zerobin;
|
||||
$content = ob_get_contents();
|
||||
@@ -632,7 +793,7 @@ class zerobinTest extends PHPUnit_Framework_TestCase
|
||||
$content,
|
||||
'outputs deleted status correctly'
|
||||
);
|
||||
$this->assertFalse($this->_model->exists(self::$pasteid), 'paste successfully deleted');
|
||||
$this->assertFalse($this->_model->exists(helper::getPasteId()), 'paste successfully deleted');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -641,7 +802,7 @@ class zerobinTest extends PHPUnit_Framework_TestCase
|
||||
public function testDeleteInvalidId()
|
||||
{
|
||||
$this->reset();
|
||||
$this->_model->create(self::$pasteid, self::$paste);
|
||||
$this->_model->create(helper::getPasteId(), helper::getPaste());
|
||||
$_GET['pasteid'] = 'foo';
|
||||
$_GET['deletetoken'] = 'bar';
|
||||
ob_start();
|
||||
@@ -655,7 +816,7 @@ class zerobinTest extends PHPUnit_Framework_TestCase
|
||||
$content,
|
||||
'outputs delete error correctly'
|
||||
);
|
||||
$this->assertTrue($this->_model->exists(self::$pasteid), 'paste exists after failing to delete data');
|
||||
$this->assertTrue($this->_model->exists(helper::getPasteId()), 'paste exists after failing to delete data');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -664,7 +825,7 @@ class zerobinTest extends PHPUnit_Framework_TestCase
|
||||
public function testDeleteInexistantId()
|
||||
{
|
||||
$this->reset();
|
||||
$_GET['pasteid'] = self::$pasteid;
|
||||
$_GET['pasteid'] = helper::getPasteId();
|
||||
$_GET['deletetoken'] = 'bar';
|
||||
ob_start();
|
||||
new zerobin;
|
||||
@@ -685,8 +846,8 @@ class zerobinTest extends PHPUnit_Framework_TestCase
|
||||
public function testDeleteInvalidToken()
|
||||
{
|
||||
$this->reset();
|
||||
$this->_model->create(self::$pasteid, self::$paste);
|
||||
$_GET['pasteid'] = self::$pasteid;
|
||||
$this->_model->create(helper::getPasteId(), helper::getPaste());
|
||||
$_GET['pasteid'] = helper::getPasteId();
|
||||
$_GET['deletetoken'] = 'bar';
|
||||
ob_start();
|
||||
new zerobin;
|
||||
@@ -699,7 +860,7 @@ class zerobinTest extends PHPUnit_Framework_TestCase
|
||||
$content,
|
||||
'outputs delete error correctly'
|
||||
);
|
||||
$this->assertTrue($this->_model->exists(self::$pasteid), 'paste exists after failing to delete data');
|
||||
$this->assertTrue($this->_model->exists(helper::getPasteId()), 'paste exists after failing to delete data');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -708,18 +869,19 @@ class zerobinTest extends PHPUnit_Framework_TestCase
|
||||
public function testDeleteBurnAfterReading()
|
||||
{
|
||||
$this->reset();
|
||||
$burnPaste = self::$paste;
|
||||
$burnPaste['meta']['burnafterreading'] = true;
|
||||
$this->_model->create(self::$pasteid, $burnPaste);
|
||||
$this->assertTrue($this->_model->exists(self::$pasteid), 'paste exists before deleting data');
|
||||
$_GET['pasteid'] = self::$pasteid;
|
||||
$_GET['deletetoken'] = 'burnafterreading';
|
||||
$burnPaste = helper::getPaste(array('burnafterreading' => true));
|
||||
$this->_model->create(helper::getPasteId(), $burnPaste);
|
||||
$this->assertTrue($this->_model->exists(helper::getPasteId()), 'paste exists before deleting data');
|
||||
$_POST['deletetoken'] = 'burnafterreading';
|
||||
$_SERVER['QUERY_STRING'] = helper::getPasteId();
|
||||
$_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
|
||||
$_SERVER['REQUEST_METHOD'] = 'POST';
|
||||
ob_start();
|
||||
new zerobin;
|
||||
$content = ob_get_contents();
|
||||
$response = json_decode($content, true);
|
||||
$this->assertEquals(0, $response['status'], 'outputs status');
|
||||
$this->assertFalse($this->_model->exists(self::$pasteid), 'paste successfully deleted');
|
||||
$this->assertFalse($this->_model->exists(helper::getPasteId()), 'paste successfully deleted');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -728,16 +890,18 @@ class zerobinTest extends PHPUnit_Framework_TestCase
|
||||
public function testDeleteInvalidBurnAfterReading()
|
||||
{
|
||||
$this->reset();
|
||||
$this->_model->create(self::$pasteid, self::$paste);
|
||||
$this->assertTrue($this->_model->exists(self::$pasteid), 'paste exists before deleting data');
|
||||
$_GET['pasteid'] = self::$pasteid;
|
||||
$_GET['deletetoken'] = 'burnafterreading';
|
||||
$this->_model->create(helper::getPasteId(), helper::getPaste());
|
||||
$this->assertTrue($this->_model->exists(helper::getPasteId()), 'paste exists before deleting data');
|
||||
$_POST['deletetoken'] = 'burnafterreading';
|
||||
$_SERVER['QUERY_STRING'] = helper::getPasteId();
|
||||
$_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
|
||||
$_SERVER['REQUEST_METHOD'] = 'POST';
|
||||
ob_start();
|
||||
new zerobin;
|
||||
$content = ob_get_contents();
|
||||
$response = json_decode($content, true);
|
||||
$this->assertEquals(1, $response['status'], 'outputs status');
|
||||
$this->assertTrue($this->_model->exists(self::$pasteid), 'paste successfully deleted');
|
||||
$this->assertTrue($this->_model->exists(helper::getPasteId()), 'paste successfully deleted');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -746,10 +910,12 @@ class zerobinTest extends PHPUnit_Framework_TestCase
|
||||
public function testDeleteExpired()
|
||||
{
|
||||
$this->reset();
|
||||
$expiredPaste = self::$paste;
|
||||
$expiredPaste['meta']['expire_date'] = $expiredPaste['meta']['postdate'];
|
||||
$this->_model->create(self::$pasteid, $expiredPaste);
|
||||
$_SERVER['QUERY_STRING'] = self::$pasteid;
|
||||
$expiredPaste = helper::getPaste(array('expire_date' => 1000));
|
||||
$this->assertFalse($this->_model->exists(helper::getPasteId()), 'paste does not exist before being created');
|
||||
$this->_model->create(helper::getPasteId(), $expiredPaste);
|
||||
$this->assertTrue($this->_model->exists(helper::getPasteId()), 'paste exists before deleting data');
|
||||
$_GET['pasteid'] = helper::getPasteId();
|
||||
$_GET['deletetoken'] = 'does not matter in this context, but has to be set';
|
||||
ob_start();
|
||||
new zerobin;
|
||||
$content = ob_get_contents();
|
||||
@@ -761,6 +927,6 @@ class zerobinTest extends PHPUnit_Framework_TestCase
|
||||
$content,
|
||||
'outputs error correctly'
|
||||
);
|
||||
$this->assertFalse($this->_model->exists(self::$pasteid), 'paste successfully deleted');
|
||||
$this->assertFalse($this->_model->exists(helper::getPasteId()), 'paste successfully deleted');
|
||||
}
|
||||
}
|
||||
@@ -1,28 +1,6 @@
|
||||
<?php
|
||||
class zerobin_dataTest extends PHPUnit_Framework_TestCase
|
||||
{
|
||||
private static $pasteid = '5e9bc25c89fb3bf9';
|
||||
|
||||
private static $paste = array(
|
||||
'data' => '{"iv":"EN39/wd5Nk8HAiSG2K5AsQ","v":1,"iter":1000,"ks":128,"ts":64,"mode":"ccm","adata":"","cipher":"aes","salt":"QKN1DBXe5PI","ct":"8hA83xDdXjD7K2qfmw5NdA"}',
|
||||
'meta' => array(
|
||||
'postdate' => 1344803344,
|
||||
'expire_date' => 1344803644,
|
||||
'opendiscussion' => true,
|
||||
),
|
||||
);
|
||||
|
||||
private static $commentid = '5a52eebf11c4c94b';
|
||||
|
||||
private static $comment = array(
|
||||
'data' => '{"iv":"Pd4pOKWkmDTT9uPwVwd5Ag","v":1,"iter":1000,"ks":128,"ts":64,"mode":"ccm","adata":"","cipher":"aes","salt":"ZIUhFTliVz4","ct":"6nOCU3peNDclDDpFtJEBKA"}',
|
||||
'meta' => array(
|
||||
'nickname' => '{"iv":"76MkAtOGC4oFogX/aSMxRA","v":1,"iter":1000,"ks":128,"ts":64,"mode":"ccm","adata":"","cipher":"aes","salt":"ZIUhFTliVz4","ct":"b6Ae/U1xJdsX/+lATud4sQ"}',
|
||||
'vizhash' => 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAABGUlEQVQokWOsl5/94983CNKQMjnxaOePf98MeKwPfNjkLZ3AgARab6b9+PeNEVnDj3/ff/z7ZiHnzsDA8Pv7H2TVPJw8EAYLAwb48OaVgIgYKycLsrYv378wMDB8//qdCVMDRA9EKSsnCwRBxNsepaLboMFlyMDAICAi9uHNK24GITQ/MDAwoNhgIGMLtwGrzegaLjw5jMz9+vUdnN17uwDCQDhJgk0O07yvX9+teDX1x79v6DYIsIjgcgMaYGFgYOBg4kJx2JejkAiBxAw+PzAwMNz4dp6wDXDw4MdNNOl0rWYsNkD89OLXI/xmo9sgzatJjAYmBgYGDiauD3/ePP18nVgb4MF89+M5ZX6js293wUMpnr8KTQMAxsCJnJ30apMAAAAASUVORK5CYII=',
|
||||
'postdate' => 1344803528,
|
||||
),
|
||||
);
|
||||
|
||||
private $_model;
|
||||
|
||||
private $_path;
|
||||
@@ -42,29 +20,47 @@ class zerobin_dataTest extends PHPUnit_Framework_TestCase
|
||||
|
||||
public function testFileBasedDataStoreWorks()
|
||||
{
|
||||
$this->_model->delete(helper::getPasteId());
|
||||
|
||||
// storing pastes
|
||||
$this->assertFalse($this->_model->exists(self::$pasteid), 'paste does not yet exist');
|
||||
$this->assertTrue($this->_model->create(self::$pasteid, self::$paste), 'store new paste');
|
||||
$this->assertTrue($this->_model->exists(self::$pasteid), 'paste exists after storing it');
|
||||
$this->assertFalse($this->_model->create(self::$pasteid, self::$paste), 'unable to store the same paste twice');
|
||||
$this->assertEquals(json_decode(json_encode(self::$paste)), $this->_model->read(self::$pasteid));
|
||||
$paste = helper::getPaste(array('expire_date' => 1344803344));
|
||||
$this->assertFalse($this->_model->exists(helper::getPasteId()), 'paste does not yet exist');
|
||||
$this->assertTrue($this->_model->create(helper::getPasteId(), $paste), 'store new paste');
|
||||
$this->assertTrue($this->_model->exists(helper::getPasteId()), 'paste exists after storing it');
|
||||
$this->assertFalse($this->_model->create(helper::getPasteId(), $paste), 'unable to store the same paste twice');
|
||||
$this->assertEquals(json_decode(json_encode($paste)), $this->_model->read(helper::getPasteId()));
|
||||
|
||||
// storing comments
|
||||
$this->assertFalse($this->_model->existsComment(self::$pasteid, self::$pasteid, self::$commentid), 'comment does not yet exist');
|
||||
$this->assertTrue($this->_model->createComment(self::$pasteid, self::$pasteid, self::$commentid, self::$comment) !== false, 'store comment');
|
||||
$this->assertTrue($this->_model->existsComment(self::$pasteid, self::$pasteid, self::$commentid), 'comment exists after storing it');
|
||||
$comment = json_decode(json_encode(self::$comment));
|
||||
$comment->meta->commentid = self::$commentid;
|
||||
$comment->meta->parentid = self::$pasteid;
|
||||
$this->assertFalse($this->_model->existsComment(helper::getPasteId(), helper::getPasteId(), helper::getCommentId()), 'comment does not yet exist');
|
||||
$this->assertTrue($this->_model->createComment(helper::getPasteId(), helper::getPasteId(), helper::getCommentId(), helper::getComment()) !== false, 'store comment');
|
||||
$this->assertTrue($this->_model->existsComment(helper::getPasteId(), helper::getPasteId(), helper::getCommentId()), 'comment exists after storing it');
|
||||
$comment = json_decode(json_encode(helper::getComment()));
|
||||
$comment->id = helper::getCommentId();
|
||||
$comment->parentid = helper::getPasteId();
|
||||
$this->assertEquals(
|
||||
array($comment->meta->postdate => $comment),
|
||||
$this->_model->readComments(self::$pasteid)
|
||||
$this->_model->readComments(helper::getPasteId())
|
||||
);
|
||||
|
||||
// deleting pastes
|
||||
$this->_model->delete(self::$pasteid);
|
||||
$this->assertFalse($this->_model->exists(self::$pasteid), 'paste successfully deleted');
|
||||
$this->assertFalse($this->_model->existsComment(self::$pasteid, self::$pasteid, self::$commentid), 'comment was deleted with paste');
|
||||
$this->assertFalse($this->_model->read(self::$pasteid), 'paste can no longer be found');
|
||||
$this->_model->delete(helper::getPasteId());
|
||||
$this->assertFalse($this->_model->exists(helper::getPasteId()), 'paste successfully deleted');
|
||||
$this->assertFalse($this->_model->existsComment(helper::getPasteId(), helper::getPasteId(), helper::getCommentId()), 'comment was deleted with paste');
|
||||
$this->assertFalse($this->_model->read(helper::getPasteId()), 'paste can no longer be found');
|
||||
}
|
||||
|
||||
public function testFileBasedAttachmentStoreWorks()
|
||||
{
|
||||
$this->_model->delete(helper::getPasteId());
|
||||
$original = $paste = helper::getPasteWithAttachment(array('expire_date' => 1344803344));
|
||||
$paste['meta']['attachment'] = $paste['attachment'];
|
||||
$paste['meta']['attachmentname'] = $paste['attachmentname'];
|
||||
unset($paste['attachment'], $paste['attachmentname']);
|
||||
$this->assertFalse($this->_model->exists(helper::getPasteId()), 'paste does not yet exist');
|
||||
$this->assertTrue($this->_model->create(helper::getPasteId(), $paste), 'store new paste');
|
||||
$this->assertTrue($this->_model->exists(helper::getPasteId()), 'paste exists after storing it');
|
||||
$this->assertFalse($this->_model->create(helper::getPasteId(), $paste), 'unable to store the same paste twice');
|
||||
$this->assertEquals(json_decode(json_encode($original)), $this->_model->read(helper::getPasteId()));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,69 +1,65 @@
|
||||
<?php
|
||||
class zerobin_dbTest extends PHPUnit_Framework_TestCase
|
||||
{
|
||||
private static $pasteid = '5e9bc25c89fb3bf9';
|
||||
|
||||
private static $paste = array(
|
||||
'data' => '{"iv":"EN39/wd5Nk8HAiSG2K5AsQ","v":1,"iter":1000,"ks":128,"ts":64,"mode":"ccm","adata":"","cipher":"aes","salt":"QKN1DBXe5PI","ct":"8hA83xDdXjD7K2qfmw5NdA"}',
|
||||
'meta' => array(
|
||||
'postdate' => 1344803344,
|
||||
'expire_date' => 1344803644,
|
||||
'opendiscussion' => true,
|
||||
),
|
||||
);
|
||||
|
||||
private static $commentid = '5a52eebf11c4c94b';
|
||||
|
||||
private static $comment = array(
|
||||
'data' => '{"iv":"Pd4pOKWkmDTT9uPwVwd5Ag","v":1,"iter":1000,"ks":128,"ts":64,"mode":"ccm","adata":"","cipher":"aes","salt":"ZIUhFTliVz4","ct":"6nOCU3peNDclDDpFtJEBKA"}',
|
||||
'meta' => array(
|
||||
'nickname' => '{"iv":"76MkAtOGC4oFogX/aSMxRA","v":1,"iter":1000,"ks":128,"ts":64,"mode":"ccm","adata":"","cipher":"aes","salt":"ZIUhFTliVz4","ct":"b6Ae/U1xJdsX/+lATud4sQ"}',
|
||||
'vizhash' => 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAABGUlEQVQokWOsl5/94983CNKQMjnxaOePf98MeKwPfNjkLZ3AgARab6b9+PeNEVnDj3/ff/z7ZiHnzsDA8Pv7H2TVPJw8EAYLAwb48OaVgIgYKycLsrYv378wMDB8//qdCVMDRA9EKSsnCwRBxNsepaLboMFlyMDAICAi9uHNK24GITQ/MDAwoNhgIGMLtwGrzegaLjw5jMz9+vUdnN17uwDCQDhJgk0O07yvX9+teDX1x79v6DYIsIjgcgMaYGFgYOBg4kJx2JejkAiBxAw+PzAwMNz4dp6wDXDw4MdNNOl0rWYsNkD89OLXI/xmo9sgzatJjAYmBgYGDiauD3/ePP18nVgb4MF89+M5ZX6js293wUMpnr8KTQMAxsCJnJ30apMAAAAASUVORK5CYII=',
|
||||
'postdate' => 1344803528,
|
||||
),
|
||||
);
|
||||
|
||||
private $_model;
|
||||
|
||||
private $_options = array(
|
||||
'dsn' => 'sqlite::memory:',
|
||||
'usr' => null,
|
||||
'pwd' => null,
|
||||
'opt' => array(PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION),
|
||||
);
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
/* Setup Routine */
|
||||
$this->_model = zerobin_db::getInstance(
|
||||
array(
|
||||
'dsn' => 'sqlite::memory:',
|
||||
'usr' => null,
|
||||
'pwd' => null,
|
||||
'opt' => array(PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION),
|
||||
)
|
||||
);
|
||||
$this->_model = zerobin_db::getInstance($this->_options);
|
||||
}
|
||||
|
||||
public function testDatabaseBasedDataStoreWorks()
|
||||
{
|
||||
$this->_model->delete(helper::getPasteId());
|
||||
|
||||
// storing pastes
|
||||
$this->assertFalse($this->_model->exists(self::$pasteid), 'paste does not yet exist');
|
||||
$this->assertTrue($this->_model->create(self::$pasteid, self::$paste), 'store new paste');
|
||||
$this->assertTrue($this->_model->exists(self::$pasteid), 'paste exists after storing it');
|
||||
$this->assertFalse($this->_model->create(self::$pasteid, self::$paste), 'unable to store the same paste twice');
|
||||
$this->assertEquals(json_decode(json_encode(self::$paste)), $this->_model->read(self::$pasteid));
|
||||
$paste = helper::getPaste(array('expire_date' => 1344803344));
|
||||
$this->assertFalse($this->_model->exists(helper::getPasteId()), 'paste does not yet exist');
|
||||
$this->assertTrue($this->_model->create(helper::getPasteId(), $paste), 'store new paste');
|
||||
$this->assertTrue($this->_model->exists(helper::getPasteId()), 'paste exists after storing it');
|
||||
$this->assertFalse($this->_model->create(helper::getPasteId(), $paste), 'unable to store the same paste twice');
|
||||
$this->assertEquals(json_decode(json_encode($paste)), $this->_model->read(helper::getPasteId()));
|
||||
|
||||
// storing comments
|
||||
$this->assertFalse($this->_model->existsComment(self::$pasteid, self::$pasteid, self::$commentid), 'comment does not yet exist');
|
||||
$this->assertTrue($this->_model->createComment(self::$pasteid, self::$pasteid, self::$commentid, self::$comment) !== false, 'store comment');
|
||||
$this->assertTrue($this->_model->existsComment(self::$pasteid, self::$pasteid, self::$commentid), 'comment exists after storing it');
|
||||
$comment = json_decode(json_encode(self::$comment));
|
||||
$comment->meta->commentid = self::$commentid;
|
||||
$comment->meta->parentid = self::$pasteid;
|
||||
$this->assertFalse($this->_model->existsComment(helper::getPasteId(), helper::getPasteId(), helper::getCommentId()), 'comment does not yet exist');
|
||||
$this->assertTrue($this->_model->createComment(helper::getPasteId(), helper::getPasteId(), helper::getCommentId(), helper::getComment()) !== false, 'store comment');
|
||||
$this->assertTrue($this->_model->existsComment(helper::getPasteId(), helper::getPasteId(), helper::getCommentId()), 'comment exists after storing it');
|
||||
$comment = json_decode(json_encode(helper::getComment()));
|
||||
$comment->id = helper::getCommentId();
|
||||
$comment->parentid = helper::getPasteId();
|
||||
$this->assertEquals(
|
||||
array($comment->meta->postdate => $comment),
|
||||
$this->_model->readComments(self::$pasteid)
|
||||
$this->_model->readComments(helper::getPasteId())
|
||||
);
|
||||
|
||||
// deleting pastes
|
||||
$this->_model->delete(self::$pasteid);
|
||||
$this->assertFalse($this->_model->exists(self::$pasteid), 'paste successfully deleted');
|
||||
$this->assertFalse($this->_model->existsComment(self::$pasteid, self::$pasteid, self::$commentid), 'comment was deleted with paste');
|
||||
$this->assertFalse($this->_model->read(self::$pasteid), 'paste can no longer be found');
|
||||
$this->_model->delete(helper::getPasteId());
|
||||
$this->assertFalse($this->_model->exists(helper::getPasteId()), 'paste successfully deleted');
|
||||
$this->assertFalse($this->_model->existsComment(helper::getPasteId(), helper::getPasteId(), helper::getCommentId()), 'comment was deleted with paste');
|
||||
$this->assertFalse($this->_model->read(helper::getPasteId()), 'paste can no longer be found');
|
||||
}
|
||||
|
||||
public function testDatabaseBasedAttachmentStoreWorks()
|
||||
{
|
||||
$this->_model->delete(helper::getPasteId());
|
||||
$original = $paste = helper::getPasteWithAttachment(array('expire_date' => 1344803344));
|
||||
$paste['meta']['burnafterreading'] = $original['meta']['burnafterreading'] = true;
|
||||
$paste['meta']['attachment'] = $paste['attachment'];
|
||||
$paste['meta']['attachmentname'] = $paste['attachmentname'];
|
||||
unset($paste['attachment'], $paste['attachmentname']);
|
||||
$this->assertFalse($this->_model->exists(helper::getPasteId()), 'paste does not yet exist');
|
||||
$this->assertTrue($this->_model->create(helper::getPasteId(), $paste), 'store new paste');
|
||||
$this->assertTrue($this->_model->exists(helper::getPasteId()), 'paste exists after storing it');
|
||||
$this->assertFalse($this->_model->create(helper::getPasteId(), $paste), 'unable to store the same paste twice');
|
||||
$this->assertEquals(json_decode(json_encode($original)), $this->_model->read(helper::getPasteId()));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -142,4 +138,29 @@ class zerobin_dbTest extends PHPUnit_Framework_TestCase
|
||||
'dsn' => 'foo:', 'usr' => null, 'pwd' => null, 'opt' => null
|
||||
));
|
||||
}
|
||||
|
||||
public function testTableUpgrade()
|
||||
{
|
||||
$path = PATH . 'data/db-test.sq3';
|
||||
@unlink($path);
|
||||
$this->_options['dsn'] = 'sqlite:' . $path;
|
||||
$this->_options['tbl'] = 'foo_';
|
||||
$db = new PDO(
|
||||
$this->_options['dsn'],
|
||||
$this->_options['usr'],
|
||||
$this->_options['pwd'],
|
||||
$this->_options['opt']
|
||||
);
|
||||
$db->exec(
|
||||
'CREATE TABLE foo_paste ( ' .
|
||||
'dataid CHAR(16), ' .
|
||||
'data TEXT, ' .
|
||||
'postdate INT, ' .
|
||||
'expiredate INT, ' .
|
||||
'opendiscussion INT, ' .
|
||||
'burnafterreading INT );'
|
||||
);
|
||||
zerobin_db::getInstance($this->_options);
|
||||
@unlink($path);
|
||||
}
|
||||
}
|
||||
|
||||
43
tst/zerobinWithDb.php
Normal file
43
tst/zerobinWithDb.php
Normal file
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
require_once 'zerobin.php';
|
||||
|
||||
class zerobinWithDbTest extends zerobinTest
|
||||
{
|
||||
private $_options = array(
|
||||
'dsn' => 'sqlite:../data/tst.sq3',
|
||||
'usr' => null,
|
||||
'pwd' => null,
|
||||
'opt' => array(
|
||||
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
|
||||
PDO::ATTR_PERSISTENT => true
|
||||
),
|
||||
);
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
/* Setup Routine */
|
||||
$this->_model = zerobin_db::getInstance($this->_options);
|
||||
serversalt::setPath(PATH . 'data');
|
||||
$this->reset();
|
||||
}
|
||||
|
||||
public function tearDown()
|
||||
{
|
||||
/* Tear Down Routine */
|
||||
parent::tearDown();
|
||||
@unlink('../data/tst.sq3');
|
||||
}
|
||||
|
||||
public function reset()
|
||||
{
|
||||
parent::reset();
|
||||
// but then inject a db config
|
||||
$options = parse_ini_file(CONF, true);
|
||||
$options['model'] = array(
|
||||
'class' => 'zerobin_db',
|
||||
);
|
||||
$options['model_options'] = $this->_options;
|
||||
helper::confBackup();
|
||||
helper::createIniFile(CONF, $options);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user