mirror of
https://github.com/PrivateBin/PrivateBin.git
synced 2024-03-22 13:10:41 +08:00
Merge branch 'burnafterreading-fix'
This commit is contained in:
commit
4ccb4b1af4
|
@ -11,7 +11,6 @@ insert_final_newline = true
|
||||||
|
|
||||||
[*.css]
|
[*.css]
|
||||||
indent_style = tab
|
indent_style = tab
|
||||||
indent_size = 4
|
|
||||||
|
|
||||||
[*.js]
|
[*.js]
|
||||||
indent_style = space
|
indent_style = space
|
||||||
|
@ -23,7 +22,6 @@ indent_size = 4
|
||||||
|
|
||||||
[*.jsonld]
|
[*.jsonld]
|
||||||
indent_style = tab
|
indent_style = tab
|
||||||
indent_size = 4
|
|
||||||
|
|
||||||
[*.php]
|
[*.php]
|
||||||
indent_style = space
|
indent_style = space
|
||||||
|
@ -31,7 +29,6 @@ indent_size = 4
|
||||||
|
|
||||||
[*.{htm,html}]
|
[*.{htm,html}]
|
||||||
indent_style = tab
|
indent_style = tab
|
||||||
indent_size = 4
|
|
||||||
|
|
||||||
[*.{md,markdown}]
|
[*.{md,markdown}]
|
||||||
indent_style = space
|
indent_style = space
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
* CHANGED: Minimum required PHP version is 5.4 (#186)
|
* CHANGED: Minimum required PHP version is 5.4 (#186)
|
||||||
* CHANGED: Shipped .htaccess files were updated for Apache 2.4 (#192)
|
* CHANGED: Shipped .htaccess files were updated for Apache 2.4 (#192)
|
||||||
* CHANGED: Cleanup of bootstrap template variants and moved icons to `img` directory
|
* CHANGED: Cleanup of bootstrap template variants and moved icons to `img` directory
|
||||||
|
* CHANGED: Removed option to hide clone button on expiring pastes, since this requires reading the paste for rendering the template, which leaks information on the pastes state
|
||||||
* **1.1.1 (2017-10-06)**
|
* **1.1.1 (2017-10-06)**
|
||||||
* CHANGED: Switched to `.php` file extension for configuration file, to avoid leaking configuration data in unprotected installation.
|
* CHANGED: Switched to `.php` file extension for configuration file, to avoid leaking configuration data in unprotected installation.
|
||||||
* **1.1 (2016-12-26)**
|
* **1.1 (2016-12-26)**
|
||||||
|
|
|
@ -39,7 +39,7 @@ process (see also
|
||||||
> #### PATH Example
|
> #### PATH Example
|
||||||
> Your PrivateBin installation lives in a subfolder called "paste" inside of
|
> Your PrivateBin installation lives in a subfolder called "paste" inside of
|
||||||
> your document root. The URL looks like this:
|
> your document root. The URL looks like this:
|
||||||
> http://example.com/paste/
|
> https://example.com/paste/
|
||||||
>
|
>
|
||||||
> The full path of PrivateBin on your webserver is:
|
> The full path of PrivateBin on your webserver is:
|
||||||
> /home/example.com/htdocs/paste
|
> /home/example.com/htdocs/paste
|
||||||
|
|
|
@ -22,10 +22,6 @@ fileupload = false
|
||||||
; preselect the burn-after-reading feature, defaults to false
|
; preselect the burn-after-reading feature, defaults to false
|
||||||
burnafterreadingselected = false
|
burnafterreadingselected = false
|
||||||
|
|
||||||
; delete a burn after reading paste immediatly after it is first accessed from
|
|
||||||
; the server and do not wait for a successful decryption
|
|
||||||
instantburnafterreading = false
|
|
||||||
|
|
||||||
; which display mode to preselect by default, defaults to "plaintext"
|
; which display mode to preselect by default, defaults to "plaintext"
|
||||||
; make sure the value exists in [formatter_options]
|
; make sure the value exists in [formatter_options]
|
||||||
defaultformatter = "plaintext"
|
defaultformatter = "plaintext"
|
||||||
|
@ -85,10 +81,6 @@ zerobincompatibility = false
|
||||||
; 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
|
||||||
|
|
337
js/privatebin.js
337
js/privatebin.js
|
@ -45,6 +45,18 @@ jQuery.PrivateBin = (function($, sjcl, Base64, RawDeflate) {
|
||||||
var Helper = (function () {
|
var Helper = (function () {
|
||||||
var me = {};
|
var me = {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* blacklist of UserAgents (parts) known to belong to a bot
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
* @enum {Object}
|
||||||
|
* @readonly
|
||||||
|
*/
|
||||||
|
var BadBotUA = [
|
||||||
|
'Bot',
|
||||||
|
'bot'
|
||||||
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* cache for script location
|
* cache for script location
|
||||||
*
|
*
|
||||||
|
@ -121,7 +133,7 @@ jQuery.PrivateBin = (function($, sjcl, Base64, RawDeflate) {
|
||||||
* URLs to handle:
|
* URLs to handle:
|
||||||
* <pre>
|
* <pre>
|
||||||
* magnet:?xt.1=urn:sha1:YNCKHTQCWBTRNJIV4WNAE52SJUQCZO5C&xt.2=urn:sha1:TXGCZQTH26NL6OUQAJJPFALHG2LTGBC7
|
* magnet:?xt.1=urn:sha1:YNCKHTQCWBTRNJIV4WNAE52SJUQCZO5C&xt.2=urn:sha1:TXGCZQTH26NL6OUQAJJPFALHG2LTGBC7
|
||||||
* http://example.com:8800/zero/?6f09182b8ea51997#WtLEUO5Epj9UHAV9JFs+6pUQZp13TuspAUjnF+iM+dM=
|
* https://example.com:8800/zero/?6f09182b8ea51997#WtLEUO5Epj9UHAV9JFs+6pUQZp13TuspAUjnF+iM+dM=
|
||||||
* http://user:example.com@localhost:8800/zero/?6f09182b8ea51997#WtLEUO5Epj9UHAV9JFs+6pUQZp13TuspAUjnF+iM+dM=
|
* http://user:example.com@localhost:8800/zero/?6f09182b8ea51997#WtLEUO5Epj9UHAV9JFs+6pUQZp13TuspAUjnF+iM+dM=
|
||||||
* </pre>
|
* </pre>
|
||||||
*
|
*
|
||||||
|
@ -204,7 +216,7 @@ jQuery.PrivateBin = (function($, sjcl, Base64, RawDeflate) {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* get the current location (without search or hash part of the URL),
|
* get the current location (without search or hash part of the URL),
|
||||||
* eg. http://example.com/path/?aaaa#bbbb --> http://example.com/path/
|
* eg. https://example.com/path/?aaaa#bbbb --> https://example.com/path/
|
||||||
*
|
*
|
||||||
* @name Helper.baseUri
|
* @name Helper.baseUri
|
||||||
* @function
|
* @function
|
||||||
|
@ -232,6 +244,26 @@ jQuery.PrivateBin = (function($, sjcl, Base64, RawDeflate) {
|
||||||
baseUri = null;
|
baseUri = null;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* checks whether this is a bot we dislike
|
||||||
|
*
|
||||||
|
* @name Helper.isBadBot
|
||||||
|
* @function
|
||||||
|
* @return {bool}
|
||||||
|
*/
|
||||||
|
me.isBadBot = function() {
|
||||||
|
// check whether a bot user agent part can be found in the current
|
||||||
|
// user agent
|
||||||
|
var arrayLength = BadBotUA.length;
|
||||||
|
for (var i = 0; i < arrayLength; i++) {
|
||||||
|
if (navigator.userAgent.indexOf(BadBotUA) >= 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
return me;
|
return me;
|
||||||
})();
|
})();
|
||||||
|
|
||||||
|
@ -358,7 +390,7 @@ jQuery.PrivateBin = (function($, sjcl, Base64, RawDeflate) {
|
||||||
// file is loaded
|
// file is loaded
|
||||||
}
|
}
|
||||||
|
|
||||||
// for all other langauges than English for which this behaviour
|
// for all other languages than English for which this behaviour
|
||||||
// is expected as it is built-in, log error
|
// is expected as it is built-in, log error
|
||||||
if (language !== null && language !== 'en') {
|
if (language !== null && language !== 'en') {
|
||||||
console.error('Missing translation for: \'' + messageId + '\' in language ' + language);
|
console.error('Missing translation for: \'' + messageId + '\' in language ' + language);
|
||||||
|
@ -623,7 +655,7 @@ jQuery.PrivateBin = (function($, sjcl, Base64, RawDeflate) {
|
||||||
var Model = (function () {
|
var Model = (function () {
|
||||||
var me = {};
|
var me = {};
|
||||||
|
|
||||||
var $cipherData,
|
var pasteData = null,
|
||||||
$templates;
|
$templates;
|
||||||
|
|
||||||
var id = null, symmetricKey = null;
|
var id = null, symmetricKey = null;
|
||||||
|
@ -653,32 +685,53 @@ jQuery.PrivateBin = (function($, sjcl, Base64, RawDeflate) {
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* check if cipher data was supplied
|
* returns the paste data (including the cipher data)
|
||||||
*
|
*
|
||||||
* @name Model.getCipherData
|
* @name Model.getPasteData
|
||||||
* @function
|
|
||||||
* @return boolean
|
|
||||||
*/
|
|
||||||
me.hasCipherData = function()
|
|
||||||
{
|
|
||||||
return me.getCipherData().length > 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* returns the cipher data
|
|
||||||
*
|
|
||||||
* @name Model.getCipherData
|
|
||||||
* @function
|
* @function
|
||||||
|
* @param {function} callback (optional) Called when data is available
|
||||||
|
* @param {function} useCache (optional) Whether to use the cache or
|
||||||
|
* force a data reload. Default: true
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
me.getCipherData = function()
|
me.getPasteData = function(callback, useCache)
|
||||||
{
|
{
|
||||||
return $cipherData.text();
|
// use cache if possible/allowed
|
||||||
|
if (useCache !== false && pasteData !== null) {
|
||||||
|
//execute callback
|
||||||
|
if (typeof callback === 'function') {
|
||||||
|
return callback(pasteData);
|
||||||
|
}
|
||||||
|
|
||||||
|
// alternatively just using inline
|
||||||
|
return pasteData;
|
||||||
|
}
|
||||||
|
|
||||||
|
// reload data
|
||||||
|
Uploader.prepare();
|
||||||
|
Uploader.setUrl(Helper.baseUri() + '?' + me.getPasteId());
|
||||||
|
|
||||||
|
Uploader.setFailure(function (status, data) {
|
||||||
|
// revert loading status…
|
||||||
|
Alert.hideLoading();
|
||||||
|
TopNav.showViewButtons();
|
||||||
|
|
||||||
|
// show error message
|
||||||
|
Alert.showError(Uploader.parseUploadError(status, data, 'getting paste data'));
|
||||||
|
});
|
||||||
|
Uploader.setSuccess(function (status, data) {
|
||||||
|
pasteData = data;
|
||||||
|
|
||||||
|
if (typeof callback === 'function') {
|
||||||
|
return callback(data);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Uploader.run();
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* get the pastes unique identifier from the URL,
|
* get the pastes unique identifier from the URL,
|
||||||
* eg. http://example.com/path/?c05354954c49a487#dfdsdgdgdfgdf returns c05354954c49a487
|
* eg. https://example.com/path/?c05354954c49a487#dfdsdgdgdfgdf returns c05354954c49a487
|
||||||
*
|
*
|
||||||
* @name Model.getPasteId
|
* @name Model.getPasteId
|
||||||
* @function
|
* @function
|
||||||
|
@ -688,6 +741,7 @@ jQuery.PrivateBin = (function($, sjcl, Base64, RawDeflate) {
|
||||||
me.getPasteId = function()
|
me.getPasteId = function()
|
||||||
{
|
{
|
||||||
if (id === null) {
|
if (id === null) {
|
||||||
|
// Attention: This also returns the delete token inside of the ID, if it is specified
|
||||||
id = window.location.search.substring(1);
|
id = window.location.search.substring(1);
|
||||||
|
|
||||||
if (id === '') {
|
if (id === '') {
|
||||||
|
@ -696,7 +750,19 @@ jQuery.PrivateBin = (function($, sjcl, Base64, RawDeflate) {
|
||||||
}
|
}
|
||||||
|
|
||||||
return id;
|
return id;
|
||||||
};
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true, when the URL has a delete token and the current call was used for deleting a paste.
|
||||||
|
*
|
||||||
|
* @name Model.hasDeleteToken
|
||||||
|
* @function
|
||||||
|
* @return {bool}
|
||||||
|
*/
|
||||||
|
me.hasDeleteToken = function()
|
||||||
|
{
|
||||||
|
return window.location.search.indexOf('deletetoken') !== -1;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* return the deciphering key stored in anchor part of the URL
|
* return the deciphering key stored in anchor part of the URL
|
||||||
|
@ -751,7 +817,7 @@ jQuery.PrivateBin = (function($, sjcl, Base64, RawDeflate) {
|
||||||
*/
|
*/
|
||||||
me.reset = function()
|
me.reset = function()
|
||||||
{
|
{
|
||||||
$cipherData = $templates = id = symmetricKey = null;
|
pasteData = $templates = id = symmetricKey = null;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -764,7 +830,6 @@ jQuery.PrivateBin = (function($, sjcl, Base64, RawDeflate) {
|
||||||
*/
|
*/
|
||||||
me.init = function()
|
me.init = function()
|
||||||
{
|
{
|
||||||
$cipherData = $('#cipherdata');
|
|
||||||
$templates = $('#templates');
|
$templates = $('#templates');
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1259,8 +1324,8 @@ jQuery.PrivateBin = (function($, sjcl, Base64, RawDeflate) {
|
||||||
if (pasteMetaData.burnafterreading) {
|
if (pasteMetaData.burnafterreading) {
|
||||||
// display paste "for your eyes only" if it is deleted
|
// display paste "for your eyes only" if it is deleted
|
||||||
|
|
||||||
// actually remove paste, before we claim it is deleted
|
// the paste has been deleted when the JSON with the ciphertext
|
||||||
Controller.removePaste(Model.getPasteId(), 'burnafterreading');
|
// has been downloaded
|
||||||
|
|
||||||
Alert.showRemaining("FOR YOUR EYES ONLY. Don't close this window, this message can't be displayed again.");
|
Alert.showRemaining("FOR YOUR EYES ONLY. Don't close this window, this message can't be displayed again.");
|
||||||
$remainingTime.addClass('foryoureyesonly');
|
$remainingTime.addClass('foryoureyesonly');
|
||||||
|
@ -1402,6 +1467,21 @@ jQuery.PrivateBin = (function($, sjcl, Base64, RawDeflate) {
|
||||||
return password;
|
return password;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* resets the password to an empty string
|
||||||
|
*
|
||||||
|
* @name Prompt.reset
|
||||||
|
* @function
|
||||||
|
*/
|
||||||
|
me.reset = function()
|
||||||
|
{
|
||||||
|
// reset internal
|
||||||
|
password = '';
|
||||||
|
|
||||||
|
// and also reset UI
|
||||||
|
$passwordDecrypt.val('');
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* init status manager
|
* init status manager
|
||||||
*
|
*
|
||||||
|
@ -2149,7 +2229,6 @@ jQuery.PrivateBin = (function($, sjcl, Base64, RawDeflate) {
|
||||||
loadedFile = $fileInput[0].files[0];
|
loadedFile = $fileInput[0].files[0];
|
||||||
$dragAndDropFileName.text('');
|
$dragAndDropFileName.text('');
|
||||||
} else {
|
} else {
|
||||||
// TODO: cannot set original $fileWrap here for security reasons…
|
|
||||||
$dragAndDropFileName.text(loadedFile.name);
|
$dragAndDropFileName.text(loadedFile.name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2295,6 +2374,10 @@ jQuery.PrivateBin = (function($, sjcl, Base64, RawDeflate) {
|
||||||
if (items.hasOwnProperty(i)) {
|
if (items.hasOwnProperty(i)) {
|
||||||
var item = items[i];
|
var item = items[i];
|
||||||
if (item.kind === 'file') {
|
if (item.kind === 'file') {
|
||||||
|
//Clear the file input:
|
||||||
|
$fileInput.wrap('<form>').closest('form').get(0).reset();
|
||||||
|
$fileInput.unwrap();
|
||||||
|
|
||||||
readFileData(item.getAsFile());
|
readFileData(item.getAsFile());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2685,9 +2768,11 @@ jQuery.PrivateBin = (function($, sjcl, Base64, RawDeflate) {
|
||||||
$passwordInput,
|
$passwordInput,
|
||||||
$rawTextButton,
|
$rawTextButton,
|
||||||
$qrCodeLink,
|
$qrCodeLink,
|
||||||
$sendButton;
|
$sendButton,
|
||||||
|
$retryButton;
|
||||||
|
|
||||||
var pasteExpiration = '1week';
|
var pasteExpiration = '1week',
|
||||||
|
retryButtonCallback;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* set the expiration on bootstrap templates in dropdown
|
* set the expiration on bootstrap templates in dropdown
|
||||||
|
@ -2836,6 +2921,19 @@ jQuery.PrivateBin = (function($, sjcl, Base64, RawDeflate) {
|
||||||
Controller.newPaste();
|
Controller.newPaste();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* retrys some callback registered before
|
||||||
|
*
|
||||||
|
* @name TopNav.clickRetryButton
|
||||||
|
* @private
|
||||||
|
* @function
|
||||||
|
* @param {Event} event
|
||||||
|
*/
|
||||||
|
function clickRetryButton(event)
|
||||||
|
{
|
||||||
|
retryButtonCallback(event);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* removes the existing attachment
|
* removes the existing attachment
|
||||||
*
|
*
|
||||||
|
@ -2915,8 +3013,8 @@ jQuery.PrivateBin = (function($, sjcl, Base64, RawDeflate) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$newButton.addClass('hidden');
|
|
||||||
$cloneButton.addClass('hidden');
|
$cloneButton.addClass('hidden');
|
||||||
|
$newButton.addClass('hidden');
|
||||||
$rawTextButton.addClass('hidden');
|
$rawTextButton.addClass('hidden');
|
||||||
$qrCodeLink.addClass('hidden');
|
$qrCodeLink.addClass('hidden');
|
||||||
|
|
||||||
|
@ -2948,14 +3046,14 @@ jQuery.PrivateBin = (function($, sjcl, Base64, RawDeflate) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$sendButton.removeClass('hidden');
|
$attach.removeClass('hidden');
|
||||||
|
$burnAfterReadingOption.removeClass('hidden');
|
||||||
$expiration.removeClass('hidden');
|
$expiration.removeClass('hidden');
|
||||||
$formatter.removeClass('hidden');
|
$formatter.removeClass('hidden');
|
||||||
$burnAfterReadingOption.removeClass('hidden');
|
|
||||||
$openDiscussionOption.removeClass('hidden');
|
|
||||||
$newButton.removeClass('hidden');
|
$newButton.removeClass('hidden');
|
||||||
|
$openDiscussionOption.removeClass('hidden');
|
||||||
$password.removeClass('hidden');
|
$password.removeClass('hidden');
|
||||||
$attach.removeClass('hidden');
|
$sendButton.removeClass('hidden');
|
||||||
|
|
||||||
createButtonsDisplayed = true;
|
createButtonsDisplayed = true;
|
||||||
};
|
};
|
||||||
|
@ -2996,6 +3094,28 @@ jQuery.PrivateBin = (function($, sjcl, Base64, RawDeflate) {
|
||||||
$newButton.removeClass('hidden');
|
$newButton.removeClass('hidden');
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* only shows the "retry" button
|
||||||
|
*
|
||||||
|
* @name TopNav.showRetryButton
|
||||||
|
* @function
|
||||||
|
*/
|
||||||
|
me.showRetryButton = function()
|
||||||
|
{
|
||||||
|
$retryButton.removeClass('hidden');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* hides the "retry" button
|
||||||
|
*
|
||||||
|
* @name TopNav.hideRetryButton
|
||||||
|
* @function
|
||||||
|
*/
|
||||||
|
me.hideRetryButton = function()
|
||||||
|
{
|
||||||
|
$retryButton.addClass('hidden');
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* only hides the clone button
|
* only hides the clone button
|
||||||
*
|
*
|
||||||
|
@ -3140,6 +3260,18 @@ jQuery.PrivateBin = (function($, sjcl, Base64, RawDeflate) {
|
||||||
return $customAttachment;
|
return $customAttachment;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set a function to call when the retry button is clicked.
|
||||||
|
*
|
||||||
|
* @name TopNav.setRetryCallback
|
||||||
|
* @function
|
||||||
|
* @param {function} callback
|
||||||
|
*/
|
||||||
|
me.setRetryCallback = function(callback)
|
||||||
|
{
|
||||||
|
retryButtonCallback = callback;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* init navigation manager
|
* init navigation manager
|
||||||
*
|
*
|
||||||
|
@ -3165,6 +3297,7 @@ jQuery.PrivateBin = (function($, sjcl, Base64, RawDeflate) {
|
||||||
$password = $('#password');
|
$password = $('#password');
|
||||||
$passwordInput = $('#passwordinput');
|
$passwordInput = $('#passwordinput');
|
||||||
$rawTextButton = $('#rawtextbutton');
|
$rawTextButton = $('#rawtextbutton');
|
||||||
|
$retryButton = $('#retrybutton');
|
||||||
$sendButton = $('#sendbutton');
|
$sendButton = $('#sendbutton');
|
||||||
$qrCodeLink = $('#qrcodelink');
|
$qrCodeLink = $('#qrcodelink');
|
||||||
|
|
||||||
|
@ -3180,6 +3313,7 @@ jQuery.PrivateBin = (function($, sjcl, Base64, RawDeflate) {
|
||||||
$sendButton.click(PasteEncrypter.sendPaste);
|
$sendButton.click(PasteEncrypter.sendPaste);
|
||||||
$cloneButton.click(Controller.clonePaste);
|
$cloneButton.click(Controller.clonePaste);
|
||||||
$rawTextButton.click(rawText);
|
$rawTextButton.click(rawText);
|
||||||
|
$retryButton.click(clickRetryButton);
|
||||||
$fileRemoveButton.click(removeAttachment);
|
$fileRemoveButton.click(removeAttachment);
|
||||||
$qrCodeLink.click(displayQrCode);
|
$qrCodeLink.click(displayQrCode);
|
||||||
|
|
||||||
|
@ -3815,7 +3949,7 @@ jQuery.PrivateBin = (function($, sjcl, Base64, RawDeflate) {
|
||||||
var me = {};
|
var me = {};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* decrypt data or prompts for password in cvase of failure
|
* decrypt data or prompts for password in case of failure
|
||||||
*
|
*
|
||||||
* @name PasteDecrypter.decryptOrPromptPassword
|
* @name PasteDecrypter.decryptOrPromptPassword
|
||||||
* @private
|
* @private
|
||||||
|
@ -3833,18 +3967,23 @@ jQuery.PrivateBin = (function($, sjcl, Base64, RawDeflate) {
|
||||||
|
|
||||||
// if it fails, request password
|
// if it fails, request password
|
||||||
if (plaindata.length === 0 && password.length === 0) {
|
if (plaindata.length === 0 && password.length === 0) {
|
||||||
// try to get cached password first
|
// show prompt
|
||||||
password = Prompt.getPassword();
|
Prompt.requestPassword();
|
||||||
|
|
||||||
// if password is there, re-try
|
// if password is there instantly (legacy method), re-try encryption
|
||||||
if (password.length === 0) {
|
if (Prompt.getPassword().length !== 0) {
|
||||||
password = Prompt.requestPassword();
|
// recursive
|
||||||
|
// note: an infinite loop is prevented as the previous if
|
||||||
|
// clause checks whether a password is already set and ignores
|
||||||
|
// errors when a password has been passed
|
||||||
|
return decryptOrPromptPassword(key, password, cipherdata);
|
||||||
}
|
}
|
||||||
// recursive
|
|
||||||
// note: an infinite loop is prevented as the previous if
|
// if password could not be received yet, the new modal is used,
|
||||||
// clause checks whether a password is already set and ignores
|
// which uses asyncronous event-driven methods to get the password.
|
||||||
// errors when a password has been passed
|
// Thus, we cannot do anything yet, we need to wait for the user
|
||||||
return decryptOrPromptPassword.apply(key, password, cipherdata);
|
// input.
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// if all tries failed, we can only return an error
|
// if all tries failed, we can only return an error
|
||||||
|
@ -3858,7 +3997,7 @@ jQuery.PrivateBin = (function($, sjcl, Base64, RawDeflate) {
|
||||||
/**
|
/**
|
||||||
* decrypt the actual paste text
|
* decrypt the actual paste text
|
||||||
*
|
*
|
||||||
* @name PasteDecrypter.decryptOrPromptPassword
|
* @name PasteDecrypter.decryptPaste
|
||||||
* @private
|
* @private
|
||||||
* @function
|
* @function
|
||||||
* @param {object} paste - paste data in object form
|
* @param {object} paste - paste data in object form
|
||||||
|
@ -3981,7 +4120,9 @@ jQuery.PrivateBin = (function($, sjcl, Base64, RawDeflate) {
|
||||||
Alert.showLoading('Decrypting paste…', 'cloud-download');
|
Alert.showLoading('Decrypting paste…', 'cloud-download');
|
||||||
|
|
||||||
if (typeof paste === 'undefined') {
|
if (typeof paste === 'undefined') {
|
||||||
paste = $.parseJSON(Model.getCipherData());
|
// get cipher data and wait until it is available
|
||||||
|
Model.getPasteData(me.run);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var key = Model.getPasteKey(),
|
var key = Model.getPasteKey(),
|
||||||
|
@ -4006,10 +4147,11 @@ jQuery.PrivateBin = (function($, sjcl, Base64, RawDeflate) {
|
||||||
// ignore empty paste, as this is allowed when pasting attachments
|
// ignore empty paste, as this is allowed when pasting attachments
|
||||||
decryptPaste(paste, key, password, true);
|
decryptPaste(paste, key, password, true);
|
||||||
} else {
|
} else {
|
||||||
decryptPaste(paste, key, password);
|
if (decryptPaste(paste, key, password) === false) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// shows the remaining time (until) deletion
|
// shows the remaining time (until) deletion
|
||||||
PasteStatus.showRemainingTime(paste.meta);
|
PasteStatus.showRemainingTime(paste.meta);
|
||||||
|
|
||||||
|
@ -4025,7 +4167,15 @@ jQuery.PrivateBin = (function($, sjcl, Base64, RawDeflate) {
|
||||||
|
|
||||||
// log and show error
|
// log and show error
|
||||||
console.error(err);
|
console.error(err);
|
||||||
Alert.showError('Could not decrypt data (Wrong key?)');
|
Alert.showError('Could not decrypt data. Did you enter a wrong password? Retry with the button at the top.');
|
||||||
|
// reset password, so it can be re-entered and sow retry button
|
||||||
|
Prompt.reset();
|
||||||
|
TopNav.setRetryCallback(function () {
|
||||||
|
TopNav.hideRetryButton();
|
||||||
|
|
||||||
|
me.run(paste);
|
||||||
|
});
|
||||||
|
TopNav.showRetryButton();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -4090,6 +4240,18 @@ jQuery.PrivateBin = (function($, sjcl, Base64, RawDeflate) {
|
||||||
Alert.hideLoading();
|
Alert.hideLoading();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* shows how we much we love bots that execute JS ;)
|
||||||
|
*
|
||||||
|
* @name Controller.showBadBotMessage
|
||||||
|
* @function
|
||||||
|
*/
|
||||||
|
me.showBadBotMessage = function()
|
||||||
|
{
|
||||||
|
TopNav.hideAllButtons();
|
||||||
|
Alert.showError('I love you too, bot…');
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* shows the loaded paste
|
* shows the loaded paste
|
||||||
*
|
*
|
||||||
|
@ -4099,7 +4261,6 @@ jQuery.PrivateBin = (function($, sjcl, Base64, RawDeflate) {
|
||||||
me.showPaste = function()
|
me.showPaste = function()
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
Model.getPasteId();
|
|
||||||
Model.getPasteKey();
|
Model.getPasteKey();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
|
@ -4127,29 +4288,36 @@ jQuery.PrivateBin = (function($, sjcl, Base64, RawDeflate) {
|
||||||
// save window position to restore it later
|
// save window position to restore it later
|
||||||
var orgPosition = $(window).scrollTop();
|
var orgPosition = $(window).scrollTop();
|
||||||
|
|
||||||
Uploader.prepare();
|
Model.getPasteData(function (data) {
|
||||||
Uploader.setUrl(Helper.baseUri() + '?' + Model.getPasteId());
|
Uploader.prepare();
|
||||||
|
Uploader.setUrl(Helper.baseUri() + '?' + Model.getPasteId());
|
||||||
|
|
||||||
Uploader.setFailure(function (status, data) {
|
Uploader.setFailure(function (status, data) {
|
||||||
// revert loading status…
|
// revert loading status…
|
||||||
Alert.hideLoading();
|
Alert.hideLoading();
|
||||||
TopNav.showViewButtons();
|
TopNav.showViewButtons();
|
||||||
|
|
||||||
// show error message
|
// show error message
|
||||||
Alert.showError(
|
Alert.showError(
|
||||||
Uploader.parseUploadError(status, data, 'refresh display')
|
Uploader.parseUploadError(status, data, 'refresh display')
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
Uploader.setSuccess(function (status, data) {
|
Uploader.setSuccess(function (status, data) {
|
||||||
PasteDecrypter.run(data);
|
PasteDecrypter.run(data);
|
||||||
|
|
||||||
// restore position
|
// restore position
|
||||||
window.scrollTo(0, orgPosition);
|
window.scrollTo(0, orgPosition);
|
||||||
|
|
||||||
callback();
|
PasteDecrypter.run(data);
|
||||||
});
|
|
||||||
Uploader.run();
|
// NOTE: could create problems as callback may be called
|
||||||
};
|
// asyncronously if PasteDecrypter e.g. needs to wait for a
|
||||||
|
// password being entered
|
||||||
|
callback();
|
||||||
|
});
|
||||||
|
Uploader.run();
|
||||||
|
}, false); // this false is important as it circumvents the cache
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* clone the current paste
|
* clone the current paste
|
||||||
|
@ -4207,6 +4375,7 @@ jQuery.PrivateBin = (function($, sjcl, Base64, RawDeflate) {
|
||||||
* @function
|
* @function
|
||||||
* @param {string} pasteId
|
* @param {string} pasteId
|
||||||
* @param {string} deleteToken
|
* @param {string} deleteToken
|
||||||
|
* @deprecated not used anymore, de we still need it?
|
||||||
*/
|
*/
|
||||||
me.removePaste = function(pasteId, deleteToken) {
|
me.removePaste = function(pasteId, deleteToken) {
|
||||||
// unfortunately many web servers don't support DELETE (and PUT) out of the box
|
// unfortunately many web servers don't support DELETE (and PUT) out of the box
|
||||||
|
@ -4251,14 +4420,30 @@ jQuery.PrivateBin = (function($, sjcl, Base64, RawDeflate) {
|
||||||
UiHelper.init();
|
UiHelper.init();
|
||||||
Uploader.init();
|
Uploader.init();
|
||||||
|
|
||||||
// display an existing paste
|
// check whether existing paste needs to be shown
|
||||||
if (Model.hasCipherData()) {
|
try {
|
||||||
return me.showPaste();
|
Model.getPasteId();
|
||||||
|
} catch (e) {
|
||||||
|
// otherwise create a new paste
|
||||||
|
return me.newPaste();
|
||||||
}
|
}
|
||||||
|
|
||||||
// otherwise create a new paste
|
// if delete token is passed (i.e. paste has been deleted by this access)
|
||||||
me.newPaste();
|
// there is no more stuf we need to do
|
||||||
};
|
if (Model.hasDeleteToken()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// prevent bots from viewing a paste and potentially deleting data
|
||||||
|
// when burn-after-reading is set
|
||||||
|
// see https://github.com/elrido/ZeroBin/issues/11
|
||||||
|
if (Helper.isBadBot()) {
|
||||||
|
return me.showBadBotMessage();
|
||||||
|
}
|
||||||
|
|
||||||
|
// display an existing paste
|
||||||
|
return me.showPaste();
|
||||||
|
}
|
||||||
|
|
||||||
return me;
|
return me;
|
||||||
})(window, document);
|
})(window, document);
|
||||||
|
|
|
@ -70,48 +70,6 @@ describe('Model', function () {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('hasCipherData', function () {
|
|
||||||
before(function () {
|
|
||||||
$.PrivateBin.Model.reset();
|
|
||||||
cleanup();
|
|
||||||
});
|
|
||||||
|
|
||||||
jsc.property(
|
|
||||||
'checks if the element with id "cipherdata" contains any data',
|
|
||||||
'asciistring',
|
|
||||||
function (value) {
|
|
||||||
value = common.htmlEntities(value).trim();
|
|
||||||
$('body').html('<div id="cipherdata">' + value + '</div>');
|
|
||||||
$.PrivateBin.Model.init();
|
|
||||||
var result = $.PrivateBin.Model.hasCipherData();
|
|
||||||
$.PrivateBin.Model.reset();
|
|
||||||
return (value.length > 0) === result;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('getCipherData', function () {
|
|
||||||
before(function () {
|
|
||||||
$.PrivateBin.Model.reset();
|
|
||||||
cleanup();
|
|
||||||
});
|
|
||||||
|
|
||||||
jsc.property(
|
|
||||||
'returns the contents of the element with id "cipherdata"',
|
|
||||||
'asciistring',
|
|
||||||
function (value) {
|
|
||||||
value = common.htmlEntities(value).trim();
|
|
||||||
$('body').html('<div id="cipherdata">' + value + '</div>');
|
|
||||||
$.PrivateBin.Model.init();
|
|
||||||
var result = common.htmlEntities(
|
|
||||||
$.PrivateBin.Model.getCipherData()
|
|
||||||
);
|
|
||||||
$.PrivateBin.Model.reset();
|
|
||||||
return value === result;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('getPasteId', function () {
|
describe('getPasteId', function () {
|
||||||
this.timeout(30000);
|
this.timeout(30000);
|
||||||
before(function () {
|
before(function () {
|
||||||
|
|
|
@ -7,6 +7,7 @@ describe('Prompt', function () {
|
||||||
describe('requestPassword & getPassword', function () {
|
describe('requestPassword & getPassword', function () {
|
||||||
this.timeout(30000);
|
this.timeout(30000);
|
||||||
before(function () {
|
before(function () {
|
||||||
|
$.PrivateBin.Model.reset();
|
||||||
cleanup();
|
cleanup();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -15,7 +16,7 @@ describe('Prompt', function () {
|
||||||
'string',
|
'string',
|
||||||
function (password) {
|
function (password) {
|
||||||
password = password.replace(/\r+/g, '');
|
password = password.replace(/\r+/g, '');
|
||||||
var clean = jsdom('', {url: 'ftp://example.com/#0'});
|
var clean = jsdom('', {url: 'ftp://example.com/?0'});
|
||||||
$('body').html(
|
$('body').html(
|
||||||
'<div id="passwordmodal" class="modal fade" role="dialog">' +
|
'<div id="passwordmodal" class="modal fade" role="dialog">' +
|
||||||
'<div class="modal-dialog"><div class="modal-content">' +
|
'<div class="modal-dialog"><div class="modal-content">' +
|
||||||
|
@ -23,7 +24,7 @@ describe('Prompt', function () {
|
||||||
'<div class="form-group"><input id="passworddecrypt" ' +
|
'<div class="form-group"><input id="passworddecrypt" ' +
|
||||||
'type="password" class="form-control" placeholder="Enter ' +
|
'type="password" class="form-control" placeholder="Enter ' +
|
||||||
'password"></div><button type="submit">Decrypt</button>' +
|
'password"></div><button type="submit">Decrypt</button>' +
|
||||||
'</form></div></div></div></div><div id="cipherdata">{}</div>'
|
'</form></div></div></div></div>'
|
||||||
);
|
);
|
||||||
$.PrivateBin.Model.init();
|
$.PrivateBin.Model.init();
|
||||||
$.PrivateBin.Prompt.init();
|
$.PrivateBin.Prompt.init();
|
||||||
|
@ -31,6 +32,7 @@ describe('Prompt', function () {
|
||||||
$('#passworddecrypt').val(password);
|
$('#passworddecrypt').val(password);
|
||||||
$('#passwordform').submit();
|
$('#passwordform').submit();
|
||||||
var result = $.PrivateBin.Prompt.getPassword();
|
var result = $.PrivateBin.Prompt.getPassword();
|
||||||
|
$.PrivateBin.Model.reset();
|
||||||
clean();
|
clean();
|
||||||
return result === password;
|
return result === password;
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,7 +43,6 @@ class Configuration
|
||||||
'password' => true,
|
'password' => true,
|
||||||
'fileupload' => false,
|
'fileupload' => false,
|
||||||
'burnafterreadingselected' => false,
|
'burnafterreadingselected' => false,
|
||||||
'instantburnafterreading' => false,
|
|
||||||
'defaultformatter' => 'plaintext',
|
'defaultformatter' => 'plaintext',
|
||||||
'syntaxhighlightingtheme' => null,
|
'syntaxhighlightingtheme' => null,
|
||||||
'sizelimit' => 2097152,
|
'sizelimit' => 2097152,
|
||||||
|
@ -59,7 +58,6 @@ class Configuration
|
||||||
),
|
),
|
||||||
'expire' => array(
|
'expire' => array(
|
||||||
'default' => '1week',
|
'default' => '1week',
|
||||||
'clone' => true,
|
|
||||||
),
|
),
|
||||||
'expire_options' => array(
|
'expire_options' => array(
|
||||||
'5min' => 300,
|
'5min' => 300,
|
||||||
|
|
|
@ -49,7 +49,7 @@ class Paste extends AbstractModel
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if non-expired burn after reading paste needs to be deleted
|
// check if non-expired burn after reading paste needs to be deleted
|
||||||
if (property_exists($data->meta, 'burnafterreading') && $data->meta->burnafterreading && $this->_conf->getKey('instantburnafterreading')) {
|
if (property_exists($data->meta, 'burnafterreading') && $data->meta->burnafterreading) {
|
||||||
$this->delete();
|
$this->delete();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -72,6 +72,12 @@ class Paste extends AbstractModel
|
||||||
$data->comment_offset = 0;
|
$data->comment_offset = 0;
|
||||||
$data->{'@context'} = 'js/paste.jsonld';
|
$data->{'@context'} = 'js/paste.jsonld';
|
||||||
$this->_data = $data;
|
$this->_data = $data;
|
||||||
|
|
||||||
|
// If the paste was meant to be read only once, delete it.
|
||||||
|
if ($this->isBurnafterreading()) {
|
||||||
|
$this->delete();
|
||||||
|
}
|
||||||
|
|
||||||
return $this->_data;
|
return $this->_data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -163,7 +169,7 @@ class Paste extends AbstractModel
|
||||||
*
|
*
|
||||||
* The token is the hmac of the pastes ID signed with the server salt.
|
* The token is the hmac of the pastes ID signed with the server salt.
|
||||||
* The paste can be deleted by calling:
|
* The paste can be deleted by calling:
|
||||||
* http://example.com/privatebin/?pasteid=<pasteid>&deletetoken=<deletetoken>
|
* https://example.com/privatebin/?pasteid=<pasteid>&deletetoken=<deletetoken>
|
||||||
*
|
*
|
||||||
* @access public
|
* @access public
|
||||||
* @return string
|
* @return string
|
||||||
|
|
|
@ -52,22 +52,6 @@ class PrivateBin
|
||||||
*/
|
*/
|
||||||
private $_conf;
|
private $_conf;
|
||||||
|
|
||||||
/**
|
|
||||||
* data
|
|
||||||
*
|
|
||||||
* @access private
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
private $_data = '';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* does the paste expire
|
|
||||||
*
|
|
||||||
* @access private
|
|
||||||
* @var bool
|
|
||||||
*/
|
|
||||||
private $_doesExpire = false;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* error message
|
* error message
|
||||||
*
|
*
|
||||||
|
@ -327,10 +311,10 @@ class PrivateBin
|
||||||
// deleted if it has already expired
|
// deleted if it has already expired
|
||||||
$burnafterreading = $paste->isBurnafterreading();
|
$burnafterreading = $paste->isBurnafterreading();
|
||||||
if (
|
if (
|
||||||
($burnafterreading && $deletetoken == 'burnafterreading') ||
|
($burnafterreading && $deletetoken == 'burnafterreading') || // either we burn-after it has been read //@TODO: not needed anymore now?
|
||||||
Filter::slowEquals($deletetoken, $paste->getDeleteToken())
|
Filter::slowEquals($deletetoken, $paste->getDeleteToken()) // or we manually delete it with this secret token
|
||||||
) {
|
) {
|
||||||
// Paste exists and deletion token is valid: Delete the paste.
|
// Paste exists and deletion token (if required) is valid: Delete the paste.
|
||||||
$paste->delete();
|
$paste->delete();
|
||||||
$this->_status = 'Paste was properly deleted.';
|
$this->_status = 'Paste was properly deleted.';
|
||||||
} else {
|
} else {
|
||||||
|
@ -356,35 +340,30 @@ class PrivateBin
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Read an existing paste or comment
|
* Read an existing paste or comment, only allowed via a JSON API call
|
||||||
*
|
*
|
||||||
* @access private
|
* @access private
|
||||||
* @param string $dataid
|
* @param string $dataid
|
||||||
*/
|
*/
|
||||||
private function _read($dataid)
|
private function _read($dataid)
|
||||||
{
|
{
|
||||||
|
if (!$this->_request->isJsonApiCall()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$paste = $this->_model->getPaste($dataid);
|
$paste = $this->_model->getPaste($dataid);
|
||||||
if ($paste->exists()) {
|
if ($paste->exists()) {
|
||||||
$data = $paste->get();
|
$data = $paste->get();
|
||||||
$this->_doesExpire = property_exists($data, 'meta') && property_exists($data->meta, 'expire_date');
|
|
||||||
if (property_exists($data->meta, 'salt')) {
|
if (property_exists($data->meta, 'salt')) {
|
||||||
unset($data->meta->salt);
|
unset($data->meta->salt);
|
||||||
}
|
}
|
||||||
$this->_data = json_encode($data);
|
$this->_return_message(0, $dataid, (array) $data);
|
||||||
} else {
|
} else {
|
||||||
$this->_error = self::GENERIC_ERROR;
|
$this->_return_message(1, self::GENERIC_ERROR);
|
||||||
}
|
}
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
$this->_error = $e->getMessage();
|
$this->_return_message(1, $e->getMessage());
|
||||||
}
|
|
||||||
|
|
||||||
if ($this->_request->isJsonApiCall()) {
|
|
||||||
if (strlen($this->_error)) {
|
|
||||||
$this->_return_message(1, $this->_error);
|
|
||||||
} else {
|
|
||||||
$this->_return_message(0, $dataid, json_decode($this->_data, true));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -425,7 +404,6 @@ class PrivateBin
|
||||||
|
|
||||||
$page = new View;
|
$page = new View;
|
||||||
$page->assign('NAME', $this->_conf->getKey('name'));
|
$page->assign('NAME', $this->_conf->getKey('name'));
|
||||||
$page->assign('CIPHERDATA', $this->_data);
|
|
||||||
$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);
|
||||||
|
@ -445,7 +423,6 @@ class PrivateBin
|
||||||
$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->getKey('default', 'expire'));
|
$page->assign('EXPIREDEFAULT', $this->_conf->getKey('default', 'expire'));
|
||||||
$page->assign('EXPIRECLONE', !$this->_doesExpire || ($this->_doesExpire && $this->_conf->getKey('clone', 'expire')));
|
|
||||||
$page->assign('URLSHORTENER', $this->_conf->getKey('urlshortener'));
|
$page->assign('URLSHORTENER', $this->_conf->getKey('urlshortener'));
|
||||||
$page->assign('QRCODE', $this->_conf->getKey('qrcode'));
|
$page->assign('QRCODE', $this->_conf->getKey('qrcode'));
|
||||||
$page->draw($this->_conf->getKey('template'));
|
$page->draw($this->_conf->getKey('template'));
|
||||||
|
|
|
@ -5,5 +5,3 @@ root = false
|
||||||
# special format for PHP templates
|
# special format for PHP templates
|
||||||
[*.php]
|
[*.php]
|
||||||
indent_style = tab
|
indent_style = tab
|
||||||
indent_size = 4
|
|
||||||
|
|
||||||
|
|
|
@ -75,7 +75,7 @@ if ($MARKDOWN):
|
||||||
<?php
|
<?php
|
||||||
endif;
|
endif;
|
||||||
?>
|
?>
|
||||||
<script type="text/javascript" src="js/privatebin.js?<?php echo rawurlencode($VERSION); ?>" integrity="sha512-Q3ZXxdH/2epUyO1qorIGVt2JLRZg7QU5YYeBe1evtjOGuHQPUr3zppGtncyNLmaYXbac09NJXeANiy7WeU2NmA==" crossorigin="anonymous"></script>
|
<script type="text/javascript" src="js/privatebin.js?<?php echo rawurlencode($VERSION); ?>" integrity="sha512-P+tLS0U2r7om1G6T0qLVMntNKoQnBvd3qcHGTgyOfHnj5qkyRWdMppa5q5hdQrxjGevb4hBnLsQKrLMyyRpsiw==" crossorigin="anonymous"></script>
|
||||||
<!--[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]-->
|
||||||
|
@ -147,6 +147,11 @@ endif;
|
||||||
<?php echo I18n::_('Loading…'), PHP_EOL; ?>
|
<?php echo I18n::_('Loading…'), PHP_EOL; ?>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
|
<button id="retrybutton" type="button" class="reloadlink hidden btn btn-<?php echo $isDark ? 'warning' : 'primary'; ?> navbar-btn">
|
||||||
|
<span class="glyphicon glyphicon-repeat" aria-hidden="true"></span> <?php echo I18n::_('Retry'), PHP_EOL; ?>
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
<?php
|
<?php
|
||||||
if ($isPage):
|
if ($isPage):
|
||||||
?>
|
?>
|
||||||
|
@ -159,15 +164,9 @@ else:
|
||||||
endif;
|
endif;
|
||||||
?>
|
?>
|
||||||
</button>
|
</button>
|
||||||
<?php
|
|
||||||
if ($EXPIRECLONE):
|
|
||||||
?>
|
|
||||||
<button id="clonebutton" type="button" class="hidden btn btn-<?php echo $isDark ? 'warning' : 'default'; ?> navbar-btn">
|
<button id="clonebutton" type="button" class="hidden btn btn-<?php echo $isDark ? 'warning' : 'default'; ?> navbar-btn">
|
||||||
<span class="glyphicon glyphicon-duplicate" aria-hidden="true"></span> <?php echo I18n::_('Clone'), PHP_EOL; ?>
|
<span class="glyphicon glyphicon-duplicate" aria-hidden="true"></span> <?php echo I18n::_('Clone'), PHP_EOL; ?>
|
||||||
</button>
|
</button>
|
||||||
<?php
|
|
||||||
endif;
|
|
||||||
?>
|
|
||||||
<button id="rawtextbutton" type="button" class="hidden btn btn-<?php echo $isDark ? 'warning' : 'default'; ?> navbar-btn">
|
<button id="rawtextbutton" type="button" class="hidden btn btn-<?php echo $isDark ? 'warning' : 'default'; ?> navbar-btn">
|
||||||
<span class="glyphicon glyphicon-text-background" aria-hidden="true"></span> <?php echo I18n::_('Raw text'), PHP_EOL; ?>
|
<span class="glyphicon glyphicon-text-background" aria-hidden="true"></span> <?php echo I18n::_('Raw text'), PHP_EOL; ?>
|
||||||
</button>
|
</button>
|
||||||
|
@ -519,19 +518,18 @@ endif;
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
</main>
|
</main>
|
||||||
<div id="serverdata" class="hidden" aria-hidden="true">
|
|
||||||
<div id="cipherdata"><?php echo htmlspecialchars($CIPHERDATA, ENT_NOQUOTES); ?></div>
|
|
||||||
<?php
|
<?php
|
||||||
if ($DISCUSSION):
|
if ($DISCUSSION):
|
||||||
?>
|
?>
|
||||||
|
<div id="serverdata" class="hidden" aria-hidden="true">
|
||||||
<div id="templates">
|
<div id="templates">
|
||||||
<article id="commenttemplate" class="comment"><div class="commentmeta"><span class="nickname">name</span><span class="commentdate">0000-00-00</span></div><div class="commentdata">c</div><button class="btn btn-default btn-sm"><?php echo I18n::_('Reply'); ?></button></article>
|
<article id="commenttemplate" class="comment"><div class="commentmeta"><span class="nickname">name</span><span class="commentdate">0000-00-00</span></div><div class="commentdata">c</div><button class="btn btn-default btn-sm"><?php echo I18n::_('Reply'); ?></button></article>
|
||||||
<p id="commenttailtemplate" class="comment"><button class="btn btn-default btn-sm"><?php echo I18n::_('Add comment'); ?></button></p>
|
<p id="commenttailtemplate" class="comment"><button class="btn btn-default btn-sm"><?php echo I18n::_('Add comment'); ?></button></p>
|
||||||
<div id="replytemplate" class="reply hidden"><input type="text" id="nickname" class="form-control" title="<?php echo I18n::_('Optional nickname…'); ?>" placeholder="<?php echo I18n::_('Optional nickname…'); ?>" /><textarea id="replymessage" class="replymessage form-control" cols="80" rows="7"></textarea><br /><div id="replystatus" role="alert" class="statusmessage hidden alert"><span class="glyphicon" aria-hidden="true"></span> </div><button id="replybutton" class="btn btn-default btn-sm"><?php echo I18n::_('Post comment'); ?></button></div>
|
<div id="replytemplate" class="reply hidden"><input type="text" id="nickname" class="form-control" title="<?php echo I18n::_('Optional nickname…'); ?>" placeholder="<?php echo I18n::_('Optional nickname…'); ?>" /><textarea id="replymessage" class="replymessage form-control" cols="80" rows="7"></textarea><br /><div id="replystatus" role="alert" class="statusmessage hidden alert"><span class="glyphicon" aria-hidden="true"></span> </div><button id="replybutton" class="btn btn-default btn-sm"><?php echo I18n::_('Post comment'); ?></button></div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
<?php
|
<?php
|
||||||
endif;
|
endif;
|
||||||
?>
|
?>
|
||||||
</div>
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
14
tpl/page.php
14
tpl/page.php
|
@ -54,7 +54,7 @@ if ($QRCODE):
|
||||||
<?php
|
<?php
|
||||||
endif;
|
endif;
|
||||||
?>
|
?>
|
||||||
<script type="text/javascript" src="js/privatebin.js?<?php echo rawurlencode($VERSION); ?>" integrity="sha512-Q3ZXxdH/2epUyO1qorIGVt2JLRZg7QU5YYeBe1evtjOGuHQPUr3zppGtncyNLmaYXbac09NJXeANiy7WeU2NmA==" crossorigin="anonymous"></script>
|
<script type="text/javascript" src="js/privatebin.js?<?php echo rawurlencode($VERSION); ?>" integrity="sha512-P+tLS0U2r7om1G6T0qLVMntNKoQnBvd3qcHGTgyOfHnj5qkyRWdMppa5q5hdQrxjGevb4hBnLsQKrLMyyRpsiw==" crossorigin="anonymous"></script>
|
||||||
<!--[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]-->
|
||||||
|
@ -96,14 +96,9 @@ endif;
|
||||||
<div id="errormessage" class="hidden"><?php echo htmlspecialchars($ERROR); ?></div>
|
<div id="errormessage" class="hidden"><?php echo htmlspecialchars($ERROR); ?></div>
|
||||||
<div id="toolbar">
|
<div id="toolbar">
|
||||||
<button id="newbutton" class="reloadlink hidden"><img src="img/icon_new.png" width="11" height="15" alt="" /><?php echo I18n::_('New'); ?></button>
|
<button id="newbutton" class="reloadlink hidden"><img src="img/icon_new.png" width="11" height="15" alt="" /><?php echo I18n::_('New'); ?></button>
|
||||||
|
<button id="retrybutton" class="reloadlink hidden"><?php echo I18n::_('Retry'), PHP_EOL; ?></button>
|
||||||
<button id="sendbutton" class="hidden"><img src="img/icon_send.png" width="18" height="15" alt="" /><?php echo I18n::_('Send'); ?></button>
|
<button id="sendbutton" class="hidden"><img src="img/icon_send.png" width="18" height="15" alt="" /><?php echo I18n::_('Send'); ?></button>
|
||||||
<?php
|
|
||||||
if ($EXPIRECLONE):
|
|
||||||
?>
|
|
||||||
<button id="clonebutton" class="hidden"><img src="img/icon_clone.png" width="15" height="17" alt="" /><?php echo I18n::_('Clone'); ?></button>
|
<button id="clonebutton" class="hidden"><img src="img/icon_clone.png" width="15" height="17" alt="" /><?php echo I18n::_('Clone'); ?></button>
|
||||||
<?php
|
|
||||||
endif;
|
|
||||||
?>
|
|
||||||
<button id="rawtextbutton" class="hidden"><img src="img/icon_raw.png" width="15" height="15" alt="" /><?php echo I18n::_('Raw text'); ?></button>
|
<button id="rawtextbutton" class="hidden"><img src="img/icon_raw.png" width="15" height="15" alt="" /><?php echo I18n::_('Raw text'); ?></button>
|
||||||
<?php
|
<?php
|
||||||
if ($QRCODE):
|
if ($QRCODE):
|
||||||
|
@ -246,20 +241,19 @@ endif;
|
||||||
<div id="commentcontainer"></div>
|
<div id="commentcontainer"></div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
<div id="serverdata" class="hidden" aria-hidden="true">
|
|
||||||
<div id="cipherdata" class="hidden"><?php echo htmlspecialchars($CIPHERDATA, ENT_NOQUOTES); ?></div>
|
|
||||||
<?php
|
<?php
|
||||||
if ($DISCUSSION):
|
if ($DISCUSSION):
|
||||||
?>
|
?>
|
||||||
|
<div id="serverdata" class="hidden" aria-hidden="true">
|
||||||
<div id="templates">
|
<div id="templates">
|
||||||
<article id="commenttemplate" class="comment"><div class="commentmeta"><span class="nickname">name</span><span class="commentdate">0000-00-00</span></div><div class="commentdata">c</div><button class="btn btn-default btn-sm"><?php echo I18n::_('Reply'); ?></button></article>
|
<article id="commenttemplate" class="comment"><div class="commentmeta"><span class="nickname">name</span><span class="commentdate">0000-00-00</span></div><div class="commentdata">c</div><button class="btn btn-default btn-sm"><?php echo I18n::_('Reply'); ?></button></article>
|
||||||
<div id="commenttailtemplate" class="comment"><button class="btn btn-default btn-sm"><?php echo I18n::_('Add comment'); ?></button></div>
|
<div id="commenttailtemplate" class="comment"><button class="btn btn-default btn-sm"><?php echo I18n::_('Add comment'); ?></button></div>
|
||||||
<div id="replytemplate" class="reply hidden"><input type="text" id="nickname" class="form-control" title="<?php echo I18n::_('Optional nickname…'); ?>" placeholder="<?php echo I18n::_('Optional nickname…'); ?>" /><textarea id="replymessage" class="replymessage form-control" cols="80" rows="7"></textarea><br /><div id="replystatus" role="alert" class="statusmessage hidden alert"><span class="glyphicon" aria-hidden="true"></span> </div><button id="replybutton" class="btn btn-default btn-sm"><?php echo I18n::_('Post comment'); ?></button></div>
|
<div id="replytemplate" class="reply hidden"><input type="text" id="nickname" class="form-control" title="<?php echo I18n::_('Optional nickname…'); ?>" placeholder="<?php echo I18n::_('Optional nickname…'); ?>" /><textarea id="replymessage" class="replymessage form-control" cols="80" rows="7"></textarea><br /><div id="replystatus" role="alert" class="statusmessage hidden alert"><span class="glyphicon" aria-hidden="true"></span> </div><button id="replybutton" class="btn btn-default btn-sm"><?php echo I18n::_('Post comment'); ?></button></div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
<?php
|
<?php
|
||||||
endif;
|
endif;
|
||||||
?>
|
?>
|
||||||
</div>
|
|
||||||
<section class="container">
|
<section class="container">
|
||||||
<div id="noscript" role="alert" class="nonworking alert alert-info noscript-hide"><span class="glyphicon glyphicon-exclamation-sign" aria-hidden="true">
|
<div id="noscript" role="alert" class="nonworking alert alert-info noscript-hide"><span class="glyphicon glyphicon-exclamation-sign" aria-hidden="true">
|
||||||
<span> <?php echo I18n::_('Loading…'); ?></span><br>
|
<span> <?php echo I18n::_('Loading…'); ?></span><br>
|
||||||
|
|
|
@ -674,41 +674,20 @@ class PrivateBinTest extends PHPUnit_Framework_TestCase
|
||||||
$this->assertTrue($this->_model->existsComment(Helper::getPasteId(), Helper::getPasteId(), Helper::getCommentId()), 'paste exists after posting data');
|
$this->assertTrue($this->_model->existsComment(Helper::getPasteId(), Helper::getPasteId(), Helper::getCommentId()), 'paste exists after posting data');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @runInSeparateProcess
|
|
||||||
*/
|
|
||||||
public function testRead()
|
|
||||||
{
|
|
||||||
$this->_model->create(Helper::getPasteId(), Helper::getPaste());
|
|
||||||
$_SERVER['QUERY_STRING'] = Helper::getPasteId();
|
|
||||||
ob_start();
|
|
||||||
new PrivateBin;
|
|
||||||
$content = ob_get_contents();
|
|
||||||
ob_end_clean();
|
|
||||||
$this->assertRegExp(
|
|
||||||
'#<div id="cipherdata"[^>]*>' .
|
|
||||||
preg_quote(htmlspecialchars(Helper::getPasteAsJson(), ENT_NOQUOTES)) .
|
|
||||||
'</div>#',
|
|
||||||
$content,
|
|
||||||
'outputs data correctly'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @runInSeparateProcess
|
* @runInSeparateProcess
|
||||||
*/
|
*/
|
||||||
public function testReadInvalidId()
|
public function testReadInvalidId()
|
||||||
{
|
{
|
||||||
$_SERVER['QUERY_STRING'] = 'foo';
|
$_SERVER['QUERY_STRING'] = 'foo';
|
||||||
|
$_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
|
||||||
ob_start();
|
ob_start();
|
||||||
new PrivateBin;
|
new PrivateBin;
|
||||||
$content = ob_get_contents();
|
$content = ob_get_contents();
|
||||||
ob_end_clean();
|
ob_end_clean();
|
||||||
$this->assertRegExp(
|
$response = json_decode($content, true);
|
||||||
'#<div[^>]*id="errormessage"[^>]*>.*Invalid paste ID\.#s',
|
$this->assertEquals(1, $response['status'], 'outputs error status');
|
||||||
$content,
|
$this->assertEquals('Invalid paste ID.', $response['message'], 'outputs error message');
|
||||||
'outputs error correctly'
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -716,16 +695,15 @@ class PrivateBinTest extends PHPUnit_Framework_TestCase
|
||||||
*/
|
*/
|
||||||
public function testReadNonexisting()
|
public function testReadNonexisting()
|
||||||
{
|
{
|
||||||
$_SERVER['QUERY_STRING'] = Helper::getPasteId();
|
$_SERVER['QUERY_STRING'] = Helper::getPasteId();
|
||||||
|
$_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
|
||||||
ob_start();
|
ob_start();
|
||||||
new PrivateBin;
|
new PrivateBin;
|
||||||
$content = ob_get_contents();
|
$content = ob_get_contents();
|
||||||
ob_end_clean();
|
ob_end_clean();
|
||||||
$this->assertRegExp(
|
$response = json_decode($content, true);
|
||||||
'#<div[^>]*id="errormessage"[^>]*>.*Paste does not exist, has expired or has been deleted\.#s',
|
$this->assertEquals(1, $response['status'], 'outputs error status');
|
||||||
$content,
|
$this->assertEquals('Paste does not exist, has expired or has been deleted.', $response['message'], 'outputs error message');
|
||||||
'outputs error correctly'
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -735,16 +713,15 @@ class PrivateBinTest extends PHPUnit_Framework_TestCase
|
||||||
{
|
{
|
||||||
$expiredPaste = Helper::getPaste(array('expire_date' => 1344803344));
|
$expiredPaste = Helper::getPaste(array('expire_date' => 1344803344));
|
||||||
$this->_model->create(Helper::getPasteId(), $expiredPaste);
|
$this->_model->create(Helper::getPasteId(), $expiredPaste);
|
||||||
$_SERVER['QUERY_STRING'] = Helper::getPasteId();
|
$_SERVER['QUERY_STRING'] = Helper::getPasteId();
|
||||||
|
$_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
|
||||||
ob_start();
|
ob_start();
|
||||||
new PrivateBin;
|
new PrivateBin;
|
||||||
$content = ob_get_contents();
|
$content = ob_get_contents();
|
||||||
ob_end_clean();
|
ob_end_clean();
|
||||||
$this->assertRegExp(
|
$response = json_decode($content, true);
|
||||||
'#<div[^>]*id="errormessage"[^>]*>.*Paste does not exist, has expired or has been deleted\.#s',
|
$this->assertEquals(1, $response['status'], 'outputs error status');
|
||||||
$content,
|
$this->assertEquals('Paste does not exist, has expired or has been deleted.', $response['message'], 'outputs error message');
|
||||||
'outputs error correctly'
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -752,51 +729,26 @@ class PrivateBinTest extends PHPUnit_Framework_TestCase
|
||||||
*/
|
*/
|
||||||
public function testReadBurn()
|
public function testReadBurn()
|
||||||
{
|
{
|
||||||
$burnPaste = Helper::getPaste(array('burnafterreading' => true));
|
$paste = Helper::getPaste(array('burnafterreading' => true));
|
||||||
$this->_model->create(Helper::getPasteId(), $burnPaste);
|
$this->_model->create(Helper::getPasteId(), $paste);
|
||||||
$_SERVER['QUERY_STRING'] = Helper::getPasteId();
|
$_SERVER['QUERY_STRING'] = Helper::getPasteId();
|
||||||
|
$_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
|
||||||
ob_start();
|
ob_start();
|
||||||
new PrivateBin;
|
new PrivateBin;
|
||||||
$content = ob_get_contents();
|
$content = ob_get_contents();
|
||||||
ob_end_clean();
|
ob_end_clean();
|
||||||
unset($burnPaste['meta']['salt']);
|
$response = json_decode($content, true);
|
||||||
$this->assertRegExp(
|
$this->assertEquals(0, $response['status'], 'outputs success status');
|
||||||
'#<div id="cipherdata"[^>]*>' .
|
$this->assertEquals(Helper::getPasteId(), $response['id'], 'outputs data correctly');
|
||||||
preg_quote(htmlspecialchars(Helper::getPasteAsJson($burnPaste['meta']), ENT_NOQUOTES)) .
|
$this->assertStringEndsWith('?' . $response['id'], $response['url'], 'returned URL points to new paste');
|
||||||
'</div>#',
|
$this->assertEquals($paste['data'], $response['data'], 'outputs data correctly');
|
||||||
$content,
|
$this->assertEquals($paste['meta']['formatter'], $response['meta']['formatter'], 'outputs format correctly');
|
||||||
'outputs data correctly'
|
$this->assertEquals($paste['meta']['postdate'], $response['meta']['postdate'], 'outputs postdate correctly');
|
||||||
);
|
$this->assertEquals($paste['meta']['opendiscussion'], $response['meta']['opendiscussion'], 'outputs opendiscussion correctly');
|
||||||
// by default it will be deleted after encryption by the JS
|
$this->assertEquals(1, $response['meta']['burnafterreading'], 'outputs burnafterreading correctly');
|
||||||
$this->assertTrue($this->_model->exists(Helper::getPasteId()), 'paste exists after reading');
|
$this->assertEquals(0, $response['comment_count'], 'outputs comment_count correctly');
|
||||||
}
|
$this->assertEquals(0, $response['comment_offset'], 'outputs comment_offset correctly');
|
||||||
|
// by default it will be deleted instantly after it is read
|
||||||
/**
|
|
||||||
* @runInSeparateProcess
|
|
||||||
*/
|
|
||||||
public function testReadInstantBurn()
|
|
||||||
{
|
|
||||||
$this->reset();
|
|
||||||
$options = parse_ini_file(CONF, true);
|
|
||||||
$options['main']['instantburnafterreading'] = 1;
|
|
||||||
Helper::confBackup();
|
|
||||||
Helper::createIniFile(CONF, $options);
|
|
||||||
$burnPaste = Helper::getPaste(array('burnafterreading' => true));
|
|
||||||
$this->_model->create(Helper::getPasteId(), $burnPaste);
|
|
||||||
$_SERVER['QUERY_STRING'] = Helper::getPasteId();
|
|
||||||
ob_start();
|
|
||||||
new PrivateBin;
|
|
||||||
$content = ob_get_contents();
|
|
||||||
ob_end_clean();
|
|
||||||
unset($burnPaste['meta']['salt']);
|
|
||||||
$this->assertRegExp(
|
|
||||||
'#<div id="cipherdata"[^>]*>' .
|
|
||||||
preg_quote(htmlspecialchars(Helper::getPasteAsJson($burnPaste['meta']), ENT_NOQUOTES)) .
|
|
||||||
'</div>#',
|
|
||||||
$content,
|
|
||||||
'outputs data correctly'
|
|
||||||
);
|
|
||||||
// in this case the changed configuration deletes it instantly
|
|
||||||
$this->assertFalse($this->_model->exists(Helper::getPasteId()), 'paste exists after reading');
|
$this->assertFalse($this->_model->exists(Helper::getPasteId()), 'paste exists after reading');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -828,8 +780,15 @@ class PrivateBinTest extends PHPUnit_Framework_TestCase
|
||||||
/**
|
/**
|
||||||
* @runInSeparateProcess
|
* @runInSeparateProcess
|
||||||
*/
|
*/
|
||||||
public function testReadInvalidJson()
|
public function testReadOldSyntax()
|
||||||
{
|
{
|
||||||
|
$paste = Helper::getPaste();
|
||||||
|
$paste['meta'] = array(
|
||||||
|
'syntaxcoloring' => true,
|
||||||
|
'postdate' => $paste['meta']['postdate'],
|
||||||
|
'opendiscussion' => $paste['meta']['opendiscussion'],
|
||||||
|
);
|
||||||
|
$this->_model->create(Helper::getPasteId(), $paste);
|
||||||
$_SERVER['QUERY_STRING'] = Helper::getPasteId();
|
$_SERVER['QUERY_STRING'] = Helper::getPasteId();
|
||||||
$_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
|
$_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
|
||||||
ob_start();
|
ob_start();
|
||||||
|
@ -837,59 +796,15 @@ class PrivateBinTest extends PHPUnit_Framework_TestCase
|
||||||
$content = ob_get_contents();
|
$content = ob_get_contents();
|
||||||
ob_end_clean();
|
ob_end_clean();
|
||||||
$response = json_decode($content, true);
|
$response = json_decode($content, true);
|
||||||
$this->assertEquals(1, $response['status'], 'outputs error status');
|
$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');
|
||||||
* @runInSeparateProcess
|
$this->assertEquals('syntaxhighlighting', $response['meta']['formatter'], 'outputs format correctly');
|
||||||
*/
|
$this->assertEquals($paste['meta']['postdate'], $response['meta']['postdate'], 'outputs postdate correctly');
|
||||||
public function testReadOldSyntax()
|
$this->assertEquals($paste['meta']['opendiscussion'], $response['meta']['opendiscussion'], 'outputs opendiscussion correctly');
|
||||||
{
|
$this->assertEquals(0, $response['comment_count'], 'outputs comment_count correctly');
|
||||||
$oldPaste = Helper::getPaste();
|
$this->assertEquals(0, $response['comment_offset'], 'outputs comment_offset correctly');
|
||||||
$meta = array(
|
|
||||||
'syntaxcoloring' => true,
|
|
||||||
'postdate' => $oldPaste['meta']['postdate'],
|
|
||||||
'opendiscussion' => $oldPaste['meta']['opendiscussion'],
|
|
||||||
);
|
|
||||||
$oldPaste['meta'] = $meta;
|
|
||||||
$this->_model->create(Helper::getPasteId(), $oldPaste);
|
|
||||||
$_SERVER['QUERY_STRING'] = Helper::getPasteId();
|
|
||||||
ob_start();
|
|
||||||
new PrivateBin;
|
|
||||||
$content = ob_get_contents();
|
|
||||||
ob_end_clean();
|
|
||||||
$meta['formatter'] = 'syntaxhighlighting';
|
|
||||||
$this->assertRegExp(
|
|
||||||
'#<div id="cipherdata"[^>]*>' .
|
|
||||||
preg_quote(htmlspecialchars(Helper::getPasteAsJson($meta), ENT_NOQUOTES)) .
|
|
||||||
'</div>#',
|
|
||||||
$content,
|
|
||||||
'outputs data correctly'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @runInSeparateProcess
|
|
||||||
*/
|
|
||||||
public function testReadOldFormat()
|
|
||||||
{
|
|
||||||
$oldPaste = Helper::getPaste();
|
|
||||||
unset($oldPaste['meta']['formatter']);
|
|
||||||
$this->_model->create(Helper::getPasteId(), $oldPaste);
|
|
||||||
$_SERVER['QUERY_STRING'] = Helper::getPasteId();
|
|
||||||
ob_start();
|
|
||||||
new PrivateBin;
|
|
||||||
$content = ob_get_contents();
|
|
||||||
ob_end_clean();
|
|
||||||
$oldPaste['meta']['formatter'] = 'plaintext';
|
|
||||||
unset($oldPaste['meta']['salt']);
|
|
||||||
$this->assertRegExp(
|
|
||||||
'#<div id="cipherdata"[^>]*>' .
|
|
||||||
preg_quote(htmlspecialchars(Helper::getPasteAsJson($oldPaste['meta']), ENT_NOQUOTES)) .
|
|
||||||
'</div>#',
|
|
||||||
$content,
|
|
||||||
'outputs data correctly'
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -34,7 +34,6 @@ class ViewTest extends PHPUnit_Framework_TestCase
|
||||||
/* Setup Routine */
|
/* Setup Routine */
|
||||||
$page = new View;
|
$page = new View;
|
||||||
$page->assign('NAME', 'PrivateBinTest');
|
$page->assign('NAME', 'PrivateBinTest');
|
||||||
$page->assign('CIPHERDATA', Helper::getPaste()['data']);
|
|
||||||
$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);
|
||||||
|
@ -54,7 +53,6 @@ class ViewTest 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);
|
|
||||||
$page->assign('URLSHORTENER', '');
|
$page->assign('URLSHORTENER', '');
|
||||||
$page->assign('QRCODE', true);
|
$page->assign('QRCODE', true);
|
||||||
|
|
||||||
|
@ -97,13 +95,6 @@ class ViewTest extends PHPUnit_Framework_TestCase
|
||||||
public function testTemplateRendersCorrectly()
|
public function testTemplateRendersCorrectly()
|
||||||
{
|
{
|
||||||
foreach ($this->_content as $template => $content) {
|
foreach ($this->_content as $template => $content) {
|
||||||
$this->assertRegExp(
|
|
||||||
'#<div[^>]+id="cipherdata"[^>]*>' .
|
|
||||||
preg_quote(htmlspecialchars(Helper::getPaste()['data'], ENT_NOQUOTES)) .
|
|
||||||
'</div>#',
|
|
||||||
$content,
|
|
||||||
$template . ': outputs data correctly'
|
|
||||||
);
|
|
||||||
$this->assertRegExp(
|
$this->assertRegExp(
|
||||||
'#<div[^>]+id="errormessage"[^>]*>.*' . self::$error . '#s',
|
'#<div[^>]+id="errormessage"[^>]*>.*' . self::$error . '#s',
|
||||||
$content,
|
$content,
|
||||||
|
|
Loading…
Reference in New Issue
Block a user