From 299e9d15fb88512767850a641145e49be039f08e Mon Sep 17 00:00:00 2001 From: Jermolene Date: Sun, 19 Jan 2014 18:43:02 +0000 Subject: [PATCH] Add support for importing encrypted TiddlyWiki documents --- boot/boot.css.tid | 4 ++ boot/boot.js | 37 ++++++++--- core/modules/wiki.js | 61 +++++++++++++++++-- .../tw5.com/tiddlers/Release 5.0.7beta.tid | 1 + 4 files changed, 91 insertions(+), 12 deletions(-) diff --git a/boot/boot.css.tid b/boot/boot.css.tid index 2d8718a96..5aa18410e 100644 --- a/boot/boot.css.tid +++ b/boot/boot.css.tid @@ -58,3 +58,7 @@ Error message and password prompt line-height: 20px; padding-bottom: 16px; } + +.tw-password-wrapper input { + width: 100%; +} diff --git a/boot/boot.js b/boot/boot.js index d9145c1bf..14060536a 100644 --- a/boot/boot.js +++ b/boot/boot.js @@ -448,11 +448,13 @@ Adds a new password prompt. Options are: submitText: text to use for submit button (defaults to "Login") serviceName: text of the human readable service name 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 */ $tw.utils.PasswordPrompt.prototype.createPrompt = function(options) { // Create and add the prompt to the DOM - var submitText = options.submitText || "Login", + var self = this, + submitText = options.submitText || "Login", dm = $tw.utils.domMaker, children = [dm("h1",{text: options.serviceName})]; if(!options.noUserName) { @@ -465,6 +467,19 @@ $tw.utils.PasswordPrompt.prototype.createPrompt = function(options) { attributes: {type: "password", name: "password", placeholder: "Password"}, "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",{ attributes: {type: "submit"}, text: submitText, @@ -492,12 +507,7 @@ $tw.utils.PasswordPrompt.prototype.createPrompt = function(options) { // Call the callback if(options.callback(data)) { // Remove the prompt if the callback returned true - var i = self.passwordPrompts.indexOf(promptInfo); - if(i !== -1) { - self.passwordPrompts.splice(i,1); - promptInfo.form.parentNode.removeChild(promptInfo.form); - self.setWrapperDisplay(); - } + self.removePrompt(promptInfo); } else { // Clear the password if the callback returned false $tw.utils.each(form.elements,function(element) { @@ -520,6 +530,15 @@ $tw.utils.PasswordPrompt.prototype.createPrompt = function(options) { 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 the password, and to encrypt/decrypt a block of text @@ -530,7 +549,9 @@ $tw.utils.Crypto = function() { callSjcl = function(method,inputText) { var outputText; try { - outputText = sjcl[method](password,inputText); + if(password) { + outputText = sjcl[method](password,inputText); + } } catch(ex) { console.log("Crypto error:" + ex); outputText = null; diff --git a/core/modules/wiki.js b/core/modules/wiki.js index ab8d01159..866b4dbda 100755 --- a/core/modules/wiki.js +++ b/core/modules/wiki.js @@ -1071,17 +1071,70 @@ exports.readFile = function(file,callback) { // Onload reader.onload = function(event) { // 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? if(isBinary) { // 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) { - tiddlerFields.text = event.target.result.substr(commaPos+1); + tiddlerFields.text = text.substr(commaPos+1); callback([tiddlerFields]); } } else { - callback(self.deserializeTiddlers(type,event.target.result,tiddlerFields)); + // Check whether this is an encrypted TiddlyWiki file + var encryptedStoreAreaStartMarker = "
",
+				encryptedStoreAreaStart = text.indexOf(encryptedStoreAreaStartMarker),
+				encryptedStoreAreaEnd = encryptedStoreAreaStart !== -1 ? text.indexOf("
",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 diff --git a/editions/tw5.com/tiddlers/Release 5.0.7beta.tid b/editions/tw5.com/tiddlers/Release 5.0.7beta.tid index 514767906..fc7197c51 100644 --- a/editions/tw5.com/tiddlers/Release 5.0.7beta.tid +++ b/editions/tw5.com/tiddlers/Release 5.0.7beta.tid @@ -16,6 +16,7 @@ type: text/vnd.tiddlywiki !! 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 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