mirror of
				https://github.com/Jermolene/TiddlyWiki5
				synced 2025-10-29 14:47:40 +00:00 
			
		
		
		
	Add support for importing encrypted TiddlyWiki documents
This commit is contained in:
		| @@ -58,3 +58,7 @@ Error message and password prompt | ||||
| 	line-height: 20px; | ||||
| 	padding-bottom: 16px; | ||||
| } | ||||
|  | ||||
| .tw-password-wrapper input { | ||||
| 	width: 100%; | ||||
| } | ||||
|   | ||||
							
								
								
									
										35
									
								
								boot/boot.js
									
									
									
									
									
								
							
							
						
						
									
										35
									
								
								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 { | ||||
| 				if(password) { | ||||
| 					outputText = sjcl[method](password,inputText); | ||||
| 				} | ||||
| 			} catch(ex) { | ||||
| 				console.log("Crypto error:" + ex); | ||||
| 				outputText = null;	 | ||||
|   | ||||
| @@ -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 = "<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 | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Jermolene
					Jermolene