Compare commits
41 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 |
20
CHANGELOG.md
20
CHANGELOG.md
@@ -1,5 +1,25 @@
|
|||||||
# ZeroBin version history #
|
# 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)**:
|
* **0.21 (2015-09-19)**:
|
||||||
* ADDED: Translations for German, French and Polish, language selection menu (optional)
|
* ADDED: Translations for German, French and Polish, language selection menu (optional)
|
||||||
* ADDED: File upload and image display support (optional)
|
* ADDED: File upload and image display support (optional)
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ MrKooky - HTML5 markup, CSS cleanup
|
|||||||
Simon Rupf - MVC refactoring, configuration, i18n and unit tests
|
Simon Rupf - MVC refactoring, configuration, i18n and unit tests
|
||||||
Hexalyse - Password protection
|
Hexalyse - Password protection
|
||||||
Viktor Stanchev - File upload support
|
Viktor Stanchev - File upload support
|
||||||
|
azlux - Tab character input support
|
||||||
|
Adam Fisher - Favicons
|
||||||
|
|
||||||
Translations:
|
Translations:
|
||||||
Hexalyse - French
|
Hexalyse - French
|
||||||
|
|||||||
31
INSTALL.md
31
INSTALL.md
@@ -16,8 +16,10 @@ and extract it in your web hosts folder were you want to install your ZeroBin in
|
|||||||
|
|
||||||
### Configuration
|
### Configuration
|
||||||
|
|
||||||
In the file `cfg/conf.ini` you can configure ZeroBin. The config file is divided
|
In the file `cfg/conf.ini` you can configure ZeroBin. A `cfg/conf.ini.sample`
|
||||||
into multiple sections, which are enclosed in square brackets.
|
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
|
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
|
limit of stored pastes and comments in bytes. The `[traffic]` section lets you
|
||||||
@@ -74,20 +76,31 @@ any experience running ZeroBin on other RDBMS, please let us know.
|
|||||||
For reference or if you want to create the table schema for yourself:
|
For reference or if you want to create the table schema for yourself:
|
||||||
|
|
||||||
CREATE TABLE prefix_paste (
|
CREATE TABLE prefix_paste (
|
||||||
dataid CHAR(16),
|
dataid CHAR(16) NOT NULL,
|
||||||
data TEXT,
|
data BLOB,
|
||||||
postdate INT,
|
postdate INT,
|
||||||
expiredate INT,
|
expiredate INT,
|
||||||
opendiscussion INT,
|
opendiscussion INT,
|
||||||
burnafterreading INT
|
burnafterreading INT,
|
||||||
|
meta TEXT,
|
||||||
|
attachment MEDIUMBLOB,
|
||||||
|
attachmentname BLOB,
|
||||||
|
PRIMARY KEY (dataid)
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE TABLE prefix_comment (
|
CREATE TABLE prefix_comment (
|
||||||
dataid CHAR(16),
|
dataid CHAR(16),
|
||||||
pasteid CHAR(16),
|
pasteid CHAR(16),
|
||||||
parentid CHAR(16),
|
parentid CHAR(16),
|
||||||
data TEXT,
|
data BLOB,
|
||||||
nickname VARCHAR(255),
|
nickname BLOB,
|
||||||
vizhash TEXT,
|
vizhash BLOB,
|
||||||
postdate INT
|
postdate INT,
|
||||||
|
PRIMARY KEY (dataid)
|
||||||
);
|
);
|
||||||
|
CREATE INDEX parent ON prefix_comment(pasteid);
|
||||||
|
|
||||||
|
CREATE TABLE prefix_config (
|
||||||
|
id CHAR(16) NOT NULL, value TEXT, PRIMARY KEY (id)
|
||||||
|
);
|
||||||
|
INSERT INTO prefix_config VALUES('VERSION', '0.22');
|
||||||
|
|||||||
11
README.md
11
README.md
@@ -1,4 +1,4 @@
|
|||||||
# ZeroBin 0.21
|
# ZeroBin 0.22
|
||||||
|
|
||||||
ZeroBin is a minimalist, opensource online pastebin where the server has zero
|
ZeroBin is a minimalist, opensource online pastebin where the server has zero
|
||||||
knowledge of pasted data.
|
knowledge of pasted data.
|
||||||
@@ -29,6 +29,15 @@ without loosing any data.
|
|||||||
|
|
||||||
- As a user you have to trust the server administrator, your internet provider
|
- 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.
|
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 "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.
|
the URL of a paste that is not password-protected, everybody can read it.
|
||||||
|
|||||||
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
|
; @link http://sebsauvage.net/wiki/doku.php?id=php:zerobin
|
||||||
; @copyright 2012 Sébastien SAUVAGE (sebsauvage.net)
|
; @copyright 2012 Sébastien SAUVAGE (sebsauvage.net)
|
||||||
; @license http://www.opensource.org/licenses/zlib-license.php The zlib/libpng License
|
; @license http://www.opensource.org/licenses/zlib-license.php The zlib/libpng License
|
||||||
; @version 0.21
|
; @version 0.22
|
||||||
|
|
||||||
[main]
|
[main]
|
||||||
; enable or disable the discussion feature, defaults to true
|
; enable or disable the discussion feature, defaults to true
|
||||||
@@ -48,11 +48,19 @@ base64version = "2.1.9"
|
|||||||
; a session cookie to store the choice until the browser is closed.
|
; a session cookie to store the choice until the browser is closed.
|
||||||
languageselection = false
|
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]
|
||||||
; expire value that is selected per default
|
; expire value that is selected per default
|
||||||
; make sure the value exists in [expire_options]
|
; make sure the value exists in [expire_options]
|
||||||
default = "1week"
|
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]
|
[expire_options]
|
||||||
; Set each one of these to the number of seconds in the expiration period,
|
; Set each one of these to the number of seconds in the expiration period,
|
||||||
; or 0 if it should never expire
|
; or 0 if it should never expire
|
||||||
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,4 +1,4 @@
|
|||||||
/* ZeroBin 0.21 - http://sebsauvage.net/wiki/doku.php?id=php:zerobin */
|
/* ZeroBin 0.22 - http://sebsauvage.net/wiki/doku.php?id=php:zerobin */
|
||||||
|
|
||||||
|
|
||||||
body {
|
body {
|
||||||
@@ -10,7 +10,7 @@ body.navbar-spacing {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.navbar-nav {
|
.navbar-nav {
|
||||||
margin: 0;
|
margin: 0 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dropdown-menu > li > label, .dropdown-menu > li > div {
|
.dropdown-menu > li > label, .dropdown-menu > li > div {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* ZeroBin 0.21 - 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.
|
/* CSS Reset from YUI 3.4.1 (build 4118) - Copyright 2011 Yahoo! Inc. All rights reserved.
|
||||||
@@ -394,6 +394,10 @@ img.vizhash {
|
|||||||
font-size: 1.2em;
|
font-size: 1.2em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#prettyprint.prettyprinted {
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
#cleartext {
|
#cleartext {
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
}
|
}
|
||||||
|
|||||||
BIN
favicon.ico
Normal file
BIN
favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.7 KiB |
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,7 +7,7 @@
|
|||||||
* @link http://sebsauvage.net/wiki/doku.php?id=php:zerobin
|
* @link http://sebsauvage.net/wiki/doku.php?id=php:zerobin
|
||||||
* @copyright 2012 Sébastien SAUVAGE (sebsauvage.net)
|
* @copyright 2012 Sébastien SAUVAGE (sebsauvage.net)
|
||||||
* @license http://www.opensource.org/licenses/zlib-license.php The zlib/libpng License
|
* @license http://www.opensource.org/licenses/zlib-license.php The zlib/libpng License
|
||||||
* @version 0.21
|
* @version 0.21.1
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// change this, if your php files and data is outside of your webservers document root
|
// change this, if your php files and data is outside of your webservers document root
|
||||||
|
|||||||
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"}
|
||||||
|
}
|
||||||
|
}
|
||||||
242
js/zerobin.js
242
js/zerobin.js
@@ -6,7 +6,7 @@
|
|||||||
* @link http://sebsauvage.net/wiki/doku.php?id=php:zerobin
|
* @link http://sebsauvage.net/wiki/doku.php?id=php:zerobin
|
||||||
* @copyright 2012 Sébastien SAUVAGE (sebsauvage.net)
|
* @copyright 2012 Sébastien SAUVAGE (sebsauvage.net)
|
||||||
* @license http://www.opensource.org/licenses/zlib-license.php The zlib/libpng License
|
* @license http://www.opensource.org/licenses/zlib-license.php The zlib/libpng License
|
||||||
* @version 0.21
|
* @version 0.22
|
||||||
*/
|
*/
|
||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
@@ -443,7 +443,7 @@ $(function() {
|
|||||||
*/
|
*/
|
||||||
cipher: function(key, password, message)
|
cipher: function(key, password, message)
|
||||||
{
|
{
|
||||||
password = password.trim();
|
password = (password || '').trim();
|
||||||
if (password.length == 0)
|
if (password.length == 0)
|
||||||
{
|
{
|
||||||
return sjcl.encrypt(key, this.compress(message));
|
return sjcl.encrypt(key, this.compress(message));
|
||||||
@@ -482,6 +482,11 @@ $(function() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
var zerobin = {
|
var zerobin = {
|
||||||
|
/**
|
||||||
|
* headers to send in AJAX requests
|
||||||
|
*/
|
||||||
|
headers: {'X-Requested-With': 'JSONHttpRequest'},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the current script location (without search or hash part of the URL).
|
* Get the current script location (without search or hash part of the URL).
|
||||||
* eg. http://server.com/zero/?aaaa#bbbb --> http://server.com/zero/
|
* eg. http://server.com/zero/?aaaa#bbbb --> http://server.com/zero/
|
||||||
@@ -578,37 +583,42 @@ $(function() {
|
|||||||
helper.urls2links(this.clearText);
|
helper.urls2links(this.clearText);
|
||||||
helper.urls2links(this.prettyPrint);
|
helper.urls2links(this.prettyPrint);
|
||||||
this.clearText.addClass('hidden');
|
this.clearText.addClass('hidden');
|
||||||
|
if (format == 'plaintext')
|
||||||
|
{
|
||||||
|
this.prettyPrint.css('white-space', 'pre-wrap');
|
||||||
|
this.prettyPrint.css('word-break', 'normal');
|
||||||
|
this.prettyPrint.removeClass('prettyprint');
|
||||||
|
}
|
||||||
this.prettyMessage.removeClass('hidden');
|
this.prettyMessage.removeClass('hidden');
|
||||||
}
|
}
|
||||||
if (format == 'plaintext') this.prettyPrint.removeClass('prettyprint');
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Show decrypted text in the display area, including discussion (if open)
|
* Show decrypted text in the display area, including discussion (if open)
|
||||||
*
|
*
|
||||||
* @param string key : decryption key
|
* @param string key : decryption key
|
||||||
* @param array comments : Array of messages to display (items = array with keys ('data','meta')
|
* @param object paste : paste object including comments to display (items = array with keys ('data','meta')
|
||||||
*/
|
*/
|
||||||
displayMessages: function(key, comments)
|
displayMessages: function(key, paste)
|
||||||
{
|
{
|
||||||
// Try to decrypt the paste.
|
// Try to decrypt the paste.
|
||||||
var password = this.passwordInput.val();
|
var password = this.passwordInput.val();
|
||||||
if (!this.prettyPrint.hasClass('prettyprinted')) {
|
if (!this.prettyPrint.hasClass('prettyprinted')) {
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (comments[0].attachment)
|
if (paste.attachment)
|
||||||
{
|
{
|
||||||
var attachment = filter.decipher(key, password, comments[0].attachment);
|
var attachment = filter.decipher(key, password, paste.attachment);
|
||||||
if (attachment.length == 0)
|
if (attachment.length == 0)
|
||||||
{
|
{
|
||||||
if (password.length == 0) password = this.requestPassword();
|
if (password.length == 0) password = this.requestPassword();
|
||||||
attachment = filter.decipher(key, password, comments[0].attachment);
|
attachment = filter.decipher(key, password, paste.attachment);
|
||||||
}
|
}
|
||||||
if (attachment.length == 0) throw 'failed to decipher attachment';
|
if (attachment.length == 0) throw 'failed to decipher attachment';
|
||||||
|
|
||||||
if (comments[0].attachmentname)
|
if (paste.attachmentname)
|
||||||
{
|
{
|
||||||
var attachmentname = filter.decipher(key, password, comments[0].attachmentname);
|
var attachmentname = filter.decipher(key, password, paste.attachmentname);
|
||||||
if (attachmentname.length > 0) this.attachmentLink.attr('download', attachmentname);
|
if (attachmentname.length > 0) this.attachmentLink.attr('download', attachmentname);
|
||||||
}
|
}
|
||||||
this.attachmentLink.attr('href', attachment);
|
this.attachmentLink.attr('href', attachment);
|
||||||
@@ -626,20 +636,20 @@ $(function() {
|
|||||||
this.image.removeClass('hidden');
|
this.image.removeClass('hidden');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var cleartext = filter.decipher(key, password, comments[0].data);
|
var cleartext = filter.decipher(key, password, paste.data);
|
||||||
if (cleartext.length == 0 && password.length == 0 && !comments[0].attachment)
|
if (cleartext.length == 0 && password.length == 0 && !paste.attachment)
|
||||||
{
|
{
|
||||||
password = this.requestPassword();
|
password = this.requestPassword();
|
||||||
cleartext = filter.decipher(key, password, comments[0].data);
|
cleartext = filter.decipher(key, password, paste.data);
|
||||||
}
|
}
|
||||||
if (cleartext.length == 0 && !comments[0].attachment) throw 'failed to decipher message';
|
if (cleartext.length == 0 && !paste.attachment) throw 'failed to decipher message';
|
||||||
|
|
||||||
this.passwordInput.val(password);
|
this.passwordInput.val(password);
|
||||||
if (cleartext.length > 0)
|
if (cleartext.length > 0)
|
||||||
{
|
{
|
||||||
helper.setElementText(this.clearText, cleartext);
|
helper.setElementText(this.clearText, cleartext);
|
||||||
helper.setElementText(this.prettyPrint, cleartext);
|
helper.setElementText(this.prettyPrint, cleartext);
|
||||||
this.formatPaste(comments[0].meta.formatter);
|
this.formatPaste(paste.meta.formatter);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch(err)
|
catch(err)
|
||||||
@@ -653,9 +663,9 @@ $(function() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Display paste expiration / for your eyes only.
|
// Display paste expiration / for your eyes only.
|
||||||
if (comments[0].meta.expire_date)
|
if (paste.meta.expire_date)
|
||||||
{
|
{
|
||||||
var expiration = helper.secondsToHuman(comments[0].meta.remaining_time),
|
var expiration = helper.secondsToHuman(paste.meta.remaining_time),
|
||||||
expirationLabel = [
|
expirationLabel = [
|
||||||
'This document will expire in %d ' + expiration[1] + '.',
|
'This document will expire in %d ' + expiration[1] + '.',
|
||||||
'This document will expire in %d ' + expiration[1] + 's.'
|
'This document will expire in %d ' + expiration[1] + 's.'
|
||||||
@@ -664,9 +674,16 @@ $(function() {
|
|||||||
this.remainingTime.removeClass('foryoureyesonly')
|
this.remainingTime.removeClass('foryoureyesonly')
|
||||||
.removeClass('hidden');
|
.removeClass('hidden');
|
||||||
}
|
}
|
||||||
if (comments[0].meta.burnafterreading)
|
if (paste.meta.burnafterreading)
|
||||||
{
|
{
|
||||||
$.get(this.scriptLocation() + '?pasteid=' + this.pasteID() + '&deletetoken=burnafterreading', 'json')
|
// unfortunately many web servers don't support DELETE (and PUT) out of the box
|
||||||
|
$.ajax({
|
||||||
|
type: 'POST',
|
||||||
|
url: this.scriptLocation() + '?' + this.pasteID(),
|
||||||
|
data: {deletetoken: 'burnafterreading'},
|
||||||
|
dataType: 'json',
|
||||||
|
headers: this.headers
|
||||||
|
})
|
||||||
.fail(function() {
|
.fail(function() {
|
||||||
zerobin.showError(i18n._('Could not delete the paste, it was not stored in burn after reading mode.'));
|
zerobin.showError(i18n._('Could not delete the paste, it was not stored in burn after reading mode.'));
|
||||||
});
|
});
|
||||||
@@ -680,15 +697,15 @@ $(function() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// If the discussion is opened on this paste, display it.
|
// If the discussion is opened on this paste, display it.
|
||||||
if (comments[0].meta.opendiscussion)
|
if (paste.meta.opendiscussion)
|
||||||
{
|
{
|
||||||
this.comments.html('');
|
this.comments.html('');
|
||||||
|
|
||||||
// iterate over comments
|
// iterate over comments
|
||||||
for (var i = 1; i < comments.length; i++)
|
for (var i = 0; i < paste.comments.length; ++i)
|
||||||
{
|
{
|
||||||
var place = this.comments;
|
var place = this.comments;
|
||||||
var comment=comments[i];
|
var comment = paste.comments[i];
|
||||||
var cleartext = '[' + i18n._('Could not decrypt comment; Wrong key?') + ']';
|
var cleartext = '[' + i18n._('Could not decrypt comment; Wrong key?') + ']';
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -697,18 +714,18 @@ $(function() {
|
|||||||
catch(err)
|
catch(err)
|
||||||
{}
|
{}
|
||||||
// If parent comment exists, display below (CSS will automatically shift it right.)
|
// If parent comment exists, display below (CSS will automatically shift it right.)
|
||||||
var cname = '#comment_' + comment.meta.parentid;
|
var cname = '#comment_' + comment.parentid;
|
||||||
|
|
||||||
// If the element exists in page
|
// If the element exists in page
|
||||||
if ($(cname).length)
|
if ($(cname).length)
|
||||||
{
|
{
|
||||||
place = $(cname);
|
place = $(cname);
|
||||||
}
|
}
|
||||||
var divComment = $('<article><div class="comment" id="comment_' + comment.meta.commentid+'">'
|
var divComment = $('<article><div class="comment" id="comment_' + comment.id + '">'
|
||||||
+ '<div class="commentmeta"><span class="nickname"></span><span class="commentdate"></span></div><div class="commentdata"></div>'
|
+ '<div class="commentmeta"><span class="nickname"></span><span class="commentdate"></span></div><div class="commentdata"></div>'
|
||||||
+ '<button class="btn btn-default btn-sm">' + i18n._('Reply') + '</button>'
|
+ '<button class="btn btn-default btn-sm">' + i18n._('Reply') + '</button>'
|
||||||
+ '</div></article>');
|
+ '</div></article>');
|
||||||
divComment.find('button').click({commentid: comment.meta.commentid}, $.proxy(this.openReply, this));
|
divComment.find('button').click({commentid: comment.id}, $.proxy(this.openReply, this));
|
||||||
helper.setElementText(divComment.find('div.commentdata'), cleartext);
|
helper.setElementText(divComment.find('div.commentdata'), cleartext);
|
||||||
// Convert URLs to clickable links in comment.
|
// Convert URLs to clickable links in comment.
|
||||||
helper.urls2links(divComment.find('div.commentdata'));
|
helper.urls2links(divComment.find('div.commentdata'));
|
||||||
@@ -725,7 +742,7 @@ $(function() {
|
|||||||
}
|
}
|
||||||
divComment.find('span.commentdate')
|
divComment.find('span.commentdate')
|
||||||
.text(' (' + (new Date(comment.meta.postdate * 1000).toLocaleString()) + ')')
|
.text(' (' + (new Date(comment.meta.postdate * 1000).toLocaleString()) + ')')
|
||||||
.attr('title', 'CommentID: ' + comment.meta.commentid);
|
.attr('title', 'CommentID: ' + comment.id);
|
||||||
|
|
||||||
// If an avatar is available, display it.
|
// If an avatar is available, display it.
|
||||||
if (comment.meta.vizhash)
|
if (comment.meta.vizhash)
|
||||||
@@ -805,39 +822,52 @@ $(function() {
|
|||||||
nickname: ciphernickname
|
nickname: ciphernickname
|
||||||
};
|
};
|
||||||
|
|
||||||
$.post(this.scriptLocation(), data_to_send, function(data)
|
$.ajax({
|
||||||
{
|
type: 'POST',
|
||||||
if (data.status == 0)
|
url: this.scriptLocation(),
|
||||||
|
data: data_to_send,
|
||||||
|
dataType: 'json',
|
||||||
|
headers: this.headers,
|
||||||
|
success: function(data)
|
||||||
{
|
{
|
||||||
zerobin.showStatus(i18n._('Comment posted.'), false);
|
if (data.status == 0)
|
||||||
$.get(zerobin.scriptLocation() + '?' + zerobin.pasteID() + '&json', function(data)
|
|
||||||
{
|
{
|
||||||
if (data.status == 0)
|
zerobin.showStatus(i18n._('Comment posted.'), false);
|
||||||
{
|
$.ajax({
|
||||||
zerobin.displayMessages(zerobin.pageKey(), data.messages);
|
type: 'GET',
|
||||||
}
|
url: zerobin.scriptLocation() + '?' + zerobin.pasteID(),
|
||||||
else if (data.status == 1)
|
dataType: 'json',
|
||||||
{
|
headers: zerobin.headers,
|
||||||
zerobin.showError(i18n._('Could not refresh display: %s', data.message));
|
success: function(data)
|
||||||
}
|
{
|
||||||
else
|
if (data.status == 0)
|
||||||
{
|
{
|
||||||
zerobin.showError(i18n._('Could not refresh display: %s', i18n._('unknown status')));
|
zerobin.displayMessages(zerobin.pageKey(), data);
|
||||||
}
|
}
|
||||||
}, 'json')
|
else if (data.status == 1)
|
||||||
.fail(function() {
|
{
|
||||||
zerobin.showError(i18n._('Could not refresh display: %s', i18n._('server error or not responding')));
|
zerobin.showError(i18n._('Could not refresh display: %s', data.message));
|
||||||
});
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
zerobin.showError(i18n._('Could not refresh display: %s', i18n._('unknown status')));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.fail(function() {
|
||||||
|
zerobin.showError(i18n._('Could not refresh display: %s', i18n._('server error or not responding')));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else if (data.status == 1)
|
||||||
|
{
|
||||||
|
zerobin.showError(i18n._('Could not post comment: %s', data.message));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
zerobin.showError(i18n._('Could not post comment: %s', i18n._('unknown status')));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (data.status == 1)
|
})
|
||||||
{
|
|
||||||
zerobin.showError(i18n._('Could not post comment: %s', data.message));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
zerobin.showError(i18n._('Could not post comment: %s', i18n._('unknown status')));
|
|
||||||
}
|
|
||||||
}, 'json')
|
|
||||||
.fail(function() {
|
.fail(function() {
|
||||||
zerobin.showError(i18n._('Could not post comment: %s', i18n._('server error or not responding')));
|
zerobin.showError(i18n._('Could not post comment: %s', i18n._('server error or not responding')));
|
||||||
});
|
});
|
||||||
@@ -932,36 +962,44 @@ $(function() {
|
|||||||
data_to_send.attachmentname = cipherdata_attachment_name;
|
data_to_send.attachmentname = cipherdata_attachment_name;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$.post(this.scriptLocation(), data_to_send, function(data)
|
$.ajax({
|
||||||
|
type: 'POST',
|
||||||
|
url: this.scriptLocation(),
|
||||||
|
data: data_to_send,
|
||||||
|
dataType: 'json',
|
||||||
|
headers: this.headers,
|
||||||
|
success: function(data)
|
||||||
|
{
|
||||||
|
if (data.status == 0) {
|
||||||
|
zerobin.stateExistingPaste();
|
||||||
|
var url = zerobin.scriptLocation() + '?' + data.id + '#' + randomkey;
|
||||||
|
var deleteUrl = zerobin.scriptLocation() + '?pasteid=' + data.id + '&deletetoken=' + data.deletetoken;
|
||||||
|
zerobin.showStatus('', false);
|
||||||
|
zerobin.errorMessage.addClass('hidden');
|
||||||
|
|
||||||
|
$('#pastelink').html(i18n._('Your paste is <a id="pasteurl" href="%s">%s</a> <span id="copyhint">(Hit [Ctrl]+[c] to copy)</span>', url, url));
|
||||||
|
$('#deletelink').html('<a href="' + deleteUrl + '">' + i18n._('Delete data') + '</a>');
|
||||||
|
zerobin.pasteResult.removeClass('hidden');
|
||||||
|
// We pre-select the link so that the user only has to [Ctrl]+[c] the link.
|
||||||
|
helper.selectText('pasteurl');
|
||||||
|
zerobin.showStatus('', false);
|
||||||
|
|
||||||
|
helper.setElementText(zerobin.clearText, zerobin.message.val());
|
||||||
|
helper.setElementText(zerobin.prettyPrint, zerobin.message.val());
|
||||||
|
zerobin.formatPaste(data_to_send.formatter);
|
||||||
|
}
|
||||||
|
else if (data.status==1)
|
||||||
|
{
|
||||||
|
zerobin.showError(i18n._('Could not create paste: %s', data.message));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
zerobin.showError(i18n._('Could not create paste: %s', i18n._('unknown status')));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.fail(function()
|
||||||
{
|
{
|
||||||
if (data.status == 0) {
|
|
||||||
zerobin.stateExistingPaste();
|
|
||||||
var url = zerobin.scriptLocation() + '?' + data.id + '#' + randomkey;
|
|
||||||
var deleteUrl = zerobin.scriptLocation() + '?pasteid=' + data.id + '&deletetoken=' + data.deletetoken;
|
|
||||||
zerobin.showStatus('', false);
|
|
||||||
zerobin.errorMessage.addClass('hidden');
|
|
||||||
|
|
||||||
$('#pastelink').html(i18n._('Your paste is <a id="pasteurl" href="%s">%s</a> <span id="copyhint">(Hit [Ctrl]+[c] to copy)</span>', url, url));
|
|
||||||
$('#deletelink').html('<a href="' + deleteUrl + '">' + i18n._('Delete data') + '</a>');
|
|
||||||
zerobin.pasteResult.removeClass('hidden');
|
|
||||||
// We pre-select the link so that the user only has to [Ctrl]+[c] the link.
|
|
||||||
helper.selectText('pasteurl');
|
|
||||||
zerobin.showStatus('', false);
|
|
||||||
|
|
||||||
helper.setElementText(zerobin.clearText, zerobin.message.val());
|
|
||||||
helper.setElementText(zerobin.prettyPrint, zerobin.message.val());
|
|
||||||
zerobin.formatPaste(data_to_send.formatter);
|
|
||||||
}
|
|
||||||
else if (data.status==1)
|
|
||||||
{
|
|
||||||
zerobin.showError(i18n._('Could not create paste: %s', data.message));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
zerobin.showError(i18n._('Could not create paste: %s', i18n._('unknown status')));
|
|
||||||
}
|
|
||||||
}, 'json')
|
|
||||||
.fail(function() {
|
|
||||||
zerobin.showError(i18n._('Could not create paste: %s', i18n._('server error or not responding')));
|
zerobin.showError(i18n._('Could not create paste: %s', i18n._('server error or not responding')));
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@@ -1040,7 +1078,7 @@ $(function() {
|
|||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reload the page
|
* Reload the page.
|
||||||
*
|
*
|
||||||
* @param Event event
|
* @param Event event
|
||||||
*/
|
*/
|
||||||
@@ -1051,7 +1089,7 @@ $(function() {
|
|||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return raw text
|
* Return raw text.
|
||||||
*
|
*
|
||||||
* @param Event event
|
* @param Event event
|
||||||
*/
|
*/
|
||||||
@@ -1087,6 +1125,30 @@ $(function() {
|
|||||||
$('.navbar-toggle').click();
|
$('.navbar-toggle').click();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Support input of tab character.
|
||||||
|
*
|
||||||
|
* @param Event event
|
||||||
|
*/
|
||||||
|
supportTabs: function(event)
|
||||||
|
{
|
||||||
|
var keyCode = event.keyCode || event.which;
|
||||||
|
// tab was pressed
|
||||||
|
if (keyCode === 9)
|
||||||
|
{
|
||||||
|
// prevent the textarea to lose focus
|
||||||
|
event.preventDefault();
|
||||||
|
// get caret position & selection
|
||||||
|
var val = this.value,
|
||||||
|
start = this.selectionStart,
|
||||||
|
end = this.selectionEnd;
|
||||||
|
// set textarea value to: text before caret + tab + text after caret
|
||||||
|
this.value = val.substring(0, start) + '\t' + val.substring(end);
|
||||||
|
// put caret at right position again
|
||||||
|
this.selectionStart = this.selectionEnd = start + 1;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new paste.
|
* Create a new paste.
|
||||||
*/
|
*/
|
||||||
@@ -1095,7 +1157,6 @@ $(function() {
|
|||||||
this.stateNewPaste();
|
this.stateNewPaste();
|
||||||
this.showStatus('', false);
|
this.showStatus('', false);
|
||||||
this.message.text('');
|
this.message.text('');
|
||||||
$('.navbar-toggle').click();
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1171,6 +1232,7 @@ $(function() {
|
|||||||
this.rawTextButton.click($.proxy(this.rawText, this));
|
this.rawTextButton.click($.proxy(this.rawText, this));
|
||||||
this.fileRemoveButton.click($.proxy(this.removeAttachment, this));
|
this.fileRemoveButton.click($.proxy(this.removeAttachment, this));
|
||||||
$('.reloadlink').click($.proxy(this.reloadPage, this));
|
$('.reloadlink').click($.proxy(this.reloadPage, this));
|
||||||
|
this.message.keydown(this.supportTabs);
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1236,12 +1298,12 @@ $(function() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// List of messages to display.
|
// List of messages to display.
|
||||||
var messages = $.parseJSON(this.cipherData.text());
|
var data = $.parseJSON(this.cipherData.text());
|
||||||
|
|
||||||
// Show proper elements on screen.
|
// Show proper elements on screen.
|
||||||
this.stateExistingPaste();
|
this.stateExistingPaste();
|
||||||
|
|
||||||
this.displayMessages(this.pageKey(), messages);
|
this.displayMessages(this.pageKey(), data);
|
||||||
}
|
}
|
||||||
// Display error message from php code.
|
// Display error message from php code.
|
||||||
else if (this.errorMessage.text().length > 1)
|
else if (this.errorMessage.text().length > 1)
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
* @link http://sebsauvage.net/wiki/doku.php?id=php:zerobin
|
* @link http://sebsauvage.net/wiki/doku.php?id=php:zerobin
|
||||||
* @copyright 2012 Sébastien SAUVAGE (sebsauvage.net)
|
* @copyright 2012 Sébastien SAUVAGE (sebsauvage.net)
|
||||||
* @license http://www.opensource.org/licenses/zlib-license.php The zlib/libpng License
|
* @license http://www.opensource.org/licenses/zlib-license.php The zlib/libpng License
|
||||||
* @version 0.21
|
* @version 0.22
|
||||||
*/
|
*/
|
||||||
|
|
||||||
spl_autoload_register('auto::loader');
|
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
|
* @link http://sebsauvage.net/wiki/doku.php?id=php:zerobin
|
||||||
* @copyright 2012 Sébastien SAUVAGE (sebsauvage.net)
|
* @copyright 2012 Sébastien SAUVAGE (sebsauvage.net)
|
||||||
* @license http://www.opensource.org/licenses/zlib-license.php The zlib/libpng License
|
* @license http://www.opensource.org/licenses/zlib-license.php The zlib/libpng License
|
||||||
* @version 0.21
|
* @version 0.22
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -80,19 +80,6 @@ class filter
|
|||||||
return number_format($size, ($i ? 2 : 0), '.', ' ') . ' ' . i18n::_($iec[$i]);
|
return number_format($size, ($i ? 2 : 0), '.', ' ') . ' ' . i18n::_($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);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* fixed time string comparison operation to prevent timing attacks
|
* fixed time string comparison operation to prevent timing attacks
|
||||||
* https://crackstation.net/hashing-security.htm?=rd#slowequals
|
* https://crackstation.net/hashing-security.htm?=rd#slowequals
|
||||||
|
|||||||
27
lib/i18n.php
27
lib/i18n.php
@@ -7,7 +7,7 @@
|
|||||||
* @link http://sebsauvage.net/wiki/doku.php?id=php:zerobin
|
* @link http://sebsauvage.net/wiki/doku.php?id=php:zerobin
|
||||||
* @copyright 2012 Sébastien SAUVAGE (sebsauvage.net)
|
* @copyright 2012 Sébastien SAUVAGE (sebsauvage.net)
|
||||||
* @license http://www.opensource.org/licenses/zlib-license.php The zlib/libpng License
|
* @license http://www.opensource.org/licenses/zlib-license.php The zlib/libpng License
|
||||||
* @version 0.21
|
* @version 0.22
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -26,6 +26,15 @@ class i18n
|
|||||||
*/
|
*/
|
||||||
protected static $_language = 'en';
|
protected static $_language = 'en';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* language fallback
|
||||||
|
*
|
||||||
|
* @access protected
|
||||||
|
* @static
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected static $_languageFallback = 'en';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* language labels
|
* language labels
|
||||||
*
|
*
|
||||||
@@ -248,6 +257,20 @@ class i18n
|
|||||||
return array_intersect_key(self::$_languageLabels, array_flip($languages));
|
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
|
* get language file path
|
||||||
*
|
*
|
||||||
@@ -339,7 +362,7 @@ class i18n
|
|||||||
}
|
}
|
||||||
if (count($matches) === 0)
|
if (count($matches) === 0)
|
||||||
{
|
{
|
||||||
return 'en';
|
return self::$_languageFallback;
|
||||||
}
|
}
|
||||||
krsort($matches);
|
krsort($matches);
|
||||||
$topmatches = current($matches);
|
$topmatches = current($matches);
|
||||||
|
|||||||
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
|
* @link http://sebsauvage.net/wiki/doku.php?id=php:zerobin
|
||||||
* @copyright 2012 Sébastien SAUVAGE (sebsauvage.net)
|
* @copyright 2012 Sébastien SAUVAGE (sebsauvage.net)
|
||||||
* @license http://www.opensource.org/licenses/zlib-license.php The zlib/libpng License
|
* @license http://www.opensource.org/licenses/zlib-license.php The zlib/libpng License
|
||||||
* @version 0.21
|
* @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
|
* @link http://sebsauvage.net/wiki/doku.php?id=php:zerobin
|
||||||
* @copyright 2012 Sébastien SAUVAGE (sebsauvage.net)
|
* @copyright 2012 Sébastien SAUVAGE (sebsauvage.net)
|
||||||
* @license http://www.opensource.org/licenses/zlib-license.php The zlib/libpng License
|
* @license http://www.opensource.org/licenses/zlib-license.php The zlib/libpng License
|
||||||
* @version 0.21
|
* @version 0.22
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
* @link http://sebsauvage.net/wiki/doku.php?id=php:zerobin
|
* @link http://sebsauvage.net/wiki/doku.php?id=php:zerobin
|
||||||
* @copyright 2012 Sébastien SAUVAGE (sebsauvage.net)
|
* @copyright 2012 Sébastien SAUVAGE (sebsauvage.net)
|
||||||
* @license http://www.opensource.org/licenses/zlib-license.php The zlib/libpng License
|
* @license http://www.opensource.org/licenses/zlib-license.php The zlib/libpng License
|
||||||
* @version 0.21
|
* @version 0.22
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
* @link http://sebsauvage.net/wiki/doku.php?id=php:zerobin
|
* @link http://sebsauvage.net/wiki/doku.php?id=php:zerobin
|
||||||
* @copyright 2012 Sébastien SAUVAGE (sebsauvage.net)
|
* @copyright 2012 Sébastien SAUVAGE (sebsauvage.net)
|
||||||
* @license http://www.opensource.org/licenses/zlib-license.php The zlib/libpng License
|
* @license http://www.opensource.org/licenses/zlib-license.php The zlib/libpng License
|
||||||
* @version 0.21
|
* @version 0.22
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -26,6 +26,15 @@ class trafficlimiter extends persistence
|
|||||||
*/
|
*/
|
||||||
private static $_limit = 10;
|
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
|
* set the time limit in seconds
|
||||||
*
|
*
|
||||||
@@ -39,6 +48,40 @@ class trafficlimiter extends persistence
|
|||||||
self::$_limit = $limit;
|
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
|
* traffic limiter
|
||||||
*
|
*
|
||||||
@@ -46,14 +89,15 @@ class trafficlimiter extends persistence
|
|||||||
*
|
*
|
||||||
* @access public
|
* @access public
|
||||||
* @static
|
* @static
|
||||||
* @param string $ip
|
|
||||||
* @throws Exception
|
* @throws Exception
|
||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
public static function canPass($ip)
|
public static function canPass()
|
||||||
{
|
{
|
||||||
// disable limits if set to less then 1
|
$ip = self::getIp();
|
||||||
if (self::$_limit < 1) return true;
|
|
||||||
|
// disable limits if set to less then 1
|
||||||
|
if (self::$_limit < 1) return true;
|
||||||
|
|
||||||
$file = 'traffic_limiter.php';
|
$file = 'traffic_limiter.php';
|
||||||
if (!self::_exists($file))
|
if (!self::_exists($file))
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
* @link http://sebsauvage.net/wiki/doku.php?id=php:vizhash_gd
|
* @link http://sebsauvage.net/wiki/doku.php?id=php:vizhash_gd
|
||||||
* @copyright 2012 Sébastien SAUVAGE (sebsauvage.net)
|
* @copyright 2012 Sébastien SAUVAGE (sebsauvage.net)
|
||||||
* @license http://www.opensource.org/licenses/zlib-license.php The zlib/libpng License
|
* @license http://www.opensource.org/licenses/zlib-license.php The zlib/libpng License
|
||||||
* @version 0.0.4 beta ZeroBin 0.21
|
* @version 0.0.4 beta ZeroBin 0.22
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
597
lib/zerobin.php
597
lib/zerobin.php
@@ -7,7 +7,7 @@
|
|||||||
* @link http://sebsauvage.net/wiki/doku.php?id=php:zerobin
|
* @link http://sebsauvage.net/wiki/doku.php?id=php:zerobin
|
||||||
* @copyright 2012 Sébastien SAUVAGE (sebsauvage.net)
|
* @copyright 2012 Sébastien SAUVAGE (sebsauvage.net)
|
||||||
* @license http://www.opensource.org/licenses/zlib-license.php The zlib/libpng License
|
* @license http://www.opensource.org/licenses/zlib-license.php The zlib/libpng License
|
||||||
* @version 0.21
|
* @version 0.22
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -22,7 +22,7 @@ class zerobin
|
|||||||
*
|
*
|
||||||
* @const string
|
* @const string
|
||||||
*/
|
*/
|
||||||
const VERSION = '0.21';
|
const VERSION = '0.22';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* show the same error message if the paste expired or does not exist
|
* 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.';
|
const GENERIC_ERROR = 'Paste does not exist, has expired or has been deleted.';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* configuration array
|
* configuration
|
||||||
*
|
*
|
||||||
* @access private
|
* @access private
|
||||||
* @var array
|
* @var configuration
|
||||||
*/
|
*/
|
||||||
private $_conf = array(
|
private $_conf;
|
||||||
'model' => 'zerobin_data',
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* data
|
* data
|
||||||
@@ -49,6 +47,14 @@ class zerobin
|
|||||||
*/
|
*/
|
||||||
private $_data = '';
|
private $_data = '';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* does the paste expire
|
||||||
|
*
|
||||||
|
* @access private
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
private $_doesExpire = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* formatter
|
* formatter
|
||||||
*
|
*
|
||||||
@@ -82,13 +88,29 @@ class zerobin
|
|||||||
private $_json = '';
|
private $_json = '';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* data storage model
|
* Factory of instance models
|
||||||
*
|
*
|
||||||
* @access private
|
* @access private
|
||||||
* @var zerobin_abstract
|
* @var model
|
||||||
*/
|
*/
|
||||||
private $_model;
|
private $_model;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* request
|
||||||
|
*
|
||||||
|
* @access private
|
||||||
|
* @var request
|
||||||
|
*/
|
||||||
|
private $_request;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* URL base
|
||||||
|
*
|
||||||
|
* @access private
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $_urlbase;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* constructor
|
* constructor
|
||||||
*
|
*
|
||||||
@@ -104,40 +126,35 @@ class zerobin
|
|||||||
throw new Exception(i18n::_('ZeroBin requires php 5.2.6 or above to work. Sorry.'), 1);
|
throw new Exception(i18n::_('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);
|
|
||||||
}
|
|
||||||
|
|
||||||
// load config from ini file
|
// load config from ini file
|
||||||
$this->_init();
|
$this->_init();
|
||||||
|
|
||||||
// create new paste or comment
|
switch ($this->_request->getOperation())
|
||||||
if (
|
|
||||||
(array_key_exists('data', $_POST) && !empty($_POST['data'])) ||
|
|
||||||
(array_key_exists('attachment', $_POST) && !empty($_POST['attachment']))
|
|
||||||
)
|
|
||||||
{
|
{
|
||||||
$this->_create();
|
case 'create':
|
||||||
}
|
$this->_create();
|
||||||
// delete an existing paste
|
break;
|
||||||
elseif (!empty($_GET['deletetoken']) && !empty($_GET['pasteid']))
|
case 'delete':
|
||||||
{
|
$this->_delete(
|
||||||
$this->_delete($_GET['pasteid'], $_GET['deletetoken']);
|
$this->_request->getParam('pasteid'),
|
||||||
}
|
$this->_request->getParam('deletetoken')
|
||||||
// display an existing paste
|
);
|
||||||
elseif (!empty($_SERVER['QUERY_STRING']))
|
break;
|
||||||
{
|
case 'read':
|
||||||
$this->_read($_SERVER['QUERY_STRING']);
|
$this->_read($this->_request->getParam('pasteid'));
|
||||||
|
break;
|
||||||
|
case 'jsonld':
|
||||||
|
$this->_jsonld($this->_request->getParam('jsonld'));
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// output JSON or HTML
|
// output JSON or HTML
|
||||||
if (strlen($this->_json))
|
if ($this->_request->isJsonApiCall())
|
||||||
{
|
{
|
||||||
header('Content-type: application/json');
|
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;
|
echo $this->_json;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -164,31 +181,20 @@ class zerobin
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->_conf = parse_ini_file(PATH . 'cfg' . DIRECTORY_SEPARATOR . 'conf.ini', true);
|
$this->_conf = new configuration;
|
||||||
foreach (array('main', 'model') as $section) {
|
$this->_model = new model($this->_conf);
|
||||||
if (!array_key_exists($section, $this->_conf)) {
|
$this->_request = new request;
|
||||||
throw new Exception(i18n::_('ZeroBin requires configuration section [%s] to be present in configuration file.', $section), 2);
|
$this->_urlbase = array_key_exists('REQUEST_URI', $_SERVER) ? $_SERVER['REQUEST_URI'] : '/';
|
||||||
}
|
|
||||||
}
|
|
||||||
$this->_model = $this->_conf['model']['class'];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
// set default language
|
||||||
* get the model, create one if needed
|
$lang = $this->_conf->getKey('languagedefault');
|
||||||
*
|
i18n::setLanguageFallback($lang);
|
||||||
* @access private
|
// force default language, if language selection is disabled and a default is set
|
||||||
* @return zerobin_abstract
|
if (!$this->_conf->getKey('languageselection') && strlen($lang) == 2)
|
||||||
*/
|
{
|
||||||
private function _model()
|
$_COOKIE['lang'] = $lang;
|
||||||
{
|
setcookie('lang', $lang);
|
||||||
// if needed, initialize the model
|
|
||||||
if(is_string($this->_model)) {
|
|
||||||
$this->_model = forward_static_call(
|
|
||||||
array($this->_model, 'getInstance'),
|
|
||||||
$this->_conf['model_options']
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
return $this->_model;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -203,6 +209,7 @@ class zerobin
|
|||||||
* formatter (optional) = format to display the paste as (plaintext,syntaxhighlighting,markdown) (default:syntaxhighlighting)
|
* formatter (optional) = format to display the paste as (plaintext,syntaxhighlighting,markdown) (default:syntaxhighlighting)
|
||||||
* burnafterreading (optional) = if this paste may only viewed once ? (0/1) (default:0)
|
* burnafterreading (optional) = if this paste may only viewed once ? (0/1) (default:0)
|
||||||
* opendiscusssion (optional) = is the discussion allowed on this paste ? (0/1) (default:0)
|
* opendiscusssion (optional) = is the discussion allowed on this paste ? (0/1) (default:0)
|
||||||
|
* attachmentname = json encoded SJCL encrypted text (containing keys: iv,v,iter,ks,ts,mode,adata,cipher,salt,ct)
|
||||||
* nickname (optional) = in discussion, encoded SJCL encrypted text nickname of author of comment (containing keys: iv,v,iter,ks,ts,mode,adata,cipher,salt,ct)
|
* nickname (optional) = in discussion, encoded 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.
|
* parentid (optional) = in discussion, which comment this comment replies to.
|
||||||
* pasteid (optional) = in discussion, which paste this comment belongs to.
|
* pasteid (optional) = in discussion, which paste this comment belongs to.
|
||||||
@@ -214,34 +221,21 @@ class zerobin
|
|||||||
{
|
{
|
||||||
$error = false;
|
$error = false;
|
||||||
|
|
||||||
$has_attachment = array_key_exists('attachment', $_POST);
|
// Ensure last paste from visitors IP address was more than configured amount of seconds ago.
|
||||||
$has_attachmentname = $has_attachment && array_key_exists('attachmentname', $_POST) && !empty($_POST['attachmentname']);
|
trafficlimiter::setConfiguration($this->_conf);
|
||||||
$data = array_key_exists('data', $_POST) ? $_POST['data'] : '';
|
if (!trafficlimiter::canPass()) return $this->_return_message(
|
||||||
$attachment = $has_attachment ? $_POST['attachment'] : '';
|
1, i18n::_(
|
||||||
$attachmentname = $has_attachmentname ? $_POST['attachmentname'] : '';
|
|
||||||
|
|
||||||
// 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']);
|
|
||||||
$ipKey = 'REMOTE_ADDR';
|
|
||||||
if (array_key_exists('header', $this->_conf['traffic']))
|
|
||||||
{
|
|
||||||
$header = 'HTTP_' . $this->_conf['traffic']['header'];
|
|
||||||
if (array_key_exists($header, $_SERVER) && !empty($_SERVER[$header]))
|
|
||||||
{
|
|
||||||
$ipKey = $header;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!trafficlimiter::canPass($_SERVER[$ipKey])) return $this->_return_message(
|
|
||||||
1,
|
|
||||||
i18n::_(
|
|
||||||
'Please wait %d seconds between each post.',
|
'Please wait %d seconds between each post.',
|
||||||
$this->_conf['traffic']['limit']
|
$this->_conf->getKey('limit', 'traffic')
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
// Make sure content is not too big.
|
$data = $this->_request->getParam('data');
|
||||||
$sizelimit = (int) $this->_getMainConfig('sizelimit', 2097152);
|
$attachment = $this->_request->getParam('attachment');
|
||||||
|
$attachmentname = $this->_request->getParam('attachmentname');
|
||||||
|
|
||||||
|
// Ensure content is not too big.
|
||||||
|
$sizelimit = $this->_conf->getKey('sizelimit');
|
||||||
if (
|
if (
|
||||||
strlen($data) + strlen($attachment) + strlen($attachmentname) > $sizelimit
|
strlen($data) + strlen($attachment) + strlen($attachmentname) > $sizelimit
|
||||||
) return $this->_return_message(
|
) return $this->_return_message(
|
||||||
@@ -252,181 +246,62 @@ class zerobin
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
// Make sure format is correct.
|
|
||||||
if (!sjcl::isValid($data)) return $this->_return_message(1, 'Invalid data.');
|
|
||||||
|
|
||||||
// Make sure attachments are enabled and format is correct.
|
|
||||||
if($has_attachment)
|
|
||||||
{
|
|
||||||
if (
|
|
||||||
!$this->_getMainConfig('fileupload', false) ||
|
|
||||||
!sjcl::isValid($attachment) ||
|
|
||||||
!($has_attachmentname && sjcl::isValid($attachmentname))
|
|
||||||
) return $this->_return_message(1, 'Invalid attachment.');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read additional meta-information.
|
|
||||||
$meta = array();
|
|
||||||
|
|
||||||
// Read expiration date
|
|
||||||
if (array_key_exists('expire', $_POST) && !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 (array_key_exists('burnafterreading', $_POST) && !empty($_POST['burnafterreading']))
|
|
||||||
{
|
|
||||||
$burnafterreading = $_POST['burnafterreading'];
|
|
||||||
if ($burnafterreading !== '0')
|
|
||||||
{
|
|
||||||
if ($burnafterreading !== '1') $error = true;
|
|
||||||
$meta['burnafterreading'] = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read open discussion flag.
|
|
||||||
if (
|
|
||||||
$this->_getMainConfig('discussion', true) &&
|
|
||||||
array_key_exists('opendiscussion', $_POST) &&
|
|
||||||
!empty($_POST['opendiscussion'])
|
|
||||||
)
|
|
||||||
{
|
|
||||||
$opendiscussion = $_POST['opendiscussion'];
|
|
||||||
if ($opendiscussion !== '0')
|
|
||||||
{
|
|
||||||
if ($opendiscussion !== '1') $error = true;
|
|
||||||
$meta['opendiscussion'] = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read formatter flag.
|
|
||||||
if (array_key_exists('formatter', $_POST) && !empty($_POST['formatter']))
|
|
||||||
{
|
|
||||||
$formatter = $_POST['formatter'];
|
|
||||||
if (!array_key_exists($formatter, $this->_conf['formatter_options']))
|
|
||||||
{
|
|
||||||
$formatter = $this->_getMainConfig('defaultformatter', 'plaintext');
|
|
||||||
}
|
|
||||||
$meta['formatter'] = $formatter;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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;
|
|
||||||
}
|
|
||||||
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) return $this->_return_message(1, 'Invalid data.');
|
|
||||||
|
|
||||||
// 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.
|
// The user posts a comment.
|
||||||
if (
|
$pasteid = $this->_request->getParam('pasteid');
|
||||||
!empty($_POST['parentid']) &&
|
$parentid = $this->_request->getParam('parentid');
|
||||||
!empty($_POST['pasteid'])
|
if (!empty($pasteid) && !empty($parentid))
|
||||||
)
|
|
||||||
{
|
{
|
||||||
$pasteid = (string) $_POST['pasteid'];
|
$paste = $this->_model->getPaste($pasteid);
|
||||||
$parentid = (string) $_POST['parentid'];
|
if ($paste->exists()) {
|
||||||
if (
|
try {
|
||||||
!filter::is_valid_paste_id($pasteid) ||
|
$comment = $paste->getComment($parentid);
|
||||||
!filter::is_valid_paste_id($parentid)
|
|
||||||
) return $this->_return_message(1, 'Invalid data.');
|
|
||||||
|
|
||||||
// Comments do not expire (it's the paste that expires)
|
$nickname = $this->_request->getParam('nickname');
|
||||||
unset($storage['expire_date']);
|
if (!empty($nickname)) $comment->setNickname($nickname);
|
||||||
unset($storage['opendiscussion']);
|
|
||||||
|
|
||||||
// Make sure paste exists.
|
$comment->setData($data);
|
||||||
if (
|
$comment->store();
|
||||||
!$this->_model()->exists($pasteid)
|
} catch(Exception $e) {
|
||||||
) return $this->_return_message(1, 'Invalid data.');
|
return $this->_return_message(1, $e->getMessage());
|
||||||
|
}
|
||||||
// Make sure the discussion is opened in this paste.
|
$this->_return_message(0, $comment->getId());
|
||||||
$paste = $this->_model()->read($pasteid);
|
}
|
||||||
if (
|
else
|
||||||
!$paste->meta->opendiscussion
|
{
|
||||||
) return $this->_return_message(1, 'Invalid data.');
|
$this->_return_message(1, 'Invalid data.');
|
||||||
|
}
|
||||||
// Check for improbable collision.
|
|
||||||
if (
|
|
||||||
$this->_model()->existsComment($pasteid, $parentid, $dataid)
|
|
||||||
) return $this->_return_message(1, 'You are unlucky. Try again.');
|
|
||||||
|
|
||||||
// New comment
|
|
||||||
if (
|
|
||||||
$this->_model()->createComment($pasteid, $parentid, $dataid, $storage) === false
|
|
||||||
) return $this->_return_message(1, 'Error saving comment. Sorry.');
|
|
||||||
|
|
||||||
// 0 = no error
|
|
||||||
return $this->_return_message(0, $dataid);
|
|
||||||
}
|
}
|
||||||
// The user posts a standard paste.
|
// The user posts a standard paste.
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Check for improbable collision.
|
$paste = $this->_model->getPaste();
|
||||||
if (
|
try {
|
||||||
$this->_model()->exists($dataid)
|
$paste->setData($data);
|
||||||
) return $this->_return_message(1, 'You are unlucky. Try again.');
|
|
||||||
|
|
||||||
// Add attachment and its name, if one was sent
|
if (!empty($attachment))
|
||||||
if ($has_attachment) $storage['attachment'] = $attachment;
|
{
|
||||||
if ($has_attachmentname) $storage['attachmentname'] = $attachmentname;
|
$paste->setAttachment($attachment);
|
||||||
|
if (!empty($attachmentname))
|
||||||
|
$paste->setAttachmentName($attachmentname);
|
||||||
|
}
|
||||||
|
|
||||||
// New paste
|
$expire = $this->_request->getParam('expire');
|
||||||
if (
|
if (!empty($expire)) $paste->setExpiration($expire);
|
||||||
$this->_model()->create($dataid, $storage) === false
|
|
||||||
) return $this->_return_message(1, 'Error saving paste. Sorry.');
|
|
||||||
|
|
||||||
// Generate the "delete" token.
|
$burnafterreading = $this->_request->getParam('burnafterreading');
|
||||||
// The token is the hmac of the pasteid signed with the server salt.
|
if (!empty($burnafterreading)) $paste->setBurnafterreading($burnafterreading);
|
||||||
// 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
|
$opendiscussion = $this->_request->getParam('opendiscussion');
|
||||||
return $this->_return_message(0, $dataid, array('deletetoken' => $deletetoken));
|
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());
|
||||||
|
}
|
||||||
|
$this->_return_message(0, $paste->getId(), array('deletetoken' => $paste->getDeleteToken()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -440,63 +315,48 @@ class zerobin
|
|||||||
*/
|
*/
|
||||||
private function _delete($dataid, $deletetoken)
|
private function _delete($dataid, $deletetoken)
|
||||||
{
|
{
|
||||||
// Is this a valid paste identifier?
|
try {
|
||||||
if (!filter::is_valid_paste_id($dataid))
|
$paste = $this->_model->getPaste($dataid);
|
||||||
{
|
if ($paste->exists())
|
||||||
$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
|
|
||||||
)
|
|
||||||
{
|
{
|
||||||
// Delete the paste
|
// accessing this property ensures that the paste would be
|
||||||
$this->_model()->delete($dataid);
|
// deleted if it has already expired
|
||||||
$this->_return_message(0, $dataid);
|
$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
|
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.';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -508,80 +368,23 @@ class zerobin
|
|||||||
*/
|
*/
|
||||||
private function _read($dataid)
|
private function _read($dataid)
|
||||||
{
|
{
|
||||||
$isJson = false;
|
try {
|
||||||
if (($pos = strpos($dataid, '&json')) !== false) {
|
$paste = $this->_model->getPaste($dataid);
|
||||||
$isJson = true;
|
if ($paste->exists())
|
||||||
$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()
|
|
||||||
)
|
|
||||||
{
|
{
|
||||||
// Delete the paste
|
$data = $paste->get();
|
||||||
$this->_model()->delete($dataid);
|
$this->_doesExpire = property_exists($data, 'meta') && property_exists($data->meta, 'expire_date');
|
||||||
$this->_error = self::GENERIC_ERROR;
|
$this->_data = json_encode($data);
|
||||||
}
|
}
|
||||||
// If no error, return the paste.
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// We kindly provide the remaining time before expiration (in seconds)
|
$this->_error = self::GENERIC_ERROR;
|
||||||
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)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// set formatter for for the view.
|
|
||||||
if (!property_exists($paste->meta, 'formatter'))
|
|
||||||
{
|
|
||||||
// support < 0.21 syntax highlighting
|
|
||||||
if (property_exists($paste->meta, 'syntaxcoloring') && $paste->meta->syntaxcoloring === true)
|
|
||||||
{
|
|
||||||
$paste->meta->formatter = 'syntaxhighlighting';
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
$paste->meta->formatter = $this->_getMainConfig('defaultformatter', 'plaintext');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->_data = json_encode($messages);
|
|
||||||
}
|
}
|
||||||
|
} catch (Exception $e) {
|
||||||
|
$this->_error = $e->getMessage();
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
if ($this->_request->isJsonApiCall())
|
||||||
$this->_error = self::GENERIC_ERROR;
|
|
||||||
}
|
|
||||||
if ($isJson)
|
|
||||||
{
|
{
|
||||||
if (strlen($this->_error))
|
if (strlen($this->_error))
|
||||||
{
|
{
|
||||||
@@ -589,7 +392,7 @@ class zerobin
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
$this->_return_message(0, $dataid, array('messages' => $messages));
|
$this->_return_message(0, $dataid, json_decode($this->_data, true));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -612,17 +415,17 @@ class zerobin
|
|||||||
|
|
||||||
// label all the expiration options
|
// label all the expiration options
|
||||||
$expire = array();
|
$expire = array();
|
||||||
foreach ($this->_conf['expire_options'] as $time => $seconds)
|
foreach ($this->_conf->getSection('expire_options') as $time => $seconds)
|
||||||
{
|
{
|
||||||
$expire[$time] = ($seconds == 0) ? i18n::_(ucfirst($time)): filter::time_humanreadable($time);
|
$expire[$time] = ($seconds == 0) ? i18n::_(ucfirst($time)): filter::time_humanreadable($time);
|
||||||
}
|
}
|
||||||
|
|
||||||
// translate all the formatter options
|
// translate all the formatter options
|
||||||
$formatters = array_map(array('i18n', 'translate'), $this->_conf['formatter_options']);
|
$formatters = array_map(array('i18n', 'translate'), $this->_conf->getSection('formatter_options'));
|
||||||
|
|
||||||
// set language cookie if that functionality was enabled
|
// set language cookie if that functionality was enabled
|
||||||
$languageselection = '';
|
$languageselection = '';
|
||||||
if ($this->_getMainConfig('languageselection', false))
|
if ($this->_conf->getKey('languageselection'))
|
||||||
{
|
{
|
||||||
$languageselection = i18n::getLanguage();
|
$languageselection = i18n::getLanguage();
|
||||||
setcookie('lang', $languageselection);
|
setcookie('lang', $languageselection);
|
||||||
@@ -635,42 +438,61 @@ class zerobin
|
|||||||
$page->assign('ERROR', i18n::_($this->_error));
|
$page->assign('ERROR', i18n::_($this->_error));
|
||||||
$page->assign('STATUS', i18n::_($this->_status));
|
$page->assign('STATUS', i18n::_($this->_status));
|
||||||
$page->assign('VERSION', self::VERSION);
|
$page->assign('VERSION', self::VERSION);
|
||||||
$page->assign('DISCUSSION', $this->_getMainConfig('discussion', true));
|
$page->assign('DISCUSSION', $this->_conf->getKey('discussion'));
|
||||||
$page->assign('OPENDISCUSSION', $this->_getMainConfig('opendiscussion', true));
|
$page->assign('OPENDISCUSSION', $this->_conf->getKey('opendiscussion'));
|
||||||
$page->assign('MARKDOWN', array_key_exists('markdown', $formatters));
|
$page->assign('MARKDOWN', array_key_exists('markdown', $formatters));
|
||||||
$page->assign('SYNTAXHIGHLIGHTING', array_key_exists('syntaxhighlighting', $formatters));
|
$page->assign('SYNTAXHIGHLIGHTING', array_key_exists('syntaxhighlighting', $formatters));
|
||||||
$page->assign('SYNTAXHIGHLIGHTINGTHEME', $this->_getMainConfig('syntaxhighlightingtheme', ''));
|
$page->assign('SYNTAXHIGHLIGHTINGTHEME', $this->_conf->getKey('syntaxhighlightingtheme'));
|
||||||
$page->assign('FORMATTER', $formatters);
|
$page->assign('FORMATTER', $formatters);
|
||||||
$page->assign('FORMATTERDEFAULT', $this->_getMainConfig('defaultformatter', 'plaintext'));
|
$page->assign('FORMATTERDEFAULT', $this->_conf->getKey('defaultformatter'));
|
||||||
$page->assign('NOTICE', i18n::_($this->_getMainConfig('notice', '')));
|
$page->assign('NOTICE', i18n::_($this->_conf->getKey('notice')));
|
||||||
$page->assign('BURNAFTERREADINGSELECTED', $this->_getMainConfig('burnafterreadingselected', false));
|
$page->assign('BURNAFTERREADINGSELECTED', $this->_conf->getKey('burnafterreadingselected'));
|
||||||
$page->assign('PASSWORD', $this->_getMainConfig('password', true));
|
$page->assign('PASSWORD', $this->_conf->getKey('password'));
|
||||||
$page->assign('FILEUPLOAD', $this->_getMainConfig('fileupload', false));
|
$page->assign('FILEUPLOAD', $this->_conf->getKey('fileupload'));
|
||||||
$page->assign('BASE64JSVERSION', $this->_getMainConfig('base64version', '2.1.9'));
|
$page->assign('BASE64JSVERSION', $this->_conf->getKey('base64version'));
|
||||||
$page->assign('LANGUAGESELECTION', $languageselection);
|
$page->assign('LANGUAGESELECTION', $languageselection);
|
||||||
$page->assign('LANGUAGES', i18n::getLanguageLabels(i18n::getAvailableLanguages()));
|
$page->assign('LANGUAGES', i18n::getLanguageLabels(i18n::getAvailableLanguages()));
|
||||||
$page->assign('EXPIRE', $expire);
|
$page->assign('EXPIRE', $expire);
|
||||||
$page->assign('EXPIREDEFAULT', $this->_conf['expire']['default']);
|
$page->assign('EXPIREDEFAULT', $this->_conf->getKey('default', 'expire'));
|
||||||
$page->draw($this->_getMainConfig('template', 'page'));
|
$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
|
* @access private
|
||||||
* @param string $option
|
* @param string $type
|
||||||
* @param mixed $default (optional)
|
* @return void
|
||||||
* @return mixed
|
|
||||||
*/
|
*/
|
||||||
private function _getMainConfig($option, $default = false)
|
private function _jsonld($type)
|
||||||
{
|
{
|
||||||
return array_key_exists($option, $this->_conf['main']) ?
|
if (
|
||||||
$this->_conf['main'][$option] :
|
$type !== 'paste' && $type !== 'comment' &&
|
||||||
$default;
|
$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
|
* @access private
|
||||||
* @param bool $status
|
* @param bool $status
|
||||||
@@ -688,6 +510,7 @@ class zerobin
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
$result['id'] = $message;
|
$result['id'] = $message;
|
||||||
|
$result['url'] = $this->_urlbase . '?' . $message;
|
||||||
}
|
}
|
||||||
$result += $other;
|
$result += $other;
|
||||||
$this->_json = json_encode($result);
|
$this->_json = json_encode($result);
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
* @link http://sebsauvage.net/wiki/doku.php?id=php:zerobin
|
* @link http://sebsauvage.net/wiki/doku.php?id=php:zerobin
|
||||||
* @copyright 2012 Sébastien SAUVAGE (sebsauvage.net)
|
* @copyright 2012 Sébastien SAUVAGE (sebsauvage.net)
|
||||||
* @license http://www.opensource.org/licenses/zlib-license.php The zlib/libpng License
|
* @license http://www.opensource.org/licenses/zlib-license.php The zlib/libpng License
|
||||||
* @version 0.21
|
* @version 0.22
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -17,12 +17,12 @@
|
|||||||
*/
|
*/
|
||||||
abstract class zerobin_abstract
|
abstract class zerobin_abstract
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* singleton instance
|
* singleton instance
|
||||||
*
|
*
|
||||||
* @access protected
|
* @access protected
|
||||||
* @static
|
* @static
|
||||||
* @var zerobin
|
* @var zerobin_abstract
|
||||||
*/
|
*/
|
||||||
protected static $_instance = null;
|
protected static $_instance = null;
|
||||||
|
|
||||||
@@ -87,7 +87,7 @@ abstract class zerobin_abstract
|
|||||||
*
|
*
|
||||||
* @access public
|
* @access public
|
||||||
* @param string $dataid
|
* @param string $dataid
|
||||||
* @return void
|
* @return bool
|
||||||
*/
|
*/
|
||||||
abstract public function exists($pasteid);
|
abstract public function exists($pasteid);
|
||||||
|
|
||||||
@@ -122,4 +122,24 @@ abstract class zerobin_abstract
|
|||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
abstract public function existsComment($pasteid, $parentid, $commentid);
|
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
|
* @link http://sebsauvage.net/wiki/doku.php?id=php:zerobin
|
||||||
* @copyright 2012 Sébastien SAUVAGE (sebsauvage.net)
|
* @copyright 2012 Sébastien SAUVAGE (sebsauvage.net)
|
||||||
* @license http://www.opensource.org/licenses/zlib-license.php The zlib/libpng License
|
* @license http://www.opensource.org/licenses/zlib-license.php The zlib/libpng License
|
||||||
* @version 0.21
|
* @version 0.22
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -75,9 +75,20 @@ class zerobin_data extends zerobin_abstract
|
|||||||
public function read($pasteid)
|
public function read($pasteid)
|
||||||
{
|
{
|
||||||
if(!$this->exists($pasteid)) return false;
|
if(!$this->exists($pasteid)) return false;
|
||||||
return json_decode(
|
$paste = json_decode(
|
||||||
file_get_contents(self::_dataid2path($pasteid) . $pasteid)
|
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)
|
public function delete($pasteid)
|
||||||
{
|
{
|
||||||
// Delete the paste itself.
|
// Delete the paste itself.
|
||||||
unlink(self::_dataid2path($pasteid) . $pasteid);
|
@unlink(self::_dataid2path($pasteid) . $pasteid);
|
||||||
|
|
||||||
// Delete discussion if it exists.
|
// Delete discussion if it exists.
|
||||||
$discdir = self::_dataid2discussionpath($pasteid);
|
$discdir = self::_dataid2discussionpath($pasteid);
|
||||||
@@ -100,7 +111,7 @@ class zerobin_data extends zerobin_abstract
|
|||||||
$dir = dir($discdir);
|
$dir = dir($discdir);
|
||||||
while (false !== ($filename = $dir->read()))
|
while (false !== ($filename = $dir->read()))
|
||||||
{
|
{
|
||||||
if (is_file($discdir.$filename)) unlink($discdir.$filename);
|
if (is_file($discdir.$filename)) @unlink($discdir.$filename);
|
||||||
}
|
}
|
||||||
$dir->close();
|
$dir->close();
|
||||||
|
|
||||||
@@ -161,16 +172,17 @@ class zerobin_data extends zerobin_abstract
|
|||||||
// - pasteid is the paste this reply belongs to.
|
// - pasteid is the paste this reply belongs to.
|
||||||
// - commentid is the comment identifier itself.
|
// - commentid is the comment identifier itself.
|
||||||
// - parentid is the comment this comment replies to (It can be pasteid)
|
// - 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);
|
$items = explode('.', $filename);
|
||||||
// Add some meta information not contained in file.
|
// Add some meta information not contained in file.
|
||||||
$comment->meta->commentid=$items[1];
|
$comment->id = $items[1];
|
||||||
$comment->meta->parentid=$items[2];
|
$comment->parentid = $items[2];
|
||||||
|
|
||||||
// Store in array
|
// Store in array
|
||||||
$comments[$comment->meta->postdate]=$comment;
|
$key = $this->getOpenSlot($comments, (int) $comment->meta->postdate);
|
||||||
|
$comments[$key] = $comment;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$dir->close();
|
$dir->close();
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
* @link http://sebsauvage.net/wiki/doku.php?id=php:zerobin
|
* @link http://sebsauvage.net/wiki/doku.php?id=php:zerobin
|
||||||
* @copyright 2012 Sébastien SAUVAGE (sebsauvage.net)
|
* @copyright 2012 Sébastien SAUVAGE (sebsauvage.net)
|
||||||
* @license http://www.opensource.org/licenses/zlib-license.php The zlib/libpng License
|
* @license http://www.opensource.org/licenses/zlib-license.php The zlib/libpng License
|
||||||
* @version 0.21
|
* @version 0.22
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -80,96 +80,63 @@ class zerobin_db extends zerobin_abstract
|
|||||||
array_key_exists('opt', $options)
|
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(
|
self::$_type = strtolower(
|
||||||
substr($options['dsn'], 0, strpos($options['dsn'], ':'))
|
substr($options['dsn'], 0, strpos($options['dsn'], ':'))
|
||||||
);
|
);
|
||||||
switch(self::$_type)
|
$tableQuery = self::_getTableQuery(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
|
|
||||||
);
|
|
||||||
}
|
|
||||||
self::$_db = new PDO(
|
self::$_db = new PDO(
|
||||||
$options['dsn'],
|
$options['dsn'],
|
||||||
$options['usr'],
|
$options['usr'],
|
||||||
$options['pwd'],
|
$options['pwd'],
|
||||||
$options['opt']
|
$options['opt']
|
||||||
);
|
);
|
||||||
$statement = self::$_db->query($sql);
|
|
||||||
$tables = $statement->fetchAll(PDO::FETCH_COLUMN, 0);
|
|
||||||
|
|
||||||
// create paste table if needed
|
// check if the database contains the required tables
|
||||||
if (!array_key_exists(self::$_prefix . 'paste', $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(
|
self::_createPasteTable();
|
||||||
'CREATE TABLE ' . self::$_prefix . 'paste ( ' .
|
$db_tables_exist = false;
|
||||||
'dataid CHAR(16), ' .
|
|
||||||
'data TEXT, ' .
|
|
||||||
'postdate INT, ' .
|
|
||||||
'expiredate INT, ' .
|
|
||||||
'opendiscussion INT, ' .
|
|
||||||
'burnafterreading INT );'
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// create comment table if needed
|
// create comment table if necessary
|
||||||
if (!array_key_exists(self::$_prefix . 'comment', $tables))
|
if (!in_array(self::$_prefix . 'comment', $tables))
|
||||||
{
|
{
|
||||||
self::$_db->exec(
|
self::_createCommentTable();
|
||||||
'CREATE TABLE ' . self::$_prefix . 'comment ( ' .
|
$db_tables_exist = false;
|
||||||
'dataid CHAR(16), ' .
|
}
|
||||||
'pasteid CHAR(16), ' .
|
|
||||||
'parentid CHAR(16), ' .
|
// create config table if necessary
|
||||||
'data TEXT, ' .
|
$db_version = zerobin::VERSION;
|
||||||
'nickname VARCHAR(255), ' .
|
if (!in_array(self::$_prefix . 'config', $tables))
|
||||||
'vizhash TEXT, ' .
|
{
|
||||||
'postdate INT );'
|
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)
|
public function create($pasteid, $paste)
|
||||||
{
|
{
|
||||||
if (
|
if (
|
||||||
array_key_exists($pasteid, self::$_cache)
|
array_key_exists($pasteid, self::$_cache)
|
||||||
) {
|
) {
|
||||||
if(false !== self::$_cache[$pasteid]) {
|
if(false !== self::$_cache[$pasteid]) {
|
||||||
return false;
|
return false;
|
||||||
@@ -192,21 +159,48 @@ class zerobin_db extends zerobin_abstract
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
$opendiscussion = $burnafterreading = false;
|
||||||
!array_key_exists('opendiscussion', $paste['meta'])
|
$attachment = $attachmentname = '';
|
||||||
) $paste['meta']['opendiscussion'] = false;
|
$meta = $paste['meta'];
|
||||||
if (
|
unset($meta['postdate']);
|
||||||
!array_key_exists('burnafterreading', $paste['meta'])
|
$expire_date = 0;
|
||||||
) $paste['meta']['burnafterreading'] = false;
|
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(
|
return self::_exec(
|
||||||
'INSERT INTO ' . self::$_prefix . 'paste VALUES(?,?,?,?,?,?)',
|
'INSERT INTO ' . self::$_prefix . 'paste VALUES(?,?,?,?,?,?,?,?,?)',
|
||||||
array(
|
array(
|
||||||
$pasteid,
|
$pasteid,
|
||||||
$paste['data'],
|
$paste['data'],
|
||||||
$paste['meta']['postdate'],
|
$paste['meta']['postdate'],
|
||||||
$paste['meta']['expire_date'],
|
$expire_date,
|
||||||
(int) $paste['meta']['opendiscussion'],
|
(int) $opendiscussion,
|
||||||
(int) $paste['meta']['burnafterreading'],
|
(int) $burnafterreading,
|
||||||
|
json_encode($meta),
|
||||||
|
$attachment,
|
||||||
|
$attachmentname,
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -233,9 +227,36 @@ class zerobin_db extends zerobin_abstract
|
|||||||
// create object
|
// create object
|
||||||
self::$_cache[$pasteid] = new stdClass;
|
self::$_cache[$pasteid] = new stdClass;
|
||||||
self::$_cache[$pasteid]->data = $paste['data'];
|
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->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 (
|
if (
|
||||||
$paste['opendiscussion']
|
$paste['opendiscussion']
|
||||||
) self::$_cache[$pasteid]->meta->opendiscussion = true;
|
) self::$_cache[$pasteid]->meta->opendiscussion = true;
|
||||||
@@ -325,24 +346,23 @@ class zerobin_db extends zerobin_abstract
|
|||||||
array($pasteid)
|
array($pasteid)
|
||||||
);
|
);
|
||||||
|
|
||||||
// create object
|
|
||||||
$commentTemplate = new stdClass;
|
|
||||||
$commentTemplate->meta = new stdClass;
|
|
||||||
|
|
||||||
// create comment list
|
// create comment list
|
||||||
$comments = array();
|
$comments = array();
|
||||||
if (count($rows))
|
if (count($rows))
|
||||||
{
|
{
|
||||||
foreach ($rows as $row)
|
foreach ($rows as $row)
|
||||||
{
|
{
|
||||||
$i = (int) $row['postdate'];
|
$i = $this->getOpenSlot($comments, (int) $row['postdate']);
|
||||||
$comments[$i] = clone $commentTemplate;
|
$comments[$i] = new stdClass;
|
||||||
|
$comments[$i]->id = $row['dataid'];
|
||||||
|
$comments[$i]->parentid = $row['parentid'];
|
||||||
$comments[$i]->data = $row['data'];
|
$comments[$i]->data = $row['data'];
|
||||||
$comments[$i]->meta->nickname = $row['nickname'];
|
$comments[$i]->meta = new stdClass;
|
||||||
$comments[$i]->meta->vizhash = $row['vizhash'];
|
$comments[$i]->meta->postdate = (int) $row['postdate'];
|
||||||
$comments[$i]->meta->postdate = $i;
|
if (array_key_exists('nickname', $row))
|
||||||
$comments[$i]->meta->commentid = $row['dataid'];
|
$comments[$i]->meta->nickname = $row['nickname'];
|
||||||
$comments[$i]->meta->parentid = $row['parentid'];
|
if (array_key_exists('vizhash', $row))
|
||||||
|
$comments[$i]->meta->vizhash = $row['vizhash'];
|
||||||
}
|
}
|
||||||
ksort($comments);
|
ksort($comments);
|
||||||
}
|
}
|
||||||
@@ -406,4 +426,227 @@ class zerobin_db extends zerobin_abstract
|
|||||||
$statement->closeCursor();
|
$statement->closeCursor();
|
||||||
return $result;
|
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);'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,6 +23,11 @@
|
|||||||
<!--[if lt IE 10]>
|
<!--[if lt IE 10]>
|
||||||
<style type="text/css">#ienotice {display:block !important;} #oldienotice {display:block !important;}</style>
|
<style type="text/css">#ienotice {display:block !important;} #oldienotice {display:block !important;}</style>
|
||||||
<![endif]-->
|
<![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>
|
</head>
|
||||||
<body role="document" class="navbar-spacing">
|
<body role="document" class="navbar-spacing">
|
||||||
<nav class="navbar navbar-default navbar-fixed-top">
|
<nav class="navbar navbar-default navbar-fixed-top">
|
||||||
@@ -38,13 +43,13 @@
|
|||||||
</div>
|
</div>
|
||||||
<div id="navbar" class="navbar-collapse collapse">
|
<div id="navbar" class="navbar-collapse collapse">
|
||||||
<ul class="nav navbar-nav">
|
<ul class="nav navbar-nav">
|
||||||
<li class="pr">
|
<li>
|
||||||
<button id="sendbutton" type="button" class="hidden btn btn-default navbar-btn">
|
<button id="sendbutton" type="button" class="hidden btn btn-default navbar-btn">
|
||||||
<span class="glyphicon glyphicon-upload" aria-hidden="true"></span> {function="t('Send')"}
|
<span class="glyphicon glyphicon-upload" aria-hidden="true"></span> {function="t('Send')"}
|
||||||
</button>
|
</button>{if="$EXPIRECLONE"}
|
||||||
<button id="clonebutton" type="button" class="hidden btn btn-default navbar-btn">
|
<button id="clonebutton" type="button" class="hidden btn btn-default navbar-btn">
|
||||||
<span class="glyphicon glyphicon-duplicate" aria-hidden="true"></span> {function="t('Clone')"}
|
<span class="glyphicon glyphicon-duplicate" aria-hidden="true"></span> {function="t('Clone')"}
|
||||||
</button>
|
</button>{/if}
|
||||||
<button id="rawtextbutton" type="button" class="hidden btn btn-default navbar-btn">
|
<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')"}
|
<span class="glyphicon glyphicon-text-background" aria-hidden="true"></span> {function="t('Raw text')"}
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
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>
|
||||||
@@ -23,10 +23,14 @@
|
|||||||
<!--[if lt IE 10]>
|
<!--[if lt IE 10]>
|
||||||
<style type="text/css">#ienotice {display:block !important;} #oldienotice {display:block !important;}</style>
|
<style type="text/css">#ienotice {display:block !important;} #oldienotice {display:block !important;}</style>
|
||||||
<![endif]-->
|
<![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>
|
</head>
|
||||||
<body role="document">
|
<body role="document">
|
||||||
<nav class="navbar navbar-default navbar-static-top">
|
<nav class="navbar navbar-default navbar-static-top">
|
||||||
<div class="container">
|
|
||||||
<div class="navbar-header">
|
<div class="navbar-header">
|
||||||
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
|
<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="sr-only">{function="t('Toggle navigation')"}</span>
|
||||||
@@ -38,13 +42,13 @@
|
|||||||
</div>
|
</div>
|
||||||
<div id="navbar" class="navbar-collapse collapse">
|
<div id="navbar" class="navbar-collapse collapse">
|
||||||
<ul class="nav navbar-nav">
|
<ul class="nav navbar-nav">
|
||||||
<li class="pr">
|
<li>
|
||||||
<button id="sendbutton" type="button" class="hidden btn btn-default navbar-btn">
|
<button id="newbutton" type="button" class="reloadlink hidden btn btn-default navbar-btn">
|
||||||
<span class="glyphicon glyphicon-upload" aria-hidden="true"></span> {function="t('Send')"}
|
<span class="glyphicon glyphicon-file" aria-hidden="true"></span> {function="t('New')"}
|
||||||
</button>
|
</button>{if="$EXPIRECLONE"}
|
||||||
<button id="clonebutton" type="button" class="hidden btn btn-default navbar-btn">
|
<button id="clonebutton" type="button" class="hidden btn btn-default navbar-btn">
|
||||||
<span class="glyphicon glyphicon-duplicate" aria-hidden="true"></span> {function="t('Clone')"}
|
<span class="glyphicon glyphicon-duplicate" aria-hidden="true"></span> {function="t('Clone')"}
|
||||||
</button>
|
</button>{/if}
|
||||||
<button id="rawtextbutton" type="button" class="hidden btn btn-default navbar-btn">
|
<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')"}
|
<span class="glyphicon glyphicon-text-background" aria-hidden="true"></span> {function="t('Raw text')"}
|
||||||
</button>
|
</button>
|
||||||
@@ -123,18 +127,17 @@
|
|||||||
{loop="LANGUAGES"}
|
{loop="LANGUAGES"}
|
||||||
<li>
|
<li>
|
||||||
<a href="#" class="reloadlink" onclick="document.cookie='lang={$key}';">
|
<a href="#" class="reloadlink" onclick="document.cookie='lang={$key}';">
|
||||||
{$value[0]} ({$value[1]})
|
{$value[0]} ({$value[1]})
|
||||||
</a>
|
</a>
|
||||||
</li>{/loop}
|
</li>{/loop}
|
||||||
</ul>
|
</ul>
|
||||||
</li>{/if}
|
</li>{/if}
|
||||||
<li>
|
<li>
|
||||||
<button id="newbutton" type="button" class="reloadlink hidden btn btn-default navbar-btn">
|
<button id="sendbutton" type="button" class="hidden btn btn-primary navbar-btn">
|
||||||
<span class="glyphicon glyphicon-file" aria-hidden="true"></span> {function="t('New')"}
|
<span class="glyphicon glyphicon-upload" aria-hidden="true"></span> {function="t('Send')"}
|
||||||
</button>
|
</button>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
<header class="container">{if="strlen($NOTICE)"}
|
<header class="container">{if="strlen($NOTICE)"}
|
||||||
|
|||||||
@@ -18,6 +18,11 @@
|
|||||||
<!--[if lt IE 10]>
|
<!--[if lt IE 10]>
|
||||||
<style type="text/css">body {padding-left:60px;padding-right:60px;} #ienotice {display:block;} #oldienotice {display:block;}</style>
|
<style type="text/css">body {padding-left:60px;padding-right:60px;} #ienotice {display:block;} #oldienotice {display:block;}</style>
|
||||||
<![endif]-->
|
<![endif]-->
|
||||||
|
<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>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<header>
|
<header>
|
||||||
@@ -43,8 +48,8 @@
|
|||||||
<div id="errormessage" class="hidden">{$ERROR|htmlspecialchars}</div>
|
<div id="errormessage" class="hidden">{$ERROR|htmlspecialchars}</div>
|
||||||
<div id="toolbar">
|
<div id="toolbar">
|
||||||
<button id="newbutton" class="reloadlink hidden"><img src="img/icon_new.png" width="11" height="15" alt="" />{function="t('New')"}</button>
|
<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>
|
<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>
|
<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>
|
<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')"}:
|
<div id="expiration" class="hidden button">{function="t('Expires')"}:
|
||||||
<select id="pasteExpiration" name="pasteExpiration">
|
<select id="pasteExpiration" name="pasteExpiration">
|
||||||
|
|||||||
2
tst/.gitignore
vendored
2
tst/.gitignore
vendored
@@ -1 +1 @@
|
|||||||
/configuration.php
|
/configurationCombinations.php
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
class RainTPLTest extends PHPUnit_Framework_TestCase
|
class RainTPLTest extends PHPUnit_Framework_TestCase
|
||||||
{
|
{
|
||||||
private static $data = '{"iv":"EN39/wd5Nk8HAiSG2K5AsQ","salt":"QKN1DBXe5PI","ct":"8hA83xDdXjD7K2qfmw5NdA"}';
|
|
||||||
|
|
||||||
private static $error = 'foo bar';
|
private static $error = 'foo bar';
|
||||||
|
|
||||||
private static $status = '!*#@?$+';
|
private static $status = '!*#@?$+';
|
||||||
@@ -27,7 +25,7 @@ class RainTPLTest extends PHPUnit_Framework_TestCase
|
|||||||
$page::$path_replace = false;
|
$page::$path_replace = false;
|
||||||
|
|
||||||
// We escape it here because ENT_NOQUOTES can't be used in RainTPL templates.
|
// 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('ERROR', self::$error);
|
||||||
$page->assign('STATUS', self::$status);
|
$page->assign('STATUS', self::$status);
|
||||||
$page->assign('VERSION', self::$version);
|
$page->assign('VERSION', self::$version);
|
||||||
@@ -45,6 +43,7 @@ class RainTPLTest extends PHPUnit_Framework_TestCase
|
|||||||
$page->assign('LANGUAGES', i18n::getLanguageLabels(i18n::getAvailableLanguages()));
|
$page->assign('LANGUAGES', i18n::getLanguageLabels(i18n::getAvailableLanguages()));
|
||||||
$page->assign('EXPIRE', self::$expire);
|
$page->assign('EXPIRE', self::$expire);
|
||||||
$page->assign('EXPIREDEFAULT', self::$expire_default);
|
$page->assign('EXPIREDEFAULT', self::$expire_default);
|
||||||
|
$page->assign('EXPIRECLONE', true);
|
||||||
ob_start();
|
ob_start();
|
||||||
$page->draw('page');
|
$page->draw('page');
|
||||||
$this->_content = ob_get_contents();
|
$this->_content = ob_get_contents();
|
||||||
@@ -65,7 +64,7 @@ class RainTPLTest extends PHPUnit_Framework_TestCase
|
|||||||
$this->assertTag(
|
$this->assertTag(
|
||||||
array(
|
array(
|
||||||
'id' => 'cipherdata',
|
'id' => 'cipherdata',
|
||||||
'content' => htmlspecialchars(self::$data, ENT_NOQUOTES)
|
'content' => htmlspecialchars(helper::getPaste()['data'], ENT_NOQUOTES)
|
||||||
),
|
),
|
||||||
$this->_content,
|
$this->_content,
|
||||||
'outputs data correctly'
|
'outputs data correctly'
|
||||||
|
|||||||
@@ -3,12 +3,143 @@ error_reporting( E_ALL | E_STRICT );
|
|||||||
|
|
||||||
// change this, if your php files and data is outside of your webservers document root
|
// change this, if your php files and data is outside of your webservers document root
|
||||||
if (!defined('PATH')) define('PATH', '..' . DIRECTORY_SEPARATOR);
|
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', '..');
|
if (!defined('PUBLIC_PATH')) define('PUBLIC_PATH', '..');
|
||||||
|
|
||||||
require PATH . 'lib/auto.php';
|
require PATH . 'lib/auto.php';
|
||||||
|
|
||||||
class helper
|
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
|
* delete directory and all its contents recursively
|
||||||
*
|
*
|
||||||
@@ -36,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
|
* create ini file
|
||||||
*
|
*
|
||||||
@@ -54,6 +207,18 @@ class helper
|
|||||||
continue;
|
continue;
|
||||||
} elseif (is_string($setting)) {
|
} elseif (is_string($setting)) {
|
||||||
$setting = '"' . $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 {
|
} else {
|
||||||
$setting = var_export($setting, true);
|
$setting = var_export($setting, true);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ new configurationTestGenerator(array(
|
|||||||
'settings' => array('$_POST["opendiscussion"] = "neither 1 nor 0"'),
|
'settings' => array('$_POST["opendiscussion"] = "neither 1 nor 0"'),
|
||||||
'type' => 'False',
|
'type' => 'False',
|
||||||
'args' => array(
|
'args' => array(
|
||||||
'$this->_model->exists(self::$pasteid)',
|
'$this->_model->exists(helper::getPasteId())',
|
||||||
'when discussions are enabled, but invalid flag posted, paste is not created'
|
'when discussions are enabled, but invalid flag posted, paste is not created'
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -108,134 +108,6 @@ new configurationTestGenerator(array(
|
|||||||
'affects' => $vrd
|
'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(
|
'main/burnafterreadingselected' => array(
|
||||||
array(
|
array(
|
||||||
'setting' => true,
|
'setting' => true,
|
||||||
@@ -528,7 +400,7 @@ class configurationTestGenerator
|
|||||||
*/
|
*/
|
||||||
private function _writeConfigurationTest()
|
private function _writeConfigurationTest()
|
||||||
{
|
{
|
||||||
$defaultOptions = parse_ini_file(PATH . 'cfg' . DIRECTORY_SEPARATOR . 'conf.ini', true);
|
$defaultOptions = parse_ini_file(CONF, true);
|
||||||
$code = $this->_getHeader();
|
$code = $this->_getHeader();
|
||||||
foreach ($this->_configurations as $key => $conf) {
|
foreach ($this->_configurations as $key => $conf) {
|
||||||
$fullOptions = array_replace_recursive($defaultOptions, $conf['options']);
|
$fullOptions = array_replace_recursive($defaultOptions, $conf['options']);
|
||||||
@@ -576,7 +448,7 @@ class configurationTestGenerator
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
$code .= '}' . PHP_EOL;
|
$code .= '}' . PHP_EOL;
|
||||||
file_put_contents('configuration.php', $code);
|
file_put_contents('configurationCombinations.php', $code);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -589,20 +461,10 @@ class configurationTestGenerator
|
|||||||
return <<<'EOT'
|
return <<<'EOT'
|
||||||
<?php
|
<?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 $_model;
|
||||||
|
|
||||||
private $_conf;
|
private $_conf;
|
||||||
@@ -610,9 +472,7 @@ class configurationTest extends PHPUnit_Framework_TestCase
|
|||||||
public function setUp()
|
public function setUp()
|
||||||
{
|
{
|
||||||
/* Setup Routine */
|
/* Setup Routine */
|
||||||
$this->_conf = PATH . 'cfg' . DIRECTORY_SEPARATOR . 'conf.ini';
|
helper::confBackup();
|
||||||
if (!is_file($this->_conf . '.bak') && is_file($this->_conf))
|
|
||||||
rename($this->_conf, $this->_conf . '.bak');
|
|
||||||
|
|
||||||
$this->_model = zerobin_data::getInstance(array('dir' => PATH . 'data'));
|
$this->_model = zerobin_data::getInstance(array('dir' => PATH . 'data'));
|
||||||
serversalt::setPath(PATH . 'data');
|
serversalt::setPath(PATH . 'data');
|
||||||
@@ -622,7 +482,7 @@ class configurationTest extends PHPUnit_Framework_TestCase
|
|||||||
public function tearDown()
|
public function tearDown()
|
||||||
{
|
{
|
||||||
/* Tear Down Routine */
|
/* Tear Down Routine */
|
||||||
rename($this->_conf . '.bak', $this->_conf);
|
helper::confRestore();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function reset($configuration = array())
|
public function reset($configuration = array())
|
||||||
@@ -630,9 +490,9 @@ class configurationTest extends PHPUnit_Framework_TestCase
|
|||||||
$_POST = array();
|
$_POST = array();
|
||||||
$_GET = array();
|
$_GET = array();
|
||||||
$_SERVER = array();
|
$_SERVER = array();
|
||||||
if ($this->_model->exists(self::$pasteid))
|
if ($this->_model->exists(helper::getPasteId()))
|
||||||
$this->_model->delete(self::$pasteid);
|
$this->_model->delete(helper::getPasteId());
|
||||||
helper::createIniFile($this->_conf, $configuration);
|
helper::createIniFile(CONF, $configuration);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -678,22 +538,24 @@ EOT;
|
|||||||
switch ($step) {
|
switch ($step) {
|
||||||
case 'Create':
|
case 'Create':
|
||||||
$code .= PHP_EOL . <<<'EOT'
|
$code .= PHP_EOL . <<<'EOT'
|
||||||
$_POST = self::$paste;
|
$_POST = helper::getPaste();
|
||||||
|
$_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
|
||||||
|
$_SERVER['REQUEST_METHOD'] = 'POST';
|
||||||
$_SERVER['REMOTE_ADDR'] = '::1';
|
$_SERVER['REMOTE_ADDR'] = '::1';
|
||||||
EOT;
|
EOT;
|
||||||
break;
|
break;
|
||||||
case 'Read':
|
case 'Read':
|
||||||
$code .= PHP_EOL . <<<'EOT'
|
$code .= PHP_EOL . <<<'EOT'
|
||||||
$this->_model->create(self::$pasteid, self::$paste);
|
$this->_model->create(helper::getPasteId(), helper::getPaste());
|
||||||
$_SERVER['QUERY_STRING'] = self::$pasteid;
|
$_SERVER['QUERY_STRING'] = helper::getPasteId();
|
||||||
EOT;
|
EOT;
|
||||||
break;
|
break;
|
||||||
case 'Delete':
|
case 'Delete':
|
||||||
$code .= PHP_EOL . <<<'EOT'
|
$code .= PHP_EOL . <<<'EOT'
|
||||||
$this->_model->create(self::$pasteid, self::$paste);
|
$this->_model->create(helper::getPasteId(), helper::getPaste());
|
||||||
$this->assertTrue($this->_model->exists(self::$pasteid), 'paste exists before deleting data');
|
$this->assertTrue($this->_model->exists(helper::getPasteId()), 'paste exists before deleting data');
|
||||||
$_GET['pasteid'] = self::$pasteid;
|
$_GET['pasteid'] = helper::getPasteId();
|
||||||
$_GET['deletetoken'] = hash_hmac('sha1', self::$pasteid, serversalt::get());
|
$_GET['deletetoken'] = hash_hmac('sha1', helper::getPasteId(), serversalt::get());
|
||||||
EOT;
|
EOT;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -720,7 +582,7 @@ EOT;
|
|||||||
$this->assertTag(
|
$this->assertTag(
|
||||||
array(
|
array(
|
||||||
'id' => 'cipherdata',
|
'id' => 'cipherdata',
|
||||||
'content' => htmlspecialchars(json_encode(self::$paste), ENT_NOQUOTES)
|
'content' => htmlspecialchars(helper::getPasteAsJson(), ENT_NOQUOTES)
|
||||||
),
|
),
|
||||||
$content,
|
$content,
|
||||||
'outputs data correctly'
|
'outputs data correctly'
|
||||||
@@ -738,7 +600,7 @@ EOT;
|
|||||||
$content,
|
$content,
|
||||||
'outputs deleted status correctly'
|
'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;
|
EOT;
|
||||||
break;
|
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');
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -62,13 +62,6 @@ class filterTest extends PHPUnit_Framework_TestCase
|
|||||||
$this->assertEquals('1.21 YiB', filter::size_humanreadable(1234 * $exponent));
|
$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()
|
public function testSlowEquals()
|
||||||
{
|
{
|
||||||
$this->assertTrue(filter::slow_equals('foo', 'foo'), 'same string');
|
$this->assertTrue(filter::slow_equals('foo', 'foo'), 'same string');
|
||||||
|
|||||||
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()
|
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->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":"$","v":1,"iter":1000,"ks":128,"ts":64,"mode":"ccm","adata":"","cipher":"aes","salt":"Gx1vA2/gQ3U","ct":"j7ImByuE5xCqD2YXm6aSyA"}'), 'invalid base64 encoding of iv');
|
||||||
$this->assertFalse(sjcl::isValid('{"iv":"83Ax/OdUav3SanDW9dcQPg","v":1,"iter":1000,"ks":128,"ts":64,"mode":"ccm","adata":"","cipher":"aes","salt":"$","ct":"j7ImByuE5xCqD2YXm6aSyA"}'), 'invalid base64 encoding of salt');
|
$this->assertFalse(sjcl::isValid('{"iv":"83Ax/OdUav3SanDW9dcQPg","v":1,"iter":1000,"ks":128,"ts":64,"mode":"ccm","adata":"","cipher":"aes","salt":"$","ct":"j7ImByuE5xCqD2YXm6aSyA"}'), 'invalid base64 encoding of salt');
|
||||||
|
|||||||
@@ -22,12 +22,15 @@ class trafficlimiterTest extends PHPUnit_Framework_TestCase
|
|||||||
$file = 'baz';
|
$file = 'baz';
|
||||||
$this->assertEquals($this->_path . DIRECTORY_SEPARATOR . $file, trafficlimiter::getPath($file));
|
$this->assertEquals($this->_path . DIRECTORY_SEPARATOR . $file, trafficlimiter::getPath($file));
|
||||||
trafficlimiter::setLimit(4);
|
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);
|
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);
|
sleep(3);
|
||||||
$this->assertTrue(trafficlimiter::canPass('127.0.0.1'), 'third request waited long enough and may pass');
|
$this->assertTrue(trafficlimiter::canPass(), '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');
|
$_SERVER['REMOTE_ADDR'] = '2001:1620:2057:dead:beef::cafe:babe';
|
||||||
$this->assertFalse(trafficlimiter::canPass('127.0.0.1'), 'fifth request is to fast, may not pass');
|
$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');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
462
tst/zerobin.php
462
tst/zerobin.php
@@ -1,44 +1,20 @@
|
|||||||
<?php
|
<?php
|
||||||
class zerobinTest extends PHPUnit_Framework_TestCase
|
class zerobinTest extends PHPUnit_Framework_TestCase
|
||||||
{
|
{
|
||||||
private static $pasteid = '5e9bc25c89fb3bf9';
|
protected $_model;
|
||||||
|
|
||||||
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,
|
|
||||||
'formatter' => 'syntaxhighlighting',
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
public function setUp()
|
public function setUp()
|
||||||
{
|
{
|
||||||
/* Setup Routine */
|
/* Setup Routine */
|
||||||
$this->_model = zerobin_data::getInstance(array('dir' => PATH . 'data'));
|
$this->_model = zerobin_data::getInstance(array('dir' => PATH . 'data'));
|
||||||
serversalt::setPath(PATH . 'data');
|
serversalt::setPath(PATH . 'data');
|
||||||
$this->_conf = PATH . 'cfg' . DIRECTORY_SEPARATOR . 'conf.ini';
|
|
||||||
$this->reset();
|
$this->reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function tearDown()
|
public function tearDown()
|
||||||
{
|
{
|
||||||
/* Tear Down Routine */
|
/* Tear Down Routine */
|
||||||
|
helper::confRestore();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function reset()
|
public function reset()
|
||||||
@@ -46,10 +22,9 @@ class zerobinTest extends PHPUnit_Framework_TestCase
|
|||||||
$_POST = array();
|
$_POST = array();
|
||||||
$_GET = array();
|
$_GET = array();
|
||||||
$_SERVER = array();
|
$_SERVER = array();
|
||||||
if ($this->_model->exists(self::$pasteid))
|
if ($this->_model->exists(helper::getPasteId()))
|
||||||
$this->_model->delete(self::$pasteid);
|
$this->_model->delete(helper::getPasteId());
|
||||||
if (is_file($this->_conf . '.bak'))
|
helper::confRestore();
|
||||||
rename($this->_conf . '.bak', $this->_conf);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -77,11 +52,10 @@ class zerobinTest extends PHPUnit_Framework_TestCase
|
|||||||
public function testViewLanguageSelection()
|
public function testViewLanguageSelection()
|
||||||
{
|
{
|
||||||
$this->reset();
|
$this->reset();
|
||||||
$options = parse_ini_file($this->_conf, true);
|
$options = parse_ini_file(CONF, true);
|
||||||
$options['main']['languageselection'] = true;
|
$options['main']['languageselection'] = true;
|
||||||
if (!is_file($this->_conf . '.bak') && is_file($this->_conf))
|
helper::confBackup();
|
||||||
rename($this->_conf, $this->_conf . '.bak');
|
helper::createIniFile(CONF, $options);
|
||||||
helper::createIniFile($this->_conf, $options);
|
|
||||||
$_COOKIE['lang'] = 'de';
|
$_COOKIE['lang'] = 'de';
|
||||||
ob_start();
|
ob_start();
|
||||||
new zerobin;
|
new zerobin;
|
||||||
@@ -126,9 +100,8 @@ class zerobinTest extends PHPUnit_Framework_TestCase
|
|||||||
public function testConf()
|
public function testConf()
|
||||||
{
|
{
|
||||||
$this->reset();
|
$this->reset();
|
||||||
if (!is_file($this->_conf . '.bak') && is_file($this->_conf))
|
helper::confBackup();
|
||||||
rename($this->_conf, $this->_conf . '.bak');
|
file_put_contents(CONF, '');
|
||||||
file_put_contents($this->_conf, '');
|
|
||||||
ob_start();
|
ob_start();
|
||||||
new zerobin;
|
new zerobin;
|
||||||
$content = ob_get_contents();
|
$content = ob_get_contents();
|
||||||
@@ -140,7 +113,13 @@ class zerobinTest extends PHPUnit_Framework_TestCase
|
|||||||
public function testCreate()
|
public function testCreate()
|
||||||
{
|
{
|
||||||
$this->reset();
|
$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';
|
$_SERVER['REMOTE_ADDR'] = '::1';
|
||||||
ob_start();
|
ob_start();
|
||||||
new zerobin;
|
new zerobin;
|
||||||
@@ -161,14 +140,17 @@ class zerobinTest extends PHPUnit_Framework_TestCase
|
|||||||
public function testCreateInvalidTimelimit()
|
public function testCreateInvalidTimelimit()
|
||||||
{
|
{
|
||||||
$this->reset();
|
$this->reset();
|
||||||
$_POST = self::$paste;
|
$_POST = helper::getPaste();
|
||||||
|
$_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
|
||||||
|
$_SERVER['REQUEST_METHOD'] = 'POST';
|
||||||
$_SERVER['REMOTE_ADDR'] = '::1';
|
$_SERVER['REMOTE_ADDR'] = '::1';
|
||||||
|
trafficlimiter::canPass();
|
||||||
ob_start();
|
ob_start();
|
||||||
new zerobin;
|
new zerobin;
|
||||||
$content = ob_get_contents();
|
$content = ob_get_contents();
|
||||||
$response = json_decode($content, true);
|
$response = json_decode($content, true);
|
||||||
$this->assertEquals(1, $response['status'], 'outputs error status');
|
$this->assertEquals(1, $response['status'], 'outputs error status');
|
||||||
$this->assertFalse($this->_model->exists(self::$pasteid), 'paste exists after posting data');
|
$this->assertFalse($this->_model->exists(helper::getPasteId()), 'paste exists after posting data');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -177,20 +159,21 @@ class zerobinTest extends PHPUnit_Framework_TestCase
|
|||||||
public function testCreateInvalidSize()
|
public function testCreateInvalidSize()
|
||||||
{
|
{
|
||||||
$this->reset();
|
$this->reset();
|
||||||
$options = parse_ini_file($this->_conf, true);
|
$options = parse_ini_file(CONF, true);
|
||||||
$options['main']['sizelimit'] = 10;
|
$options['main']['sizelimit'] = 10;
|
||||||
$options['traffic']['limit'] = 0;
|
$options['traffic']['limit'] = 0;
|
||||||
if (!is_file($this->_conf . '.bak') && is_file($this->_conf))
|
helper::confBackup();
|
||||||
rename($this->_conf, $this->_conf . '.bak');
|
helper::createIniFile(CONF, $options);
|
||||||
helper::createIniFile($this->_conf, $options);
|
$_POST = helper::getPaste();
|
||||||
$_POST = self::$paste;
|
$_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
|
||||||
|
$_SERVER['REQUEST_METHOD'] = 'POST';
|
||||||
$_SERVER['REMOTE_ADDR'] = '::1';
|
$_SERVER['REMOTE_ADDR'] = '::1';
|
||||||
ob_start();
|
ob_start();
|
||||||
new zerobin;
|
new zerobin;
|
||||||
$content = ob_get_contents();
|
$content = ob_get_contents();
|
||||||
$response = json_decode($content, true);
|
$response = json_decode($content, true);
|
||||||
$this->assertEquals(1, $response['status'], 'outputs error status');
|
$this->assertEquals(1, $response['status'], 'outputs error status');
|
||||||
$this->assertFalse($this->_model->exists(self::$pasteid), 'paste exists after posting data');
|
$this->assertFalse($this->_model->exists(helper::getPasteId()), 'paste exists after posting data');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -199,19 +182,21 @@ class zerobinTest extends PHPUnit_Framework_TestCase
|
|||||||
public function testCreateProxyHeader()
|
public function testCreateProxyHeader()
|
||||||
{
|
{
|
||||||
$this->reset();
|
$this->reset();
|
||||||
$options = parse_ini_file($this->_conf, true);
|
$options = parse_ini_file(CONF, true);
|
||||||
$options['traffic']['header'] = 'X_FORWARDED_FOR';
|
$options['traffic']['header'] = 'X_FORWARDED_FOR';
|
||||||
if (!is_file($this->_conf . '.bak') && is_file($this->_conf))
|
$options['traffic']['limit'] = 100;
|
||||||
rename($this->_conf, $this->_conf . '.bak');
|
helper::confBackup();
|
||||||
helper::createIniFile($this->_conf, $options);
|
helper::createIniFile(CONF, $options);
|
||||||
$_POST = self::$paste;
|
$_POST = helper::getPaste();
|
||||||
$_SERVER['HTTP_X_FORWARDED_FOR'] = '::1';
|
$_SERVER['HTTP_X_FORWARDED_FOR'] = '::1';
|
||||||
|
$_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
|
||||||
|
$_SERVER['REQUEST_METHOD'] = 'POST';
|
||||||
ob_start();
|
ob_start();
|
||||||
new zerobin;
|
new zerobin;
|
||||||
$content = ob_get_contents();
|
$content = ob_get_contents();
|
||||||
$response = json_decode($content, true);
|
$response = json_decode($content, true);
|
||||||
$this->assertEquals(1, $response['status'], 'outputs error status');
|
$this->assertEquals(1, $response['status'], 'outputs error status');
|
||||||
$this->assertFalse($this->_model->exists(self::$pasteid), 'paste exists after posting data');
|
$this->assertFalse($this->_model->exists(helper::getPasteId()), 'paste exists after posting data');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -220,20 +205,21 @@ class zerobinTest extends PHPUnit_Framework_TestCase
|
|||||||
public function testCreateDuplicateId()
|
public function testCreateDuplicateId()
|
||||||
{
|
{
|
||||||
$this->reset();
|
$this->reset();
|
||||||
$options = parse_ini_file($this->_conf, true);
|
$options = parse_ini_file(CONF, true);
|
||||||
$options['traffic']['limit'] = 0;
|
$options['traffic']['limit'] = 0;
|
||||||
if (!is_file($this->_conf . '.bak') && is_file($this->_conf))
|
helper::confBackup();
|
||||||
rename($this->_conf, $this->_conf . '.bak');
|
helper::createIniFile(CONF, $options);
|
||||||
helper::createIniFile($this->_conf, $options);
|
$this->_model->create(helper::getPasteId(), helper::getPaste());
|
||||||
$this->_model->create(self::$pasteid, self::$paste);
|
$_POST = helper::getPaste();
|
||||||
$_POST = self::$paste;
|
$_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
|
||||||
|
$_SERVER['REQUEST_METHOD'] = 'POST';
|
||||||
$_SERVER['REMOTE_ADDR'] = '::1';
|
$_SERVER['REMOTE_ADDR'] = '::1';
|
||||||
ob_start();
|
ob_start();
|
||||||
new zerobin;
|
new zerobin;
|
||||||
$content = ob_get_contents();
|
$content = ob_get_contents();
|
||||||
$response = json_decode($content, true);
|
$response = json_decode($content, true);
|
||||||
$this->assertEquals(1, $response['status'], 'outputs error status');
|
$this->assertEquals(1, $response['status'], 'outputs error status');
|
||||||
$this->assertTrue($this->_model->exists(self::$pasteid), 'paste exists after posting data');
|
$this->assertTrue($this->_model->exists(helper::getPasteId()), 'paste exists after posting data');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -242,14 +228,15 @@ class zerobinTest extends PHPUnit_Framework_TestCase
|
|||||||
public function testCreateValidExpire()
|
public function testCreateValidExpire()
|
||||||
{
|
{
|
||||||
$this->reset();
|
$this->reset();
|
||||||
$options = parse_ini_file($this->_conf, true);
|
$options = parse_ini_file(CONF, true);
|
||||||
$options['traffic']['limit'] = 0;
|
$options['traffic']['limit'] = 0;
|
||||||
if (!is_file($this->_conf . '.bak') && is_file($this->_conf))
|
helper::confBackup();
|
||||||
rename($this->_conf, $this->_conf . '.bak');
|
helper::createIniFile(CONF, $options);
|
||||||
helper::createIniFile($this->_conf, $options);
|
$_POST = helper::getPaste();
|
||||||
$_POST = self::$paste;
|
|
||||||
$_POST['expire'] = '5min';
|
$_POST['expire'] = '5min';
|
||||||
$_POST['formatter'] = 'foo';
|
$_POST['formatter'] = 'foo';
|
||||||
|
$_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
|
||||||
|
$_SERVER['REQUEST_METHOD'] = 'POST';
|
||||||
$_SERVER['REMOTE_ADDR'] = '::1';
|
$_SERVER['REMOTE_ADDR'] = '::1';
|
||||||
ob_start();
|
ob_start();
|
||||||
new zerobin;
|
new zerobin;
|
||||||
@@ -262,6 +249,40 @@ class zerobinTest extends PHPUnit_Framework_TestCase
|
|||||||
'outputs valid delete token'
|
'outputs valid delete token'
|
||||||
);
|
);
|
||||||
$this->assertTrue($this->_model->exists($response['id']), 'paste exists after posting data');
|
$this->assertTrue($this->_model->exists($response['id']), 'paste exists after posting data');
|
||||||
|
$paste = $this->_model->read($response['id']);
|
||||||
|
$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');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -270,13 +291,14 @@ class zerobinTest extends PHPUnit_Framework_TestCase
|
|||||||
public function testCreateInvalidExpire()
|
public function testCreateInvalidExpire()
|
||||||
{
|
{
|
||||||
$this->reset();
|
$this->reset();
|
||||||
$options = parse_ini_file($this->_conf, true);
|
$options = parse_ini_file(CONF, true);
|
||||||
$options['traffic']['limit'] = 0;
|
$options['traffic']['limit'] = 0;
|
||||||
if (!is_file($this->_conf . '.bak') && is_file($this->_conf))
|
helper::confBackup();
|
||||||
rename($this->_conf, $this->_conf . '.bak');
|
helper::createIniFile(CONF, $options);
|
||||||
helper::createIniFile($this->_conf, $options);
|
$_POST = helper::getPaste();
|
||||||
$_POST = self::$paste;
|
|
||||||
$_POST['expire'] = 'foo';
|
$_POST['expire'] = 'foo';
|
||||||
|
$_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
|
||||||
|
$_SERVER['REQUEST_METHOD'] = 'POST';
|
||||||
$_SERVER['REMOTE_ADDR'] = '::1';
|
$_SERVER['REMOTE_ADDR'] = '::1';
|
||||||
ob_start();
|
ob_start();
|
||||||
new zerobin;
|
new zerobin;
|
||||||
@@ -297,20 +319,21 @@ class zerobinTest extends PHPUnit_Framework_TestCase
|
|||||||
public function testCreateInvalidBurn()
|
public function testCreateInvalidBurn()
|
||||||
{
|
{
|
||||||
$this->reset();
|
$this->reset();
|
||||||
$options = parse_ini_file($this->_conf, true);
|
$options = parse_ini_file(CONF, true);
|
||||||
$options['traffic']['limit'] = 0;
|
$options['traffic']['limit'] = 0;
|
||||||
if (!is_file($this->_conf . '.bak') && is_file($this->_conf))
|
helper::confBackup();
|
||||||
rename($this->_conf, $this->_conf . '.bak');
|
helper::createIniFile(CONF, $options);
|
||||||
helper::createIniFile($this->_conf, $options);
|
$_POST = helper::getPaste();
|
||||||
$_POST = self::$paste;
|
|
||||||
$_POST['burnafterreading'] = 'neither 1 nor 0';
|
$_POST['burnafterreading'] = 'neither 1 nor 0';
|
||||||
|
$_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
|
||||||
|
$_SERVER['REQUEST_METHOD'] = 'POST';
|
||||||
$_SERVER['REMOTE_ADDR'] = '::1';
|
$_SERVER['REMOTE_ADDR'] = '::1';
|
||||||
ob_start();
|
ob_start();
|
||||||
new zerobin;
|
new zerobin;
|
||||||
$content = ob_get_contents();
|
$content = ob_get_contents();
|
||||||
$response = json_decode($content, true);
|
$response = json_decode($content, true);
|
||||||
$this->assertEquals(1, $response['status'], 'outputs error status');
|
$this->assertEquals(1, $response['status'], 'outputs error status');
|
||||||
$this->assertFalse($this->_model->exists(self::$pasteid), 'paste exists after posting data');
|
$this->assertFalse($this->_model->exists(helper::getPasteId()), 'paste exists after posting data');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -319,20 +342,21 @@ class zerobinTest extends PHPUnit_Framework_TestCase
|
|||||||
public function testCreateInvalidOpenDiscussion()
|
public function testCreateInvalidOpenDiscussion()
|
||||||
{
|
{
|
||||||
$this->reset();
|
$this->reset();
|
||||||
$options = parse_ini_file($this->_conf, true);
|
$options = parse_ini_file(CONF, true);
|
||||||
$options['traffic']['limit'] = 0;
|
$options['traffic']['limit'] = 0;
|
||||||
if (!is_file($this->_conf . '.bak') && is_file($this->_conf))
|
helper::confBackup();
|
||||||
rename($this->_conf, $this->_conf . '.bak');
|
helper::createIniFile(CONF, $options);
|
||||||
helper::createIniFile($this->_conf, $options);
|
$_POST = helper::getPaste();
|
||||||
$_POST = self::$paste;
|
|
||||||
$_POST['opendiscussion'] = 'neither 1 nor 0';
|
$_POST['opendiscussion'] = 'neither 1 nor 0';
|
||||||
|
$_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
|
||||||
|
$_SERVER['REQUEST_METHOD'] = 'POST';
|
||||||
$_SERVER['REMOTE_ADDR'] = '::1';
|
$_SERVER['REMOTE_ADDR'] = '::1';
|
||||||
ob_start();
|
ob_start();
|
||||||
new zerobin;
|
new zerobin;
|
||||||
$content = ob_get_contents();
|
$content = ob_get_contents();
|
||||||
$response = json_decode($content, true);
|
$response = json_decode($content, true);
|
||||||
$this->assertEquals(1, $response['status'], 'outputs error status');
|
$this->assertEquals(1, $response['status'], 'outputs error status');
|
||||||
$this->assertFalse($this->_model->exists(self::$pasteid), 'paste exists after posting data');
|
$this->assertFalse($this->_model->exists(helper::getPasteId()), 'paste exists after posting data');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -341,16 +365,16 @@ class zerobinTest extends PHPUnit_Framework_TestCase
|
|||||||
public function testCreateAttachment()
|
public function testCreateAttachment()
|
||||||
{
|
{
|
||||||
$this->reset();
|
$this->reset();
|
||||||
$options = parse_ini_file($this->_conf, true);
|
$options = parse_ini_file(CONF, true);
|
||||||
$options['traffic']['limit'] = 0;
|
$options['traffic']['limit'] = 0;
|
||||||
$options['main']['fileupload'] = true;
|
$options['main']['fileupload'] = true;
|
||||||
if (!is_file($this->_conf . '.bak') && is_file($this->_conf))
|
helper::confBackup();
|
||||||
rename($this->_conf, $this->_conf . '.bak');
|
helper::createIniFile(CONF, $options);
|
||||||
helper::createIniFile($this->_conf, $options);
|
$_POST = helper::getPasteWithAttachment();
|
||||||
$_POST = self::$paste;
|
$_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
|
||||||
$_POST['attachment'] = self::$comment['data'];
|
$_SERVER['REQUEST_METHOD'] = 'POST';
|
||||||
$_POST['attachmentname'] = self::$comment['meta']['nickname'];
|
|
||||||
$_SERVER['REMOTE_ADDR'] = '::1';
|
$_SERVER['REMOTE_ADDR'] = '::1';
|
||||||
|
$this->assertFalse($this->_model->exists(helper::getPasteId()), 'paste does not exists before posting data');
|
||||||
ob_start();
|
ob_start();
|
||||||
new zerobin;
|
new zerobin;
|
||||||
$content = ob_get_contents();
|
$content = ob_get_contents();
|
||||||
@@ -362,6 +386,11 @@ class zerobinTest extends PHPUnit_Framework_TestCase
|
|||||||
'outputs valid delete token'
|
'outputs valid delete token'
|
||||||
);
|
);
|
||||||
$this->assertTrue($this->_model->exists($response['id']), 'paste exists after posting data');
|
$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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -370,13 +399,14 @@ class zerobinTest extends PHPUnit_Framework_TestCase
|
|||||||
public function testCreateValidNick()
|
public function testCreateValidNick()
|
||||||
{
|
{
|
||||||
$this->reset();
|
$this->reset();
|
||||||
$options = parse_ini_file($this->_conf, true);
|
$options = parse_ini_file(CONF, true);
|
||||||
$options['traffic']['limit'] = 0;
|
$options['traffic']['limit'] = 0;
|
||||||
if (!is_file($this->_conf . '.bak') && is_file($this->_conf))
|
helper::confBackup();
|
||||||
rename($this->_conf, $this->_conf . '.bak');
|
helper::createIniFile(CONF, $options);
|
||||||
helper::createIniFile($this->_conf, $options);
|
$_POST = helper::getPaste();
|
||||||
$_POST = self::$paste;
|
$_POST['nickname'] = helper::getComment()['meta']['nickname'];
|
||||||
$_POST['nickname'] = self::$comment['meta']['nickname'];
|
$_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
|
||||||
|
$_SERVER['REQUEST_METHOD'] = 'POST';
|
||||||
$_SERVER['REMOTE_ADDR'] = '::1';
|
$_SERVER['REMOTE_ADDR'] = '::1';
|
||||||
ob_start();
|
ob_start();
|
||||||
new zerobin;
|
new zerobin;
|
||||||
@@ -397,20 +427,24 @@ class zerobinTest extends PHPUnit_Framework_TestCase
|
|||||||
public function testCreateInvalidNick()
|
public function testCreateInvalidNick()
|
||||||
{
|
{
|
||||||
$this->reset();
|
$this->reset();
|
||||||
$options = parse_ini_file($this->_conf, true);
|
$options = parse_ini_file(CONF, true);
|
||||||
$options['traffic']['limit'] = 0;
|
$options['traffic']['limit'] = 0;
|
||||||
if (!is_file($this->_conf . '.bak') && is_file($this->_conf))
|
helper::confBackup();
|
||||||
rename($this->_conf, $this->_conf . '.bak');
|
helper::createIniFile(CONF, $options);
|
||||||
helper::createIniFile($this->_conf, $options);
|
$_POST = helper::getCommentPost();
|
||||||
$_POST = self::$paste;
|
$_POST['pasteid'] = helper::getPasteId();
|
||||||
|
$_POST['parentid'] = helper::getPasteId();
|
||||||
$_POST['nickname'] = 'foo';
|
$_POST['nickname'] = 'foo';
|
||||||
|
$_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
|
||||||
|
$_SERVER['REQUEST_METHOD'] = 'POST';
|
||||||
$_SERVER['REMOTE_ADDR'] = '::1';
|
$_SERVER['REMOTE_ADDR'] = '::1';
|
||||||
|
$this->_model->create(helper::getPasteId(), helper::getPaste());
|
||||||
ob_start();
|
ob_start();
|
||||||
new zerobin;
|
new zerobin;
|
||||||
$content = ob_get_contents();
|
$content = ob_get_contents();
|
||||||
$response = json_decode($content, true);
|
$response = json_decode($content, true);
|
||||||
$this->assertEquals(1, $response['status'], 'outputs error status');
|
$this->assertEquals(1, $response['status'], 'outputs error status');
|
||||||
$this->assertFalse($this->_model->exists(self::$pasteid), 'paste exists after posting data');
|
$this->assertTrue($this->_model->exists(helper::getPasteId()), 'paste exists after posting data');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -419,22 +453,23 @@ class zerobinTest extends PHPUnit_Framework_TestCase
|
|||||||
public function testCreateComment()
|
public function testCreateComment()
|
||||||
{
|
{
|
||||||
$this->reset();
|
$this->reset();
|
||||||
$options = parse_ini_file($this->_conf, true);
|
$options = parse_ini_file(CONF, true);
|
||||||
$options['traffic']['limit'] = 0;
|
$options['traffic']['limit'] = 0;
|
||||||
if (!is_file($this->_conf . '.bak') && is_file($this->_conf))
|
helper::confBackup();
|
||||||
rename($this->_conf, $this->_conf . '.bak');
|
helper::createIniFile(CONF, $options);
|
||||||
helper::createIniFile($this->_conf, $options);
|
$_POST = helper::getCommentPost();
|
||||||
$_POST = self::$comment;
|
$_POST['pasteid'] = helper::getPasteId();
|
||||||
$_POST['pasteid'] = self::$pasteid;
|
$_POST['parentid'] = helper::getPasteId();
|
||||||
$_POST['parentid'] = self::$pasteid;
|
$_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
|
||||||
|
$_SERVER['REQUEST_METHOD'] = 'POST';
|
||||||
$_SERVER['REMOTE_ADDR'] = '::1';
|
$_SERVER['REMOTE_ADDR'] = '::1';
|
||||||
$this->_model->create(self::$pasteid, self::$paste);
|
$this->_model->create(helper::getPasteId(), helper::getPaste());
|
||||||
ob_start();
|
ob_start();
|
||||||
new zerobin;
|
new zerobin;
|
||||||
$content = ob_get_contents();
|
$content = ob_get_contents();
|
||||||
$response = json_decode($content, true);
|
$response = json_decode($content, true);
|
||||||
$this->assertEquals(0, $response['status'], 'outputs status');
|
$this->assertEquals(0, $response['status'], 'outputs status');
|
||||||
$this->assertTrue($this->_model->existsComment(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');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -443,22 +478,23 @@ class zerobinTest extends PHPUnit_Framework_TestCase
|
|||||||
public function testCreateInvalidComment()
|
public function testCreateInvalidComment()
|
||||||
{
|
{
|
||||||
$this->reset();
|
$this->reset();
|
||||||
$options = parse_ini_file($this->_conf, true);
|
$options = parse_ini_file(CONF, true);
|
||||||
$options['traffic']['limit'] = 0;
|
$options['traffic']['limit'] = 0;
|
||||||
if (!is_file($this->_conf . '.bak') && is_file($this->_conf))
|
helper::confBackup();
|
||||||
rename($this->_conf, $this->_conf . '.bak');
|
helper::createIniFile(CONF, $options);
|
||||||
helper::createIniFile($this->_conf, $options);
|
$_POST = helper::getCommentPost();
|
||||||
$_POST = self::$comment;
|
$_POST['pasteid'] = helper::getPasteId();
|
||||||
$_POST['pasteid'] = self::$pasteid;
|
|
||||||
$_POST['parentid'] = 'foo';
|
$_POST['parentid'] = 'foo';
|
||||||
|
$_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
|
||||||
|
$_SERVER['REQUEST_METHOD'] = 'POST';
|
||||||
$_SERVER['REMOTE_ADDR'] = '::1';
|
$_SERVER['REMOTE_ADDR'] = '::1';
|
||||||
$this->_model->create(self::$pasteid, self::$paste);
|
$this->_model->create(helper::getPasteId(), helper::getPaste());
|
||||||
ob_start();
|
ob_start();
|
||||||
new zerobin;
|
new zerobin;
|
||||||
$content = ob_get_contents();
|
$content = ob_get_contents();
|
||||||
$response = json_decode($content, true);
|
$response = json_decode($content, true);
|
||||||
$this->assertEquals(1, $response['status'], 'outputs error status');
|
$this->assertEquals(1, $response['status'], 'outputs error status');
|
||||||
$this->assertFalse($this->_model->existsComment(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');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -467,24 +503,24 @@ class zerobinTest extends PHPUnit_Framework_TestCase
|
|||||||
public function testCreateCommentDiscussionDisabled()
|
public function testCreateCommentDiscussionDisabled()
|
||||||
{
|
{
|
||||||
$this->reset();
|
$this->reset();
|
||||||
$options = parse_ini_file($this->_conf, true);
|
$options = parse_ini_file(CONF, true);
|
||||||
$options['traffic']['limit'] = 0;
|
$options['traffic']['limit'] = 0;
|
||||||
if (!is_file($this->_conf . '.bak') && is_file($this->_conf))
|
helper::confBackup();
|
||||||
rename($this->_conf, $this->_conf . '.bak');
|
helper::createIniFile(CONF, $options);
|
||||||
helper::createIniFile($this->_conf, $options);
|
$_POST = helper::getCommentPost();
|
||||||
$_POST = self::$comment;
|
$_POST['pasteid'] = helper::getPasteId();
|
||||||
$_POST['pasteid'] = self::$pasteid;
|
$_POST['parentid'] = helper::getPasteId();
|
||||||
$_POST['parentid'] = self::$pasteid;
|
$_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
|
||||||
|
$_SERVER['REQUEST_METHOD'] = 'POST';
|
||||||
$_SERVER['REMOTE_ADDR'] = '::1';
|
$_SERVER['REMOTE_ADDR'] = '::1';
|
||||||
$paste = self::$paste;
|
$paste = helper::getPaste(array('opendiscussion' => false));
|
||||||
$paste['meta']['opendiscussion'] = false;
|
$this->_model->create(helper::getPasteId(), $paste);
|
||||||
$this->_model->create(self::$pasteid, $paste);
|
|
||||||
ob_start();
|
ob_start();
|
||||||
new zerobin;
|
new zerobin;
|
||||||
$content = ob_get_contents();
|
$content = ob_get_contents();
|
||||||
$response = json_decode($content, true);
|
$response = json_decode($content, true);
|
||||||
$this->assertEquals(1, $response['status'], 'outputs error status');
|
$this->assertEquals(1, $response['status'], 'outputs error status');
|
||||||
$this->assertFalse($this->_model->existsComment(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');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -493,21 +529,22 @@ class zerobinTest extends PHPUnit_Framework_TestCase
|
|||||||
public function testCreateCommentInvalidPaste()
|
public function testCreateCommentInvalidPaste()
|
||||||
{
|
{
|
||||||
$this->reset();
|
$this->reset();
|
||||||
$options = parse_ini_file($this->_conf, true);
|
$options = parse_ini_file(CONF, true);
|
||||||
$options['traffic']['limit'] = 0;
|
$options['traffic']['limit'] = 0;
|
||||||
if (!is_file($this->_conf . '.bak') && is_file($this->_conf))
|
helper::confBackup();
|
||||||
rename($this->_conf, $this->_conf . '.bak');
|
helper::createIniFile(CONF, $options);
|
||||||
helper::createIniFile($this->_conf, $options);
|
$_POST = helper::getCommentPost();
|
||||||
$_POST = self::$comment;
|
$_POST['pasteid'] = helper::getPasteId();
|
||||||
$_POST['pasteid'] = self::$pasteid;
|
$_POST['parentid'] = helper::getPasteId();
|
||||||
$_POST['parentid'] = self::$pasteid;
|
$_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
|
||||||
|
$_SERVER['REQUEST_METHOD'] = 'POST';
|
||||||
$_SERVER['REMOTE_ADDR'] = '::1';
|
$_SERVER['REMOTE_ADDR'] = '::1';
|
||||||
ob_start();
|
ob_start();
|
||||||
new zerobin;
|
new zerobin;
|
||||||
$content = ob_get_contents();
|
$content = ob_get_contents();
|
||||||
$response = json_decode($content, true);
|
$response = json_decode($content, true);
|
||||||
$this->assertEquals(1, $response['status'], 'outputs error status');
|
$this->assertEquals(1, $response['status'], 'outputs error status');
|
||||||
$this->assertFalse($this->_model->existsComment(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');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -516,24 +553,25 @@ class zerobinTest extends PHPUnit_Framework_TestCase
|
|||||||
public function testCreateDuplicateComment()
|
public function testCreateDuplicateComment()
|
||||||
{
|
{
|
||||||
$this->reset();
|
$this->reset();
|
||||||
$options = parse_ini_file($this->_conf, true);
|
$options = parse_ini_file(CONF, true);
|
||||||
$options['traffic']['limit'] = 0;
|
$options['traffic']['limit'] = 0;
|
||||||
if (!is_file($this->_conf . '.bak') && is_file($this->_conf))
|
helper::confBackup();
|
||||||
rename($this->_conf, $this->_conf . '.bak');
|
helper::createIniFile(CONF, $options);
|
||||||
helper::createIniFile($this->_conf, $options);
|
$this->_model->create(helper::getPasteId(), helper::getPaste());
|
||||||
$this->_model->create(self::$pasteid, self::$paste);
|
$this->_model->createComment(helper::getPasteId(), helper::getPasteId(), helper::getCommentId(), helper::getComment());
|
||||||
$this->_model->createComment(self::$pasteid, self::$pasteid, self::$commentid, self::$comment);
|
$this->assertTrue($this->_model->existsComment(helper::getPasteId(), helper::getPasteId(), helper::getCommentId()), 'comment exists before posting data');
|
||||||
$this->assertTrue($this->_model->existsComment(self::$pasteid, self::$pasteid, self::$commentid), 'comment exists before posting data');
|
$_POST = helper::getCommentPost();
|
||||||
$_POST = self::$comment;
|
$_POST['pasteid'] = helper::getPasteId();
|
||||||
$_POST['pasteid'] = self::$pasteid;
|
$_POST['parentid'] = helper::getPasteId();
|
||||||
$_POST['parentid'] = self::$pasteid;
|
$_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
|
||||||
|
$_SERVER['REQUEST_METHOD'] = 'POST';
|
||||||
$_SERVER['REMOTE_ADDR'] = '::1';
|
$_SERVER['REMOTE_ADDR'] = '::1';
|
||||||
ob_start();
|
ob_start();
|
||||||
new zerobin;
|
new zerobin;
|
||||||
$content = ob_get_contents();
|
$content = ob_get_contents();
|
||||||
$response = json_decode($content, true);
|
$response = json_decode($content, true);
|
||||||
$this->assertEquals(1, $response['status'], 'outputs error status');
|
$this->assertEquals(1, $response['status'], 'outputs error status');
|
||||||
$this->assertTrue($this->_model->existsComment(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');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -542,15 +580,15 @@ class zerobinTest extends PHPUnit_Framework_TestCase
|
|||||||
public function testRead()
|
public function testRead()
|
||||||
{
|
{
|
||||||
$this->reset();
|
$this->reset();
|
||||||
$this->_model->create(self::$pasteid, self::$paste);
|
$this->_model->create(helper::getPasteId(), helper::getPaste());
|
||||||
$_SERVER['QUERY_STRING'] = self::$pasteid;
|
$_SERVER['QUERY_STRING'] = helper::getPasteId();
|
||||||
ob_start();
|
ob_start();
|
||||||
new zerobin;
|
new zerobin;
|
||||||
$content = ob_get_contents();
|
$content = ob_get_contents();
|
||||||
$this->assertTag(
|
$this->assertTag(
|
||||||
array(
|
array(
|
||||||
'id' => 'cipherdata',
|
'id' => 'cipherdata',
|
||||||
'content' => htmlspecialchars(json_encode(self::$paste), ENT_NOQUOTES)
|
'content' => htmlspecialchars(helper::getPasteAsJson(), ENT_NOQUOTES)
|
||||||
),
|
),
|
||||||
$content,
|
$content,
|
||||||
'outputs data correctly'
|
'outputs data correctly'
|
||||||
@@ -583,7 +621,7 @@ class zerobinTest extends PHPUnit_Framework_TestCase
|
|||||||
public function testReadNonexisting()
|
public function testReadNonexisting()
|
||||||
{
|
{
|
||||||
$this->reset();
|
$this->reset();
|
||||||
$_SERVER['QUERY_STRING'] = self::$pasteid;
|
$_SERVER['QUERY_STRING'] = helper::getPasteId();
|
||||||
ob_start();
|
ob_start();
|
||||||
new zerobin;
|
new zerobin;
|
||||||
$content = ob_get_contents();
|
$content = ob_get_contents();
|
||||||
@@ -603,10 +641,9 @@ class zerobinTest extends PHPUnit_Framework_TestCase
|
|||||||
public function testReadExpired()
|
public function testReadExpired()
|
||||||
{
|
{
|
||||||
$this->reset();
|
$this->reset();
|
||||||
$expiredPaste = self::$paste;
|
$expiredPaste = helper::getPaste(array('expire_date' => 1344803344));
|
||||||
$expiredPaste['meta']['expire_date'] = $expiredPaste['meta']['postdate'];
|
$this->_model->create(helper::getPasteId(), $expiredPaste);
|
||||||
$this->_model->create(self::$pasteid, $expiredPaste);
|
$_SERVER['QUERY_STRING'] = helper::getPasteId();
|
||||||
$_SERVER['QUERY_STRING'] = self::$pasteid;
|
|
||||||
ob_start();
|
ob_start();
|
||||||
new zerobin;
|
new zerobin;
|
||||||
$content = ob_get_contents();
|
$content = ob_get_contents();
|
||||||
@@ -626,17 +663,16 @@ class zerobinTest extends PHPUnit_Framework_TestCase
|
|||||||
public function testReadBurn()
|
public function testReadBurn()
|
||||||
{
|
{
|
||||||
$this->reset();
|
$this->reset();
|
||||||
$burnPaste = self::$paste;
|
$burnPaste = helper::getPaste(array('burnafterreading' => true));
|
||||||
$burnPaste['meta']['burnafterreading'] = true;
|
$this->_model->create(helper::getPasteId(), $burnPaste);
|
||||||
$this->_model->create(self::$pasteid, $burnPaste);
|
$_SERVER['QUERY_STRING'] = helper::getPasteId();
|
||||||
$_SERVER['QUERY_STRING'] = self::$pasteid;
|
|
||||||
ob_start();
|
ob_start();
|
||||||
new zerobin;
|
new zerobin;
|
||||||
$content = ob_get_contents();
|
$content = ob_get_contents();
|
||||||
$this->assertTag(
|
$this->assertTag(
|
||||||
array(
|
array(
|
||||||
'id' => 'cipherdata',
|
'id' => 'cipherdata',
|
||||||
'content' => htmlspecialchars(json_encode($burnPaste), ENT_NOQUOTES)
|
'content' => htmlspecialchars(helper::getPasteAsJson($burnPaste['meta']), ENT_NOQUOTES)
|
||||||
),
|
),
|
||||||
$content,
|
$content,
|
||||||
'outputs data correctly'
|
'outputs data correctly'
|
||||||
@@ -649,14 +685,23 @@ class zerobinTest extends PHPUnit_Framework_TestCase
|
|||||||
public function testReadJson()
|
public function testReadJson()
|
||||||
{
|
{
|
||||||
$this->reset();
|
$this->reset();
|
||||||
$this->_model->create(self::$pasteid, self::$paste);
|
$paste = helper::getPaste();
|
||||||
$_SERVER['QUERY_STRING'] = self::$pasteid . '&json';
|
$this->_model->create(helper::getPasteId(), $paste);
|
||||||
|
$_SERVER['QUERY_STRING'] = helper::getPasteId();
|
||||||
|
$_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
|
||||||
ob_start();
|
ob_start();
|
||||||
new zerobin;
|
new zerobin;
|
||||||
$content = ob_get_contents();
|
$content = ob_get_contents();
|
||||||
$response = json_decode($content, true);
|
$response = json_decode($content, true);
|
||||||
$this->assertEquals(0, $response['status'], 'outputs success status');
|
$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');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -665,7 +710,8 @@ class zerobinTest extends PHPUnit_Framework_TestCase
|
|||||||
public function testReadInvalidJson()
|
public function testReadInvalidJson()
|
||||||
{
|
{
|
||||||
$this->reset();
|
$this->reset();
|
||||||
$_SERVER['QUERY_STRING'] = self::$pasteid . '&json';
|
$_SERVER['QUERY_STRING'] = helper::getPasteId();
|
||||||
|
$_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
|
||||||
ob_start();
|
ob_start();
|
||||||
new zerobin;
|
new zerobin;
|
||||||
$content = ob_get_contents();
|
$content = ob_get_contents();
|
||||||
@@ -679,19 +725,23 @@ class zerobinTest extends PHPUnit_Framework_TestCase
|
|||||||
public function testReadOldSyntax()
|
public function testReadOldSyntax()
|
||||||
{
|
{
|
||||||
$this->reset();
|
$this->reset();
|
||||||
$oldPaste = self::$paste;
|
$oldPaste = helper::getPaste();
|
||||||
$oldPaste['meta']['syntaxcoloring'] = true;
|
$meta = array(
|
||||||
unset($oldPaste['meta']['formatter']);
|
'syntaxcoloring' => true,
|
||||||
$this->_model->create(self::$pasteid, $oldPaste);
|
'postdate' => $oldPaste['meta']['postdate'],
|
||||||
$_SERVER['QUERY_STRING'] = self::$pasteid;
|
'opendiscussion' => $oldPaste['meta']['opendiscussion'],
|
||||||
|
);
|
||||||
|
$oldPaste['meta'] = $meta;
|
||||||
|
$this->_model->create(helper::getPasteId(), $oldPaste);
|
||||||
|
$_SERVER['QUERY_STRING'] = helper::getPasteId();
|
||||||
ob_start();
|
ob_start();
|
||||||
new zerobin;
|
new zerobin;
|
||||||
$content = ob_get_contents();
|
$content = ob_get_contents();
|
||||||
$oldPaste['meta']['formatter'] = 'syntaxhighlighting';
|
$meta['formatter'] = 'syntaxhighlighting';
|
||||||
$this->assertTag(
|
$this->assertTag(
|
||||||
array(
|
array(
|
||||||
'id' => 'cipherdata',
|
'id' => 'cipherdata',
|
||||||
'content' => htmlspecialchars(json_encode($oldPaste), ENT_NOQUOTES)
|
'content' => htmlspecialchars(helper::getPasteAsJson($meta), ENT_NOQUOTES)
|
||||||
),
|
),
|
||||||
$content,
|
$content,
|
||||||
'outputs data correctly'
|
'outputs data correctly'
|
||||||
@@ -704,10 +754,10 @@ class zerobinTest extends PHPUnit_Framework_TestCase
|
|||||||
public function testReadOldFormat()
|
public function testReadOldFormat()
|
||||||
{
|
{
|
||||||
$this->reset();
|
$this->reset();
|
||||||
$oldPaste = self::$paste;
|
$oldPaste = helper::getPaste();
|
||||||
unset($oldPaste['meta']['formatter']);
|
unset($oldPaste['meta']['formatter']);
|
||||||
$this->_model->create(self::$pasteid, $oldPaste);
|
$this->_model->create(helper::getPasteId(), $oldPaste);
|
||||||
$_SERVER['QUERY_STRING'] = self::$pasteid;
|
$_SERVER['QUERY_STRING'] = helper::getPasteId();
|
||||||
ob_start();
|
ob_start();
|
||||||
new zerobin;
|
new zerobin;
|
||||||
$content = ob_get_contents();
|
$content = ob_get_contents();
|
||||||
@@ -715,7 +765,7 @@ class zerobinTest extends PHPUnit_Framework_TestCase
|
|||||||
$this->assertTag(
|
$this->assertTag(
|
||||||
array(
|
array(
|
||||||
'id' => 'cipherdata',
|
'id' => 'cipherdata',
|
||||||
'content' => htmlspecialchars(json_encode($oldPaste), ENT_NOQUOTES)
|
'content' => htmlspecialchars(helper::getPasteAsJson($oldPaste['meta']), ENT_NOQUOTES)
|
||||||
),
|
),
|
||||||
$content,
|
$content,
|
||||||
'outputs data correctly'
|
'outputs data correctly'
|
||||||
@@ -728,10 +778,10 @@ class zerobinTest extends PHPUnit_Framework_TestCase
|
|||||||
public function testDelete()
|
public function testDelete()
|
||||||
{
|
{
|
||||||
$this->reset();
|
$this->reset();
|
||||||
$this->_model->create(self::$pasteid, self::$paste);
|
$this->_model->create(helper::getPasteId(), helper::getPaste());
|
||||||
$this->assertTrue($this->_model->exists(self::$pasteid), 'paste exists before deleting data');
|
$this->assertTrue($this->_model->exists(helper::getPasteId()), 'paste exists before deleting data');
|
||||||
$_GET['pasteid'] = self::$pasteid;
|
$_GET['pasteid'] = helper::getPasteId();
|
||||||
$_GET['deletetoken'] = hash_hmac('sha1', self::$pasteid, serversalt::get());
|
$_GET['deletetoken'] = hash_hmac('sha1', helper::getPasteId(), serversalt::get());
|
||||||
ob_start();
|
ob_start();
|
||||||
new zerobin;
|
new zerobin;
|
||||||
$content = ob_get_contents();
|
$content = ob_get_contents();
|
||||||
@@ -743,7 +793,7 @@ class zerobinTest extends PHPUnit_Framework_TestCase
|
|||||||
$content,
|
$content,
|
||||||
'outputs deleted status correctly'
|
'outputs deleted status correctly'
|
||||||
);
|
);
|
||||||
$this->assertFalse($this->_model->exists(self::$pasteid), 'paste successfully deleted');
|
$this->assertFalse($this->_model->exists(helper::getPasteId()), 'paste successfully deleted');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -752,7 +802,7 @@ class zerobinTest extends PHPUnit_Framework_TestCase
|
|||||||
public function testDeleteInvalidId()
|
public function testDeleteInvalidId()
|
||||||
{
|
{
|
||||||
$this->reset();
|
$this->reset();
|
||||||
$this->_model->create(self::$pasteid, self::$paste);
|
$this->_model->create(helper::getPasteId(), helper::getPaste());
|
||||||
$_GET['pasteid'] = 'foo';
|
$_GET['pasteid'] = 'foo';
|
||||||
$_GET['deletetoken'] = 'bar';
|
$_GET['deletetoken'] = 'bar';
|
||||||
ob_start();
|
ob_start();
|
||||||
@@ -766,7 +816,7 @@ class zerobinTest extends PHPUnit_Framework_TestCase
|
|||||||
$content,
|
$content,
|
||||||
'outputs delete error correctly'
|
'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');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -775,7 +825,7 @@ class zerobinTest extends PHPUnit_Framework_TestCase
|
|||||||
public function testDeleteInexistantId()
|
public function testDeleteInexistantId()
|
||||||
{
|
{
|
||||||
$this->reset();
|
$this->reset();
|
||||||
$_GET['pasteid'] = self::$pasteid;
|
$_GET['pasteid'] = helper::getPasteId();
|
||||||
$_GET['deletetoken'] = 'bar';
|
$_GET['deletetoken'] = 'bar';
|
||||||
ob_start();
|
ob_start();
|
||||||
new zerobin;
|
new zerobin;
|
||||||
@@ -796,8 +846,8 @@ class zerobinTest extends PHPUnit_Framework_TestCase
|
|||||||
public function testDeleteInvalidToken()
|
public function testDeleteInvalidToken()
|
||||||
{
|
{
|
||||||
$this->reset();
|
$this->reset();
|
||||||
$this->_model->create(self::$pasteid, self::$paste);
|
$this->_model->create(helper::getPasteId(), helper::getPaste());
|
||||||
$_GET['pasteid'] = self::$pasteid;
|
$_GET['pasteid'] = helper::getPasteId();
|
||||||
$_GET['deletetoken'] = 'bar';
|
$_GET['deletetoken'] = 'bar';
|
||||||
ob_start();
|
ob_start();
|
||||||
new zerobin;
|
new zerobin;
|
||||||
@@ -810,7 +860,7 @@ class zerobinTest extends PHPUnit_Framework_TestCase
|
|||||||
$content,
|
$content,
|
||||||
'outputs delete error correctly'
|
'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');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -819,18 +869,19 @@ class zerobinTest extends PHPUnit_Framework_TestCase
|
|||||||
public function testDeleteBurnAfterReading()
|
public function testDeleteBurnAfterReading()
|
||||||
{
|
{
|
||||||
$this->reset();
|
$this->reset();
|
||||||
$burnPaste = self::$paste;
|
$burnPaste = helper::getPaste(array('burnafterreading' => true));
|
||||||
$burnPaste['meta']['burnafterreading'] = true;
|
$this->_model->create(helper::getPasteId(), $burnPaste);
|
||||||
$this->_model->create(self::$pasteid, $burnPaste);
|
$this->assertTrue($this->_model->exists(helper::getPasteId()), 'paste exists before deleting data');
|
||||||
$this->assertTrue($this->_model->exists(self::$pasteid), 'paste exists before deleting data');
|
$_POST['deletetoken'] = 'burnafterreading';
|
||||||
$_GET['pasteid'] = self::$pasteid;
|
$_SERVER['QUERY_STRING'] = helper::getPasteId();
|
||||||
$_GET['deletetoken'] = 'burnafterreading';
|
$_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
|
||||||
|
$_SERVER['REQUEST_METHOD'] = 'POST';
|
||||||
ob_start();
|
ob_start();
|
||||||
new zerobin;
|
new zerobin;
|
||||||
$content = ob_get_contents();
|
$content = ob_get_contents();
|
||||||
$response = json_decode($content, true);
|
$response = json_decode($content, true);
|
||||||
$this->assertEquals(0, $response['status'], 'outputs status');
|
$this->assertEquals(0, $response['status'], 'outputs status');
|
||||||
$this->assertFalse($this->_model->exists(self::$pasteid), 'paste successfully deleted');
|
$this->assertFalse($this->_model->exists(helper::getPasteId()), 'paste successfully deleted');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -839,16 +890,18 @@ class zerobinTest extends PHPUnit_Framework_TestCase
|
|||||||
public function testDeleteInvalidBurnAfterReading()
|
public function testDeleteInvalidBurnAfterReading()
|
||||||
{
|
{
|
||||||
$this->reset();
|
$this->reset();
|
||||||
$this->_model->create(self::$pasteid, self::$paste);
|
$this->_model->create(helper::getPasteId(), helper::getPaste());
|
||||||
$this->assertTrue($this->_model->exists(self::$pasteid), 'paste exists before deleting data');
|
$this->assertTrue($this->_model->exists(helper::getPasteId()), 'paste exists before deleting data');
|
||||||
$_GET['pasteid'] = self::$pasteid;
|
$_POST['deletetoken'] = 'burnafterreading';
|
||||||
$_GET['deletetoken'] = 'burnafterreading';
|
$_SERVER['QUERY_STRING'] = helper::getPasteId();
|
||||||
|
$_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
|
||||||
|
$_SERVER['REQUEST_METHOD'] = 'POST';
|
||||||
ob_start();
|
ob_start();
|
||||||
new zerobin;
|
new zerobin;
|
||||||
$content = ob_get_contents();
|
$content = ob_get_contents();
|
||||||
$response = json_decode($content, true);
|
$response = json_decode($content, true);
|
||||||
$this->assertEquals(1, $response['status'], 'outputs status');
|
$this->assertEquals(1, $response['status'], 'outputs status');
|
||||||
$this->assertTrue($this->_model->exists(self::$pasteid), 'paste successfully deleted');
|
$this->assertTrue($this->_model->exists(helper::getPasteId()), 'paste successfully deleted');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -857,12 +910,11 @@ class zerobinTest extends PHPUnit_Framework_TestCase
|
|||||||
public function testDeleteExpired()
|
public function testDeleteExpired()
|
||||||
{
|
{
|
||||||
$this->reset();
|
$this->reset();
|
||||||
$expiredPaste = self::$paste;
|
$expiredPaste = helper::getPaste(array('expire_date' => 1000));
|
||||||
$expiredPaste['meta']['expire_date'] = 1000;
|
$this->assertFalse($this->_model->exists(helper::getPasteId()), 'paste does not exist before being created');
|
||||||
$this->assertFalse($this->_model->exists(self::$pasteid), 'paste does not exist before being created');
|
$this->_model->create(helper::getPasteId(), $expiredPaste);
|
||||||
$this->_model->create(self::$pasteid, $expiredPaste);
|
$this->assertTrue($this->_model->exists(helper::getPasteId()), 'paste exists before deleting data');
|
||||||
$this->assertTrue($this->_model->exists(self::$pasteid), 'paste exists before deleting data');
|
$_GET['pasteid'] = helper::getPasteId();
|
||||||
$_GET['pasteid'] = self::$pasteid;
|
|
||||||
$_GET['deletetoken'] = 'does not matter in this context, but has to be set';
|
$_GET['deletetoken'] = 'does not matter in this context, but has to be set';
|
||||||
ob_start();
|
ob_start();
|
||||||
new zerobin;
|
new zerobin;
|
||||||
@@ -875,6 +927,6 @@ class zerobinTest extends PHPUnit_Framework_TestCase
|
|||||||
$content,
|
$content,
|
||||||
'outputs error correctly'
|
'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
|
<?php
|
||||||
class zerobin_dataTest extends PHPUnit_Framework_TestCase
|
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 $_model;
|
||||||
|
|
||||||
private $_path;
|
private $_path;
|
||||||
@@ -42,29 +20,47 @@ class zerobin_dataTest extends PHPUnit_Framework_TestCase
|
|||||||
|
|
||||||
public function testFileBasedDataStoreWorks()
|
public function testFileBasedDataStoreWorks()
|
||||||
{
|
{
|
||||||
|
$this->_model->delete(helper::getPasteId());
|
||||||
|
|
||||||
// storing pastes
|
// storing pastes
|
||||||
$this->assertFalse($this->_model->exists(self::$pasteid), 'paste does not yet exist');
|
$paste = helper::getPaste(array('expire_date' => 1344803344));
|
||||||
$this->assertTrue($this->_model->create(self::$pasteid, self::$paste), 'store new paste');
|
$this->assertFalse($this->_model->exists(helper::getPasteId()), 'paste does not yet exist');
|
||||||
$this->assertTrue($this->_model->exists(self::$pasteid), 'paste exists after storing it');
|
$this->assertTrue($this->_model->create(helper::getPasteId(), $paste), 'store new paste');
|
||||||
$this->assertFalse($this->_model->create(self::$pasteid, self::$paste), 'unable to store the same paste twice');
|
$this->assertTrue($this->_model->exists(helper::getPasteId()), 'paste exists after storing it');
|
||||||
$this->assertEquals(json_decode(json_encode(self::$paste)), $this->_model->read(self::$pasteid));
|
$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
|
// storing comments
|
||||||
$this->assertFalse($this->_model->existsComment(self::$pasteid, self::$pasteid, self::$commentid), 'comment does not yet exist');
|
$this->assertFalse($this->_model->existsComment(helper::getPasteId(), helper::getPasteId(), helper::getCommentId()), 'comment does not yet exist');
|
||||||
$this->assertTrue($this->_model->createComment(self::$pasteid, self::$pasteid, self::$commentid, self::$comment) !== false, 'store comment');
|
$this->assertTrue($this->_model->createComment(helper::getPasteId(), helper::getPasteId(), helper::getCommentId(), helper::getComment()) !== false, 'store comment');
|
||||||
$this->assertTrue($this->_model->existsComment(self::$pasteid, self::$pasteid, self::$commentid), 'comment exists after storing it');
|
$this->assertTrue($this->_model->existsComment(helper::getPasteId(), helper::getPasteId(), helper::getCommentId()), 'comment exists after storing it');
|
||||||
$comment = json_decode(json_encode(self::$comment));
|
$comment = json_decode(json_encode(helper::getComment()));
|
||||||
$comment->meta->commentid = self::$commentid;
|
$comment->id = helper::getCommentId();
|
||||||
$comment->meta->parentid = self::$pasteid;
|
$comment->parentid = helper::getPasteId();
|
||||||
$this->assertEquals(
|
$this->assertEquals(
|
||||||
array($comment->meta->postdate => $comment),
|
array($comment->meta->postdate => $comment),
|
||||||
$this->_model->readComments(self::$pasteid)
|
$this->_model->readComments(helper::getPasteId())
|
||||||
);
|
);
|
||||||
|
|
||||||
// deleting pastes
|
// deleting pastes
|
||||||
$this->_model->delete(self::$pasteid);
|
$this->_model->delete(helper::getPasteId());
|
||||||
$this->assertFalse($this->_model->exists(self::$pasteid), 'paste successfully deleted');
|
$this->assertFalse($this->_model->exists(helper::getPasteId()), 'paste successfully deleted');
|
||||||
$this->assertFalse($this->_model->existsComment(self::$pasteid, self::$pasteid, self::$commentid), 'comment was deleted with paste');
|
$this->assertFalse($this->_model->existsComment(helper::getPasteId(), helper::getPasteId(), helper::getCommentId()), 'comment was deleted with paste');
|
||||||
$this->assertFalse($this->_model->read(self::$pasteid), 'paste can no longer be found');
|
$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
|
<?php
|
||||||
class zerobin_dbTest extends PHPUnit_Framework_TestCase
|
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 $_model;
|
||||||
|
|
||||||
|
private $_options = array(
|
||||||
|
'dsn' => 'sqlite::memory:',
|
||||||
|
'usr' => null,
|
||||||
|
'pwd' => null,
|
||||||
|
'opt' => array(PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION),
|
||||||
|
);
|
||||||
|
|
||||||
public function setUp()
|
public function setUp()
|
||||||
{
|
{
|
||||||
/* Setup Routine */
|
/* Setup Routine */
|
||||||
$this->_model = zerobin_db::getInstance(
|
$this->_model = zerobin_db::getInstance($this->_options);
|
||||||
array(
|
|
||||||
'dsn' => 'sqlite::memory:',
|
|
||||||
'usr' => null,
|
|
||||||
'pwd' => null,
|
|
||||||
'opt' => array(PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION),
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testDatabaseBasedDataStoreWorks()
|
public function testDatabaseBasedDataStoreWorks()
|
||||||
{
|
{
|
||||||
|
$this->_model->delete(helper::getPasteId());
|
||||||
|
|
||||||
// storing pastes
|
// storing pastes
|
||||||
$this->assertFalse($this->_model->exists(self::$pasteid), 'paste does not yet exist');
|
$paste = helper::getPaste(array('expire_date' => 1344803344));
|
||||||
$this->assertTrue($this->_model->create(self::$pasteid, self::$paste), 'store new paste');
|
$this->assertFalse($this->_model->exists(helper::getPasteId()), 'paste does not yet exist');
|
||||||
$this->assertTrue($this->_model->exists(self::$pasteid), 'paste exists after storing it');
|
$this->assertTrue($this->_model->create(helper::getPasteId(), $paste), 'store new paste');
|
||||||
$this->assertFalse($this->_model->create(self::$pasteid, self::$paste), 'unable to store the same paste twice');
|
$this->assertTrue($this->_model->exists(helper::getPasteId()), 'paste exists after storing it');
|
||||||
$this->assertEquals(json_decode(json_encode(self::$paste)), $this->_model->read(self::$pasteid));
|
$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
|
// storing comments
|
||||||
$this->assertFalse($this->_model->existsComment(self::$pasteid, self::$pasteid, self::$commentid), 'comment does not yet exist');
|
$this->assertFalse($this->_model->existsComment(helper::getPasteId(), helper::getPasteId(), helper::getCommentId()), 'comment does not yet exist');
|
||||||
$this->assertTrue($this->_model->createComment(self::$pasteid, self::$pasteid, self::$commentid, self::$comment) !== false, 'store comment');
|
$this->assertTrue($this->_model->createComment(helper::getPasteId(), helper::getPasteId(), helper::getCommentId(), helper::getComment()) !== false, 'store comment');
|
||||||
$this->assertTrue($this->_model->existsComment(self::$pasteid, self::$pasteid, self::$commentid), 'comment exists after storing it');
|
$this->assertTrue($this->_model->existsComment(helper::getPasteId(), helper::getPasteId(), helper::getCommentId()), 'comment exists after storing it');
|
||||||
$comment = json_decode(json_encode(self::$comment));
|
$comment = json_decode(json_encode(helper::getComment()));
|
||||||
$comment->meta->commentid = self::$commentid;
|
$comment->id = helper::getCommentId();
|
||||||
$comment->meta->parentid = self::$pasteid;
|
$comment->parentid = helper::getPasteId();
|
||||||
$this->assertEquals(
|
$this->assertEquals(
|
||||||
array($comment->meta->postdate => $comment),
|
array($comment->meta->postdate => $comment),
|
||||||
$this->_model->readComments(self::$pasteid)
|
$this->_model->readComments(helper::getPasteId())
|
||||||
);
|
);
|
||||||
|
|
||||||
// deleting pastes
|
// deleting pastes
|
||||||
$this->_model->delete(self::$pasteid);
|
$this->_model->delete(helper::getPasteId());
|
||||||
$this->assertFalse($this->_model->exists(self::$pasteid), 'paste successfully deleted');
|
$this->assertFalse($this->_model->exists(helper::getPasteId()), 'paste successfully deleted');
|
||||||
$this->assertFalse($this->_model->existsComment(self::$pasteid, self::$pasteid, self::$commentid), 'comment was deleted with paste');
|
$this->assertFalse($this->_model->existsComment(helper::getPasteId(), helper::getPasteId(), helper::getCommentId()), 'comment was deleted with paste');
|
||||||
$this->assertFalse($this->_model->read(self::$pasteid), 'paste can no longer be found');
|
$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
|
'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