1/** 2 * Handle interaction with the DokuWiki editor 3 */ 4class EditorHandling { 5 6 failcount = 0; 7 8 /** 9 * Register handlers 10 * 11 * @param {SubtleAES} aes 12 */ 13 constructor(aes) { 14 this.aes = aes; 15 16 jQuery(document).on('click', '#edbtn__save, #edbtn__preview', this.encryptAllSyntax.bind(this)); 17 jQuery(document).on('EncryptedPasswordsToggleEvent', this.decryptAllSyntax.bind(this)); 18 } 19 20 /** 21 * 22 * @param {Event} e 23 * @param {string} edid The editor's textarea ID 24 * @return {Promise<void>} 25 */ 26 async decryptAllSyntax(e, edid) { 27 const form = jQuery(`#${edid}`)[0].form; 28 // access the syntax in the editor 29 const prefix = form.prefix.value; 30 const suffix = form.suffix.value; 31 const text = form.wikitext.value; 32 33 // check if syntax contains encrypted passwords 34 const re = new RegExp('<decrypt>.*?(<\/decrypt>)'); 35 if ( 36 !prefix.match(re) && 37 !suffix.match(re) && 38 !text.match(re) 39 ) { 40 return; 41 } 42 43 // ask for passphrase 44 const passphrase = await GUI.prompt( 45 LANG.plugins.encryptedpasswords.enterKey, 46 LANG.plugins.encryptedpasswords.passphrase, 47 false 48 ); 49 if (passphrase === null || passphrase === '') return; 50 51 // replace 52 this.failcount = 0; 53 form.prefix.value = await this.decryptSyntax(prefix, passphrase); 54 form.suffix.value = await this.decryptSyntax(suffix, passphrase); 55 form.wikitext.value = await this.decryptSyntax(text, passphrase); 56 57 if (this.failcount) { 58 GUI.toast(LANG.plugins.encryptedpasswords.failcount.replace('%d', this.failcount), 'error'); 59 } 60 } 61 62 /** 63 * Encrypt all clear text syntax in the given text 64 * 65 * @param {string} text 66 * @param {string} passphrase 67 * @return {Promise<string>} 68 */ 69 async decryptSyntax(text, passphrase) { 70 const re = new RegExp('<decrypt>(.*?)(<\/decrypt>)', 'gs'); 71 72 const matches = [...text.matchAll(re)]; 73 74 for (let i = 0; i < matches.length; i++) { 75 const cipher = matches[i][1]; 76 try { 77 const clear = await this.aes.autodecrypt(cipher, passphrase); 78 text = text.replace(matches[i][0], `<encrypt>${clear}</encrypt>`); 79 } catch (e) { 80 this.failcount++; 81 } 82 } 83 84 return text; 85 } 86 87 /** 88 * Event handler to encrypt all passwords 89 * 90 * @param {Event} e 91 * @return {Promise<void>} 92 */ 93 async encryptAllSyntax(e) { 94 // access the syntax in the editor 95 const prefix = e.target.form.prefix.value; 96 const suffix = e.target.form.suffix.value; 97 const text = e.target.form.wikitext.value; 98 99 // check if syntax contains clear text passwords 100 const re = new RegExp('<encrypt>.*?(<\/encrypt>)','s'); 101 if ( 102 !prefix.match(re) && 103 !suffix.match(re) && 104 !text.match(re) 105 ) { 106 return; 107 } 108 109 // stop the event 110 e.stopPropagation(); 111 e.preventDefault(); 112 113 // ask for passphrase 114 const passphrase = await GUI.prompt( 115 LANG.plugins.encryptedpasswords.enterKey, 116 LANG.plugins.encryptedpasswords.passphrase, 117 true 118 ); 119 if (passphrase === null || passphrase === '') return; 120 121 // replace 122 e.target.form.prefix.value = await this.encryptSyntax(prefix, passphrase); 123 e.target.form.suffix.value = await this.encryptSyntax(suffix, passphrase); 124 e.target.form.wikitext.value = await this.encryptSyntax(text, passphrase); 125 126 // trigger the event again 127 jQuery(e.target).trigger(e.type); 128 } 129 130 /** 131 * Encrypt all clear text syntax in the given text 132 * 133 * @param {string} text 134 * @param {string} passphrase 135 * @return {Promise<string>} 136 */ 137 async encryptSyntax(text, passphrase) { 138 const re = new RegExp('<encrypt>(.*?)(<\/encrypt>)', 'gs'); 139 140 const matches = [...text.matchAll(re)]; 141 142 for (let i = 0; i < matches.length; i++) { 143 const clear = matches[i][1]; 144 const cipher = await this.aes.encrypt(clear, passphrase); 145 text = text.replace(matches[i][0], `<decrypt>${cipher}</decrypt>`); 146 } 147 148 return text; 149 } 150} 151