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