|
@@ -42,11 +42,22 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) {
|
|
|
var Helper = (function () {
|
|
var Helper = (function () {
|
|
|
var me = {};
|
|
var me = {};
|
|
|
|
|
|
|
|
|
|
+ /**
|
|
|
|
|
+ * list of UserAgents (parts) known to belong to a bot
|
|
|
|
|
+ *
|
|
|
|
|
+ * @private
|
|
|
|
|
+ * @enum {Object}
|
|
|
|
|
+ * @readonly
|
|
|
|
|
+ */
|
|
|
|
|
+ var BadBotUA = [
|
|
|
|
|
+ 'Bot',
|
|
|
|
|
+ 'bot'
|
|
|
|
|
+ ];
|
|
|
|
|
+
|
|
|
/**
|
|
/**
|
|
|
* character to HTML entity lookup table
|
|
* character to HTML entity lookup table
|
|
|
*
|
|
*
|
|
|
* @see {@link https://github.com/janl/mustache.js/blob/master/mustache.js#L60}
|
|
* @see {@link https://github.com/janl/mustache.js/blob/master/mustache.js#L60}
|
|
|
- * @name Helper.entityMap
|
|
|
|
|
* @private
|
|
* @private
|
|
|
* @enum {Object}
|
|
* @enum {Object}
|
|
|
* @readonly
|
|
* @readonly
|
|
@@ -160,7 +171,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>
|
|
|
*
|
|
*
|
|
@@ -251,7 +262,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
|
|
@@ -295,6 +306,32 @@ 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() {
|
|
|
|
|
+ /*
|
|
|
|
|
+ if ($.inArray(navigator.userAgent, BadBotUA) >= 0) {
|
|
|
|
|
+ return true;
|
|
|
|
|
+ }
|
|
|
|
|
+ */
|
|
|
|
|
+
|
|
|
|
|
+ // 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;
|
|
|
})();
|
|
})();
|
|
|
|
|
|
|
@@ -689,7 +726,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;
|
|
@@ -721,32 +758,53 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
- * check if cipher data was supplied
|
|
|
|
|
|
|
+ * returns the paste data (inlduing the cipher data)
|
|
|
*
|
|
*
|
|
|
- * @name Model.getCipherData
|
|
|
|
|
- * @function
|
|
|
|
|
- * @return boolean
|
|
|
|
|
- */
|
|
|
|
|
- me.hasCipherData = function()
|
|
|
|
|
- {
|
|
|
|
|
- return (me.getCipherData().length > 0);
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- /**
|
|
|
|
|
- * returns the cipher data
|
|
|
|
|
- *
|
|
|
|
|
- * @name Model.getCipherData
|
|
|
|
|
|
|
+ * @name Model.getPasteData
|
|
|
* @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
|
|
@@ -819,7 +877,7 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) {
|
|
|
*/
|
|
*/
|
|
|
me.reset = function()
|
|
me.reset = function()
|
|
|
{
|
|
{
|
|
|
- $cipherData = $templates = id = symmetricKey = null;
|
|
|
|
|
|
|
+ pasteData = $templates = id = symmetricKey = null;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
@@ -832,7 +890,6 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) {
|
|
|
*/
|
|
*/
|
|
|
me.init = function()
|
|
me.init = function()
|
|
|
{
|
|
{
|
|
|
- $cipherData = $('#cipherdata');
|
|
|
|
|
$templates = $('#templates');
|
|
$templates = $('#templates');
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -1333,8 +1390,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
|
|
|
|
|
- Controller.removePaste(Model.getPasteId(), 'burnafterreading');
|
|
|
|
|
|
|
+ // the paste has been deleted when the JSOn with the ciohertext
|
|
|
|
|
+ // 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');
|
|
@@ -1462,7 +1519,7 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
- * getthe cached password
|
|
|
|
|
|
|
+ * get the cached password
|
|
|
*
|
|
*
|
|
|
* If you do not get a password with this function
|
|
* If you do not get a password with this function
|
|
|
* (returns an empty string), use requestPassword.
|
|
* (returns an empty string), use requestPassword.
|
|
@@ -3572,7 +3629,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
|
|
@@ -3590,18 +3647,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
|
|
|
|
|
- password = Prompt.getPassword();
|
|
|
|
|
-
|
|
|
|
|
- // if password is there, re-try
|
|
|
|
|
- if (password.length === 0) {
|
|
|
|
|
- password = Prompt.requestPassword();
|
|
|
|
|
|
|
+ // show prompt
|
|
|
|
|
+ Prompt.requestPassword();
|
|
|
|
|
+
|
|
|
|
|
+ // if password is there instantly (legacy method), re-try encryption
|
|
|
|
|
+ if (Prompt.getPassword().length !== 0) {
|
|
|
|
|
+ // 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
|
|
|
|
|
- // clause checks whether a password is already set and ignores
|
|
|
|
|
- // errors when a password has been passed
|
|
|
|
|
- return decryptOrPromptPassword.apply(key, password, cipherdata);
|
|
|
|
|
|
|
+
|
|
|
|
|
+ // if password could not be received yet, the new modal is used,
|
|
|
|
|
+ // which uses asyncronous event-driven methods to get the password.
|
|
|
|
|
+ // Thus, we cannot do anything yet, we need to wait for the user
|
|
|
|
|
+ // input.
|
|
|
|
|
+ return false;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// if all tries failed, we can only return an error
|
|
// if all tries failed, we can only return an error
|
|
@@ -3615,7 +3677,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
|
|
@@ -3627,7 +3689,7 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) {
|
|
|
*/
|
|
*/
|
|
|
function decryptPaste(paste, key, password, ignoreError)
|
|
function decryptPaste(paste, key, password, ignoreError)
|
|
|
{
|
|
{
|
|
|
- var plaintext
|
|
|
|
|
|
|
+ var plaintext;
|
|
|
if (ignoreError === true) {
|
|
if (ignoreError === true) {
|
|
|
plaintext = CryptTool.decipher(key, password, paste.data);
|
|
plaintext = CryptTool.decipher(key, password, paste.data);
|
|
|
} else {
|
|
} else {
|
|
@@ -3738,7 +3800,9 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) {
|
|
|
Alert.showLoading('Decrypting paste…', 0, 'cloud-download'); // @TODO icon maybe rotation-lock, but needs full Glyphicons
|
|
Alert.showLoading('Decrypting paste…', 0, 'cloud-download'); // @TODO icon maybe rotation-lock, but needs full Glyphicons
|
|
|
|
|
|
|
|
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(),
|
|
@@ -3761,10 +3825,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);
|
|
|
|
|
|
|
@@ -3844,6 +3909,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
|
|
|
*
|
|
*
|
|
@@ -3853,7 +3930,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);
|
|
@@ -3882,26 +3958,17 @@ 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();
|
|
|
|
|
- Uploader.setUrl(Helper.baseUri() + '?' + Model.getPasteId());
|
|
|
|
|
-
|
|
|
|
|
- Uploader.setFailure(function (status, data) {
|
|
|
|
|
- // revert loading status…
|
|
|
|
|
- Alert.hideLoading();
|
|
|
|
|
- TopNav.showViewButtons();
|
|
|
|
|
-
|
|
|
|
|
- // show error message
|
|
|
|
|
- Alert.showError(Uploader.parseUploadError(status, data, 'refresh display'));
|
|
|
|
|
- })
|
|
|
|
|
- Uploader.setSuccess(function (status, data) {
|
|
|
|
|
- PasteDecrypter.run(data);
|
|
|
|
|
-
|
|
|
|
|
|
|
+ Model.getPasteData(function (data) {
|
|
|
// restore position
|
|
// restore position
|
|
|
window.scrollTo(0, orgPosition);
|
|
window.scrollTo(0, orgPosition);
|
|
|
|
|
|
|
|
|
|
+ PasteDecrypter.run(data);
|
|
|
|
|
+
|
|
|
|
|
+ // NOTE: could create problems as callback may be called
|
|
|
|
|
+ // asyncronously if PasteDecrypter e.g. needs to wait for a
|
|
|
|
|
+ // password being entered
|
|
|
callback();
|
|
callback();
|
|
|
- })
|
|
|
|
|
- Uploader.run();
|
|
|
|
|
|
|
+ }, false); // this false is important as it circumvents the cache
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
@@ -3959,6 +4026,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
|
|
@@ -3968,7 +4036,7 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) {
|
|
|
Uploader.setUnencryptedData('deletetoken', deleteToken);
|
|
Uploader.setUnencryptedData('deletetoken', deleteToken);
|
|
|
|
|
|
|
|
Uploader.setFailure(function () {
|
|
Uploader.setFailure(function () {
|
|
|
- Controller.showError(I18n._('Could not delete the paste, it was not stored in burn after reading mode.'));
|
|
|
|
|
|
|
+ Alert.showError(I18n._('Could not delete the paste, it was not stored in burn after reading mode.'));
|
|
|
})
|
|
})
|
|
|
Uploader.run();
|
|
Uploader.run();
|
|
|
}
|
|
}
|
|
@@ -4000,13 +4068,23 @@ jQuery.PrivateBin = function($, sjcl, Base64, RawDeflate) {
|
|
|
UiHelper.init();
|
|
UiHelper.init();
|
|
|
Uploader.init();
|
|
Uploader.init();
|
|
|
|
|
|
|
|
- // display an existing paste
|
|
|
|
|
- if (Model.hasCipherData()) {
|
|
|
|
|
- return me.showPaste();
|
|
|
|
|
|
|
+ // check whether existing paste needs to be shown
|
|
|
|
|
+ try {
|
|
|
|
|
+ Model.getPasteId();
|
|
|
|
|
+ } catch (e) {
|
|
|
|
|
+ // otherwise create a new paste
|
|
|
|
|
+ return me.newPaste();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 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();
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- // otherwise create a new paste
|
|
|
|
|
- me.newPaste();
|
|
|
|
|
|
|
+ //display an existing paste
|
|
|
|
|
+ return me.showPaste();
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
return me;
|
|
return me;
|