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