Aleksander Machniak
2016-03-26 f0e88f02d5ae5ba6ca605e5d898de0abdeda5a9b
commit | author | age
48e9c1 1 /* Enigma Plugin */
T 2
0878c8 3 window.rcmail && rcmail.addEventListener('init', function(evt) {
AM 4     if (rcmail.env.task == 'settings') {
5         if (rcmail.gui_objects.keyslist) {
6             rcmail.keys_list = new rcube_list_widget(rcmail.gui_objects.keyslist,
211929 7                 {multiselect:true, draggable:false, keyboard:false});
0878c8 8             rcmail.keys_list
AM 9                 .addEventListener('select', function(o) { rcmail.enigma_keylist_select(o); })
10                 .addEventListener('keypress', function(o) { rcmail.enigma_keylist_keypress(o); })
11                 .init()
12                 .focus();
48e9c1 13
0878c8 14             rcmail.enigma_list();
48e9c1 15
0878c8 16             rcmail.register_command('firstpage', function(props) { return rcmail.enigma_list_page('first'); });
AM 17             rcmail.register_command('previouspage', function(props) { return rcmail.enigma_list_page('previous'); });
18             rcmail.register_command('nextpage', function(props) { return rcmail.enigma_list_page('next'); });
19             rcmail.register_command('lastpage', function(props) { return rcmail.enigma_list_page('last'); });
20         }
48e9c1 21
0878c8 22         if (rcmail.env.action == 'plugin.enigmakeys') {
AM 23             rcmail.register_command('search', function(props) {return rcmail.enigma_search(props); }, true);
24             rcmail.register_command('reset-search', function(props) {return rcmail.enigma_search_reset(props); }, true);
25             rcmail.register_command('plugin.enigma-import', function() { rcmail.enigma_import(); }, true);
211929 26             rcmail.register_command('plugin.enigma-key-export', function() { rcmail.enigma_export(); });
AM 27             rcmail.register_command('plugin.enigma-key-export-selected', function() { rcmail.enigma_export(true); });
28             rcmail.register_command('plugin.enigma-key-import', function() { rcmail.enigma_key_import(); }, true);
29             rcmail.register_command('plugin.enigma-key-delete', function(props) { return rcmail.enigma_delete(); });
d5501a 30             rcmail.register_command('plugin.enigma-key-create', function(props) { return rcmail.enigma_key_create(); }, true);
AM 31             rcmail.register_command('plugin.enigma-key-save', function(props) { return rcmail.enigma_key_create_save(); }, true);
211929 32
AM 33             rcmail.addEventListener('responseafterplugin.enigmakeys', function() {
34                 rcmail.enable_command('plugin.enigma-key-export', rcmail.env.rowcount > 0);
35             });
0878c8 36         }
AM 37     }
38     else if (rcmail.env.task == 'mail') {
39         if (rcmail.env.action == 'compose') {
a99c34 40             rcmail.addEventListener('beforesend', function(props) { rcmail.enigma_beforesend_handler(props); })
AM 41                 .addEventListener('beforesavedraft', function(props) { rcmail.enigma_beforesavedraft_handler(props); });
42
0878c8 43             $('input,label', $('#enigmamenu')).mouseup(function(e) {
AM 44                 // don't close the menu on mouse click inside
45                 e.stopPropagation();
46             });
47         }
58c279 48
f04b56 49         $.each(['encrypt', 'sign'], function() {
AM 50             if (rcmail.env['enigma_force_' + this])
51                 $('[name="_enigma_' + this + '"]').prop('checked', true);
52         });
53
58c279 54         if (rcmail.env.enigma_password_request) {
AM 55             rcmail.enigma_password_request(rcmail.env.enigma_password_request);
48e9c1 56         }
0878c8 57     }
AM 58 });
59
48e9c1 60
T 61 /*********************************************************/
62 /*********    Enigma Settings/Keys/Certs UI      *********/
63 /*********************************************************/
64
65 // Display key(s) import form
66 rcube_webmail.prototype.enigma_key_import = function()
67 {
0878c8 68     this.enigma_loadframe('&_action=plugin.enigmakeys&_a=import');
d5501a 69 };
AM 70
71 // Display key(s) generation form
72 rcube_webmail.prototype.enigma_key_create = function()
73 {
74     this.enigma_loadframe('&_action=plugin.enigmakeys&_a=create');
75 };
76
77 // Generate key(s) and submit them
78 rcube_webmail.prototype.enigma_key_create_save = function()
79 {
80     var options, lock,
81         user = $('#key-ident > option').filter(':selected').text(),
82         password = $('#key-pass').val(),
83         confirm = $('#key-pass-confirm').val(),
84         size = $('#key-size').val();
85
86     // validate the form
87     if (!password || !confirm)
8f8bea 88         return alert(this.get_label('enigma.formerror'));
d5501a 89
AM 90     if (password != confirm)
8f8bea 91         return alert(this.get_label('enigma.passwordsdiffer'));
d5501a 92
AM 93     if (user.match(/^<[^>]+>$/))
8f8bea 94         return alert(this.get_label('enigma.nonameident'));
d5501a 95
AM 96     // generate keys
97     // use OpenPGP.js if browser supports required features
98     if (window.openpgp && window.crypto && (window.crypto.getRandomValues || window.crypto.subtle)) {
99         lock = this.set_busy(true, 'enigma.keygenerating');
100         options = {
101             numBits: size,
102             userId: user,
103             passphrase: password
104         };
105
106         openpgp.generateKeyPair(options).then(function(keypair) {
107             // success
a0dfcb 108             var post = {_a: 'import', _keys: keypair.privateKeyArmored};
d5501a 109
AM 110             // send request to server
111             rcmail.http_post('plugin.enigmakeys', post, lock);
a8848b 112         }, function(error) {
d5501a 113             // failure
AM 114             rcmail.set_busy(false, null, lock);
8f8bea 115             rcmail.display_message(rcmail.get_label('enigma.keygenerateerror'), 'error');
d5501a 116         });
AM 117     }
118     // generate keys on the server
a0dfcb 119     else if (rcmail.env.enigma_keygen_server) {
AM 120         lock = this.set_busy(true, 'enigma.keygenerating');
121         options = {_a: 'generate', _user: user, _password: password, _size: size};
122         rcmail.http_post('plugin.enigmakeys', options, lock);
123     }
d5501a 124     else {
8f8bea 125         rcmail.display_message(rcmail.get_label('enigma.keygennosupport'), 'error');
d5501a 126     }
AM 127 };
128
129 // Action executed after successful key generation and import
130 rcube_webmail.prototype.enigma_key_create_success = function()
131 {
132     parent.rcmail.enigma_list(1);
48e9c1 133 };
T 134
0878c8 135 // Delete key(s)
211929 136 rcube_webmail.prototype.enigma_delete = function()
0878c8 137 {
AM 138     var keys = this.keys_list.get_selection();
139
140     if (!keys.length || !confirm(this.get_label('enigma.keyremoveconfirm')))
141         return;
142
143     var lock = this.display_message(this.get_label('enigma.keyremoving'), 'loading'),
144         post = {_a: 'delete', _keys: keys};
145
146     // send request to server
147     this.http_post('plugin.enigmakeys', post, lock);
211929 148 };
AM 149
150 // Export key(s)
151 rcube_webmail.prototype.enigma_export = function(selected)
152 {
153     var keys = selected ? this.keys_list.get_selection().join(',') : '*';
154
155     if (!keys.length)
156         return;
157
4a4088 158     this.goto_url('plugin.enigmakeys', {_a: 'export', _keys: keys}, false, true);
0878c8 159 };
AM 160
161 // Submit key(s) import form
48e9c1 162 rcube_webmail.prototype.enigma_import = function()
T 163 {
164     var form, file;
0878c8 165
48e9c1 166     if (form = this.gui_objects.importform) {
T 167         file = document.getElementById('rcmimportfile');
168         if (file && !file.value) {
169             alert(this.get_label('selectimportfile'));
170             return;
171         }
0878c8 172
AM 173         var lock = this.set_busy(true, 'importwait');
174
175         form.action = this.add_url(form.action, '_unlock', lock);
48e9c1 176         form.submit();
0878c8 177
48e9c1 178         this.lock_form(form, true);
T 179    }
180 };
181
182 // list row selection handler
0878c8 183 rcube_webmail.prototype.enigma_keylist_select = function(list)
48e9c1 184 {
211929 185     var id = list.get_single_selection(), url;
0878c8 186
211929 187     if (id)
AM 188         url = '&_action=plugin.enigmakeys&_a=info&_id=' + id;
189
190     this.enigma_loadframe(url);
191     this.enable_command('plugin.enigma-key-delete', 'plugin.enigma-key-export-selected', list.selection.length > 0);
0878c8 192 };
AM 193
194 rcube_webmail.prototype.enigma_keylist_keypress = function(list)
195 {
196     if (list.modkey == CONTROL_KEY)
197         return;
198
199     if (list.key_pressed == list.DELETE_KEY || list.key_pressed == list.BACKSPACE_KEY)
200         this.command('plugin.enigma-key-delete');
201     else if (list.key_pressed == 33)
202         this.command('previouspage');
203     else if (list.key_pressed == 34)
204         this.command('nextpage');
48e9c1 205 };
T 206
207 // load key frame
0878c8 208 rcube_webmail.prototype.enigma_loadframe = function(url)
48e9c1 209 {
T 210     var frm, win;
0878c8 211
48e9c1 212     if (this.env.contentframe && window.frames && (frm = window.frames[this.env.contentframe])) {
0878c8 213         if (!url && (win = window.frames[this.env.contentframe])) {
AM 214             if (win.location && win.location.href.indexOf(this.env.blankpage) < 0)
48e9c1 215                 win.location.href = this.env.blankpage;
T 216             return;
217         }
0878c8 218
d5501a 219         this.env.frame_lock = this.set_busy(true, 'loading');
AM 220         frm.location.href = this.env.comm_path + '&_framed=1&' + url;
48e9c1 221     }
T 222 };
223
224 // Search keys/certs
225 rcube_webmail.prototype.enigma_search = function(props)
226 {
227     if (!props && this.gui_objects.qsearchbox)
228         props = this.gui_objects.qsearchbox.value;
229
230     if (props || this.env.search_request) {
0878c8 231         var params = {'_a': 'search', '_q': urlencode(props)},
48e9c1 232           lock = this.set_busy(true, 'searching');
T 233 //        if (this.gui_objects.search_filter)
234   //          addurl += '&_filter=' + this.gui_objects.search_filter.value;
0878c8 235         this.env.current_page = 1;
48e9c1 236         this.enigma_loadframe();
T 237         this.enigma_clear_list();
0878c8 238         this.http_post('plugin.enigmakeys', params, lock);
48e9c1 239     }
T 240
241     return false;
242 }
243
244 // Reset search filter and the list
245 rcube_webmail.prototype.enigma_search_reset = function(props)
246 {
247     var s = this.env.search_request;
248     this.reset_qsearch();
249
250     if (s) {
251         this.enigma_loadframe();
252         this.enigma_clear_list();
253
254         // refresh the list
255         this.enigma_list();
256     }
257
258     return false;
259 }
260
261 // Keys/certs listing
262 rcube_webmail.prototype.enigma_list = function(page)
263 {
0878c8 264     var params = {'_a': 'list'},
48e9c1 265       lock = this.set_busy(true, 'loading');
T 266
267     this.env.current_page = page ? page : 1;
268
269     if (this.env.search_request)
270         params._q = this.env.search_request;
271     if (page)
272         params._p = page;
273
274     this.enigma_clear_list();
0878c8 275     this.http_post('plugin.enigmakeys', params, lock);
48e9c1 276 }
T 277
278 // Change list page
279 rcube_webmail.prototype.enigma_list_page = function(page)
280 {
281     if (page == 'next')
282         page = this.env.current_page + 1;
283     else if (page == 'last')
284         page = this.env.pagecount;
285     else if (page == 'prev' && this.env.current_page > 1)
286         page = this.env.current_page - 1;
287     else if (page == 'first' && this.env.current_page > 1)
288         page = 1;
289
290     this.enigma_list(page);
291 }
292
293 // Remove list rows
294 rcube_webmail.prototype.enigma_clear_list = function()
295 {
296     this.enigma_loadframe();
297     if (this.keys_list)
298         this.keys_list.clear(true);
211929 299
AM 300     this.enable_command('plugin.enigma-key-delete', 'plugin.enigma-key-delete-selected', false);
48e9c1 301 }
T 302
303 // Adds a row to the list
304 rcube_webmail.prototype.enigma_add_list_row = function(r)
305 {
306     if (!this.gui_objects.keyslist || !this.keys_list)
307         return false;
308
309     var list = this.keys_list,
310         tbody = this.gui_objects.keyslist.tBodies[0],
311         rowcount = tbody.rows.length,
312         even = rowcount%2,
313         css_class = 'message'
314             + (even ? ' even' : ' odd'),
315         // for performance use DOM instead of jQuery here
316         row = document.createElement('tr'),
317         col = document.createElement('td');
318
319     row.id = 'rcmrow' + r.id;
320     row.className = css_class;
321
322     col.innerHTML = r.name;
323     row.appendChild(col);
324     list.insert_row(row);
325 }
326
a99c34 327
48e9c1 328 /*********************************************************/
T 329 /*********        Enigma Message methods         *********/
330 /*********************************************************/
a99c34 331
AM 332 // handle message send/save action
333 rcube_webmail.prototype.enigma_beforesend_handler = function(props)
334 {
335     this.env.last_action = 'send';
336     this.enigma_compose_handler(props);
337 }
338
339 rcube_webmail.prototype.enigma_beforesavedraft_handler = function(props)
340 {
341     this.env.last_action = 'savedraft';
342     this.enigma_compose_handler(props);
343 }
344
345 rcube_webmail.prototype.enigma_compose_handler = function(props)
346 {
347     var form = this.gui_objects.messageform;
348
349     // copy inputs from enigma menu to the form
350     $('#enigmamenu input').each(function() {
351         var id = this.id + '_cpy', input = $('#' + id);
352
353         if (!input.length) {
354             input = $(this).clone();
355             input.prop({id: id, type: 'hidden'}).appendTo(form);
356         }
357
358         input.val(this.checked ? '1' : '');
359     });
360
361     // disable signing when saving drafts
362     if (this.env.last_action == 'savedraft') {
363         $('input[name="_enigma_sign"]', form).val(0);
364     }
365 }
48e9c1 366
T 367 // Import attached keys/certs file
368 rcube_webmail.prototype.enigma_import_attachment = function(mime_id)
369 {
0878c8 370     var lock = this.set_busy(true, 'loading'),
AM 371         post = {_uid: this.env.uid, _mbox: this.env.mailbox, _part: mime_id};
372
373     this.http_post('plugin.enigmaimport', post, lock);
48e9c1 374
T 375     return false;
0878c8 376 }
48e9c1 377
a99c34 378 // password request popup
0878c8 379 rcube_webmail.prototype.enigma_password_request = function(data)
AM 380 {
381     if (!data || !data.keyid) {
382         return;
383     }
384
385     var ref = this,
386         msg = this.get_label('enigma.enterkeypass'),
387         myprompt = $('<div class="prompt">'),
388         myprompt_content = $('<div class="message">')
389             .appendTo(myprompt),
390         myprompt_input = $('<input>').attr({type: 'password', size: 30})
391             .keypress(function(e) {
392                 if (e.which == 13)
393                     (ref.is_framed() ? window.parent.$ : $)('.ui-dialog-buttonpane button.mainaction:visible').click();
394             })
395             .appendTo(myprompt);
396
397     data.key = data.keyid;
a99c34 398     if (data.keyid.length > 8)
AM 399         data.keyid = data.keyid.substr(data.keyid.length - 8);
0878c8 400
AM 401     $.each(['keyid', 'user'], function() {
402         msg = msg.replace('$' + this, data[this]);
403     });
404
405     myprompt_content.text(msg);
406
407     this.show_popup_dialog(myprompt, this.get_label('enigma.enterkeypasstitle'),
408         [{
409             text: this.get_label('save'),
410             'class': 'mainaction',
411             click: function(e) {
412                 e.stopPropagation();
413
58c279 414                 var jq = ref.is_framed() ? window.parent.$ : $;
0878c8 415
58c279 416                 data.password = myprompt_input.val();
AM 417
418                 if (!data.password) {
0878c8 419                     myprompt_input.focus();
AM 420                     return;
421                 }
422
58c279 423                 ref.enigma_password_submit(data);
0878c8 424                 jq(this).remove();
AM 425             }
426         },
427         {
428             text: this.get_label('cancel'),
429             click: function(e) {
430                 var jq = ref.is_framed() ? window.parent.$ : $;
431                 e.stopPropagation();
432                 jq(this).remove();
433             }
434         }], {width: 400});
435
436     if (this.is_framed() && parent.rcmail.message_list) {
437         // this fixes bug when pressing Enter on "Save" button in the dialog
438         parent.rcmail.message_list.blur();
439     }
440 }
441
a99c34 442 // submit entered password
58c279 443 rcube_webmail.prototype.enigma_password_submit = function(data)
0878c8 444 {
58c279 445     if (this.env.action == 'compose' && !data['compose-init']) {
AM 446         return this.enigma_password_compose_submit(data);
a99c34 447     }
58c279 448
AM 449     var lock = this.set_busy(true, 'loading');
a99c34 450
AM 451     // message preview
0878c8 452     var form = $('<form>').attr({method: 'post', action: location.href, style: 'display:none'})
58c279 453         .append($('<input>').attr({type: 'hidden', name: '_keyid', value: data.key}))
AM 454         .append($('<input>').attr({type: 'hidden', name: '_passwd', value: data.password}))
0878c8 455         .append($('<input>').attr({type: 'hidden', name: '_token', value: this.env.request_token}))
58c279 456         .append($('<input>').attr({type: 'hidden', name: '_unlock', value: lock}))
0878c8 457         .appendTo(document.body);
AM 458
459     form.submit();
460 }
a99c34 461
AM 462 // submit entered password - in mail compose page
58c279 463 rcube_webmail.prototype.enigma_password_compose_submit = function(data)
a99c34 464 {
AM 465     var form = this.gui_objects.messageform;
466
467     if (!$('input[name="_keyid"]', form).length) {
58c279 468         $(form).append($('<input>').attr({type: 'hidden', name: '_keyid', value: data.key}))
AM 469             .append($('<input>').attr({type: 'hidden', name: '_passwd', value: data.password}));
a99c34 470     }
AM 471     else {
58c279 472         $('input[name="_keyid"]', form).val(data.key);
AM 473         $('input[name="_passwd"]', form).val(data.password);
a99c34 474     }
AM 475
476     this.submit_messageform(this.env.last_action == 'savedraft');
477 }