1
0
mirror of https://github.com/Jermolene/TiddlyWiki5 synced 2024-11-30 05:19:57 +00:00

Add support for importing encrypted TiddlyWiki documents

This commit is contained in:
Jermolene 2014-01-19 18:43:02 +00:00
parent 2f4932fefc
commit 299e9d15fb
4 changed files with 91 additions and 12 deletions

View File

@ -58,3 +58,7 @@ Error message and password prompt
line-height: 20px; line-height: 20px;
padding-bottom: 16px; padding-bottom: 16px;
} }
.tw-password-wrapper input {
width: 100%;
}

View File

@ -448,11 +448,13 @@ Adds a new password prompt. Options are:
submitText: text to use for submit button (defaults to "Login") submitText: text to use for submit button (defaults to "Login")
serviceName: text of the human readable service name serviceName: text of the human readable service name
noUserName: set true to disable username prompt noUserName: set true to disable username prompt
canCancel: set true to enable a cancel button (callback called with null)
callback: function to be called on submission with parameter of object {username:,password:}. Callback must return `true` to remove the password prompt callback: function to be called on submission with parameter of object {username:,password:}. Callback must return `true` to remove the password prompt
*/ */
$tw.utils.PasswordPrompt.prototype.createPrompt = function(options) { $tw.utils.PasswordPrompt.prototype.createPrompt = function(options) {
// Create and add the prompt to the DOM // Create and add the prompt to the DOM
var submitText = options.submitText || "Login", var self = this,
submitText = options.submitText || "Login",
dm = $tw.utils.domMaker, dm = $tw.utils.domMaker,
children = [dm("h1",{text: options.serviceName})]; children = [dm("h1",{text: options.serviceName})];
if(!options.noUserName) { if(!options.noUserName) {
@ -465,6 +467,19 @@ $tw.utils.PasswordPrompt.prototype.createPrompt = function(options) {
attributes: {type: "password", name: "password", placeholder: "Password"}, attributes: {type: "password", name: "password", placeholder: "Password"},
"class": "input-small" "class": "input-small"
})); }));
if(options.canCancel) {
children.push(dm("button",{
text: "Cancel",
"class": "btn",
eventListeners: [{
name: "click",
handlerFunction: function(event) {
self.removePrompt(promptInfo);
options.callback(null);
}
}]
}));
}
children.push(dm("button",{ children.push(dm("button",{
attributes: {type: "submit"}, attributes: {type: "submit"},
text: submitText, text: submitText,
@ -492,12 +507,7 @@ $tw.utils.PasswordPrompt.prototype.createPrompt = function(options) {
// Call the callback // Call the callback
if(options.callback(data)) { if(options.callback(data)) {
// Remove the prompt if the callback returned true // Remove the prompt if the callback returned true
var i = self.passwordPrompts.indexOf(promptInfo); self.removePrompt(promptInfo);
if(i !== -1) {
self.passwordPrompts.splice(i,1);
promptInfo.form.parentNode.removeChild(promptInfo.form);
self.setWrapperDisplay();
}
} else { } else {
// Clear the password if the callback returned false // Clear the password if the callback returned false
$tw.utils.each(form.elements,function(element) { $tw.utils.each(form.elements,function(element) {
@ -520,6 +530,15 @@ $tw.utils.PasswordPrompt.prototype.createPrompt = function(options) {
this.setWrapperDisplay(); this.setWrapperDisplay();
}; };
$tw.utils.PasswordPrompt.prototype.removePrompt = function(promptInfo) {
var i = this.passwordPrompts.indexOf(promptInfo);
if(i !== -1) {
this.passwordPrompts.splice(i,1);
promptInfo.form.parentNode.removeChild(promptInfo.form);
this.setWrapperDisplay();
}
}
/* /*
Crypto helper object for encrypted content. It maintains the password text in a closure, and provides methods to change Crypto helper object for encrypted content. It maintains the password text in a closure, and provides methods to change
the password, and to encrypt/decrypt a block of text the password, and to encrypt/decrypt a block of text
@ -530,7 +549,9 @@ $tw.utils.Crypto = function() {
callSjcl = function(method,inputText) { callSjcl = function(method,inputText) {
var outputText; var outputText;
try { try {
if(password) {
outputText = sjcl[method](password,inputText); outputText = sjcl[method](password,inputText);
}
} catch(ex) { } catch(ex) {
console.log("Crypto error:" + ex); console.log("Crypto error:" + ex);
outputText = null; outputText = null;

View File

@ -1071,17 +1071,70 @@ exports.readFile = function(file,callback) {
// Onload // Onload
reader.onload = function(event) { reader.onload = function(event) {
// Deserialise the file contents // Deserialise the file contents
var tiddlerFields = {title: file.name || "Untitled", type: type}; var text = event.target.result,
tiddlerFields = {title: file.name || "Untitled", type: type};
// Are we binary? // Are we binary?
if(isBinary) { if(isBinary) {
// The base64 section starts after the first comma in the data URI // The base64 section starts after the first comma in the data URI
var commaPos = event.target.result.indexOf(","); var commaPos = text.indexOf(",");
if(commaPos !== -1) { if(commaPos !== -1) {
tiddlerFields.text = event.target.result.substr(commaPos+1); tiddlerFields.text = text.substr(commaPos+1);
callback([tiddlerFields]); callback([tiddlerFields]);
} }
} else { } else {
callback(self.deserializeTiddlers(type,event.target.result,tiddlerFields)); // Check whether this is an encrypted TiddlyWiki file
var encryptedStoreAreaStartMarker = "<pre id=\"encryptedStoreArea\" type=\"text/plain\" style=\"display:none;\">",
encryptedStoreAreaStart = text.indexOf(encryptedStoreAreaStartMarker),
encryptedStoreAreaEnd = encryptedStoreAreaStart !== -1 ? text.indexOf("</pre>",encryptedStoreAreaStart) : -1;
if(encryptedStoreAreaStart !== -1 && encryptedStoreAreaEnd !== -1) {
var encryptedJson = $tw.utils.htmlDecode(text.substring(encryptedStoreAreaStart + encryptedStoreAreaStartMarker.length,encryptedStoreAreaEnd-1)),
attemptDecryption = function() {
var decryptedText = $tw.crypto.decrypt(encryptedJson);
if(decryptedText) {
var json = JSON.parse(decryptedText),
tiddlers = [];
for(var title in json) {
tiddlers.push(json[title]);
}
return tiddlers;
} else {
return null;
}
};
// Try to decrypt with the current password
var tiddlers = attemptDecryption();
if(tiddlers) {
callback(tiddlers);
} else {
// Prompt for a new password and keep trying
$tw.passwordPrompt.createPrompt({
serviceName: "Enter a password to decrypt the imported TiddlyWiki",
noUserName: true,
canCancel: true,
submitText: "Decrypt",
callback: function(data) {
// Exit if the user cancelled
if(!data) {
return false;
}
// Attempt to decrypt the tiddlers
$tw.crypto.setPassword(data.password);
var tiddlers = attemptDecryption();
if(tiddlers) {
callback(tiddlers);
// Exit and remove the password prompt
return true;
} else {
// We didn't decrypt everything, so continue to prompt for password
return false;
}
}
});
}
} else {
// Try to deserialise any tiddlers in the file
callback(self.deserializeTiddlers(type,text,tiddlerFields));
}
} }
}; };
// Kick off the read // Kick off the read

View File

@ -16,6 +16,7 @@ type: text/vnd.tiddlywiki
!! Improvements !! Improvements
* Added support for importing encrypted TiddlyWiki documents
* Added the [[highlight.js|http://highlightjs.org/]] syntax highlighting plugin: http://tiddlywiki.com/highlightdemo.html (thanks to João Bolila, @jbolila on GitHub) * Added the [[highlight.js|http://highlightjs.org/]] syntax highlighting plugin: http://tiddlywiki.com/highlightdemo.html (thanks to João Bolila, @jbolila on GitHub)
* Added the first export option to the ''Tools'' tab of the [[control panel|$:/ControlPanel]] * Added the first export option to the ''Tools'' tab of the [[control panel|$:/ControlPanel]]
* [[Added|https://github.com/Jermolene/TiddlyWiki5/commit/ffcc215e8f8896be96093579abc5bcfb76335e66]] an ellipsis for [[advanced search|$:/AdvancedSearch]] next to the search box in the sidebar * [[Added|https://github.com/Jermolene/TiddlyWiki5/commit/ffcc215e8f8896be96093579abc5bcfb76335e66]] an ellipsis for [[advanced search|$:/AdvancedSearch]] next to the search box in the sidebar