Du kan inte välja fler än 25 ämnen Ämnen måste starta med en bokstav eller siffra, kan innehålla bindestreck ('-') och vara max 35 tecken långa.

enigma.js 20KB

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