Merged branch 'dev-mailvelope'
1 files added
12 files modified
| | |
| | | this.http_post(postact, postdata); |
| | | } |
| | | |
| | | this.check_mailvelope(this.env.action); |
| | | |
| | | // detect browser capabilities |
| | | if (!this.is_framed() && !this.env.extwin) |
| | | this.browser_capabilities_check(); |
| | |
| | | $('input.rcpagejumper').val(this.env.current_page).prop('disabled', this.env.pagecount < 2); |
| | | }; |
| | | |
| | | // check for mailvelope API |
| | | this.check_mailvelope = function(action) |
| | | { |
| | | if (typeof window.mailvelope !== 'undefined') { |
| | | this.mailvelope_load(action); |
| | | } |
| | | else { |
| | | $(window).on('mailvelope', function() { |
| | | ref.mailvelope_load(action); |
| | | }); |
| | | } |
| | | }; |
| | | |
| | | // |
| | | this.mailvelope_load = function(action) |
| | | { |
| | | if (this.env.browser_capabilities) |
| | | this.env.browser_capabilities['pgpmime'] = 1; |
| | | |
| | | var keyring = this.get_local_storage_prefix(); |
| | | |
| | | mailvelope.getKeyring(keyring).then(function(kr) { |
| | | ref.mailvelope_keyring = kr; |
| | | ref.mailvelope_init(action, kr); |
| | | }).catch(function(err) { |
| | | // attempt to create a new keyring for this app/user |
| | | mailvelope.createKeyring(keyring).then(function(kr) { |
| | | ref.mailvelope_keyring = kr; |
| | | ref.mailvelope_init(action, kr); |
| | | }).catch(function(err) { |
| | | console.error(err); |
| | | }); |
| | | }); |
| | | }; |
| | | |
| | | // |
| | | this.mailvelope_init = function(action, keyring) |
| | | { |
| | | if (action == 'show' || action == 'preview') { |
| | | // decrypt text body |
| | | if (this.env.is_pgp_content && window.mailvelope) { |
| | | var data = $(this.env.is_pgp_content).text(); |
| | | ref.mailvelope_display_container(this.env.is_pgp_content, data, keyring); |
| | | } |
| | | // load pgp/mime message and pass it to the mailvelope display container |
| | | else if (this.env.pgp_mime_part && window.mailvelope) { |
| | | var msgid = this.display_message(this.get_label('loadingdata'), 'loading'), |
| | | selector = this.env.pgp_mime_container; |
| | | |
| | | $.ajax({ |
| | | type: 'GET', |
| | | url: this.url('get', { '_mbox': this.env.mailbox, '_uid': this.env.uid, '_part': this.env.pgp_mime_part }), |
| | | error: function(o, status, err) { |
| | | ref.http_error(o, status, err, msgid); |
| | | }, |
| | | success: function(data) { |
| | | ref.mailvelope_display_container(selector, data, keyring, msgid); |
| | | } |
| | | }); |
| | | } |
| | | } |
| | | else if (action == 'compose' && window.mailvelope) { |
| | | this.env.compose_commands.push('compose-encrypted'); |
| | | |
| | | if (this.env.pgp_mime_message) { |
| | | // fetch PGP/Mime part and open load into Mailvelope editor |
| | | var lock = this.set_busy(true, this.get_label('loadingdata')); |
| | | $.ajax({ |
| | | type: 'GET', |
| | | url: this.url('get', this.env.pgp_mime_message), |
| | | error: function(o, status, err) { |
| | | ref.http_error(o, status, err, lock); |
| | | ref.enable_command('compose-encrypted', true); |
| | | }, |
| | | success: function(data) { |
| | | ref.set_busy(false, null, lock); |
| | | ref.compose_encrypted({ quotedMail: data }); |
| | | ref.enable_command('compose-encrypted', true); |
| | | } |
| | | }); |
| | | } |
| | | else { |
| | | // enable encrypted compose toggle |
| | | this.enable_command('compose-encrypted', true); |
| | | } |
| | | } |
| | | }; |
| | | |
| | | // handler for the 'compose-encrypted' command |
| | | this.compose_encrypted = function(props) |
| | | { |
| | | var container = $('#' + this.env.composebody).parent(); |
| | | |
| | | // remove Mailvelope editor if active |
| | | if (ref.mailvelope_editor) { |
| | | ref.mailvelope_editor = null; |
| | | ref.compose_skip_unsavedcheck = false; |
| | | ref.set_button('compose-encrypted', 'act'); |
| | | |
| | | container.removeClass('mailvelope') |
| | | .find('iframe:not([aria-hidden=true])').remove(); |
| | | $('#' + ref.env.composebody).show(); |
| | | $("[name='_pgpmime']").remove(); |
| | | } |
| | | // embed Mailvelope editor container |
| | | else { |
| | | var options = { predefinedText: $('#' + this.env.composebody).val() }; |
| | | if (props.quotedMail) { |
| | | options = { quotedMail: props.quotedMail, quotedMailIndent: false }; |
| | | } |
| | | if (this.env.compose_mode == 'reply') { |
| | | options.quotedMailIndent = true; |
| | | options.quotedMailHeader = this.env.compose_reply_header; |
| | | } |
| | | |
| | | mailvelope.createEditorContainer('#' + container.attr('id'), ref.mailvelope_keyring, options).then(function(editor) { |
| | | ref.mailvelope_editor = editor; |
| | | ref.compose_skip_unsavedcheck = true; |
| | | ref.set_button('compose-encrypted', 'sel'); |
| | | |
| | | container.addClass('mailvelope'); |
| | | $('#' + ref.env.composebody).hide(); |
| | | |
| | | // notify user about loosing attachments |
| | | if (ref.env.attachments && !$.isEmptyObject(ref.env.attachments)) { |
| | | alert(ref.get_label('encryptnoattachments')); |
| | | |
| | | $.each(ref.env.attachments, function(name, attach) { |
| | | ref.remove_from_attachment_list(name); |
| | | }); |
| | | } |
| | | }).catch(function(err) { |
| | | console.error(err); |
| | | }); |
| | | } |
| | | }; |
| | | |
| | | // callback to replace the message body with the full armored |
| | | this.mailvelope_submit_messageform = function(draft, saveonly) |
| | | { |
| | | // get recipients |
| | | var recipients = []; |
| | | $.each(['to', 'cc', 'bcc'], function(i,field) { |
| | | var pos, rcpt, val = $.trim($('[name="_' + field + '"]').val()); |
| | | while (val.length && rcube_check_email(val, true)) { |
| | | rcpt = RegExp.$2; |
| | | recipients.push(rcpt); |
| | | val = val.substr(val.indexOf(rcpt) + rcpt.length + 1).replace(/^\s*,\s*/, ''); |
| | | } |
| | | }); |
| | | |
| | | // check if we have keys for all recipients |
| | | var isvalid = recipients.length > 0; |
| | | ref.mailvelope_keyring.validKeyForAddress(recipients).then(function(status) { |
| | | var missing_keys = []; |
| | | $.each(status, function(k,v) { |
| | | if (v === false) { |
| | | isvalid = false; |
| | | missing_keys.push(k); |
| | | } |
| | | }); |
| | | |
| | | // list recipients with missing keys |
| | | if (!isvalid && missing_keys.length) { |
| | | // load publickey.js |
| | | if (!$('script#publickeyjs').length) { |
| | | $('<script>') |
| | | .attr('id', 'publickeyjs') |
| | | .attr('src', ref.assets_path('program/js/publickey.js')) |
| | | .appendTo(document.body); |
| | | } |
| | | |
| | | // display dialog with missing keys |
| | | ref.show_popup_dialog( |
| | | ref.get_label('nopubkeyfor').replace('$email', missing_keys.join(', ')) + |
| | | '<p>' + ref.get_label('searchpubkeyservers') + '</p>', |
| | | ref.get_label('encryptedsendialog'), |
| | | [{ |
| | | text: ref.get_label('search'), |
| | | 'class': 'mainaction', |
| | | click: function() { |
| | | var $dialog = $(this); |
| | | ref.mailvelope_search_pubkeys(missing_keys, function() { |
| | | $dialog.dialog('close') |
| | | }); |
| | | } |
| | | }, |
| | | { |
| | | text: ref.get_label('cancel'), |
| | | click: function(){ |
| | | $(this).dialog('close'); |
| | | } |
| | | }] |
| | | ); |
| | | return false; |
| | | } |
| | | |
| | | if (!isvalid) { |
| | | if (!recipients.length) { |
| | | alert(ref.get_label('norecipientwarning')); |
| | | $("[name='_to']").focus(); |
| | | } |
| | | return false; |
| | | } |
| | | |
| | | // add sender identity to recipients to be able to decrypt our very own message |
| | | var senders = [], selected_sender = ref.env.identities[$("[name='_from'] option:selected").val()]; |
| | | $.each(ref.env.identities, function(k, sender) { |
| | | senders.push(sender.email); |
| | | }); |
| | | |
| | | ref.mailvelope_keyring.validKeyForAddress(senders).then(function(status) { |
| | | valid_sender = null; |
| | | $.each(status, function(k,v) { |
| | | if (v !== false) { |
| | | valid_sender = k; |
| | | if (valid_sender == selected_sender) { |
| | | return false; // break |
| | | } |
| | | } |
| | | }); |
| | | |
| | | if (!valid_sender) { |
| | | if (!confirm(ref.get_label('nopubkeyforsender'))) { |
| | | return false; |
| | | } |
| | | } |
| | | |
| | | recipients.push(valid_sender); |
| | | |
| | | ref.mailvelope_editor.encrypt(recipients).then(function(armored) { |
| | | // all checks passed, send message |
| | | var form = ref.gui_objects.messageform, |
| | | hidden = $("[name='_pgpmime']", form), |
| | | msgid = ref.set_busy(true, draft || saveonly ? 'savingmessage' : 'sendingmessage') |
| | | |
| | | form.target = 'savetarget'; |
| | | form._draft.value = draft ? '1' : ''; |
| | | form.action = ref.add_url(form.action, '_unlock', msgid); |
| | | form.action = ref.add_url(form.action, '_framed', 1); |
| | | |
| | | if (saveonly) { |
| | | form.action = ref.add_url(form.action, '_saveonly', 1); |
| | | } |
| | | |
| | | // send pgp conent via hidden field |
| | | if (!hidden.length) { |
| | | hidden = $('<input type="hidden" name="_pgpmime">').appendTo(form); |
| | | } |
| | | hidden.val(armored); |
| | | |
| | | form.submit(); |
| | | |
| | | }).catch(function(err) { |
| | | console.log(err); |
| | | }); // mailvelope_editor.encrypt() |
| | | |
| | | }).catch(function(err) { |
| | | console.error(err); |
| | | }); // mailvelope_keyring.validKeyForAddress(senders) |
| | | |
| | | }).catch(function(err) { |
| | | console.error(err); |
| | | }); // mailvelope_keyring.validKeyForAddress(recipients) |
| | | |
| | | return false; |
| | | }; |
| | | |
| | | // wrapper for the mailvelope.createDisplayContainer API call |
| | | this.mailvelope_display_container = function(selector, data, keyring, msgid) |
| | | { |
| | | mailvelope.createDisplayContainer(selector, data, keyring, { showExternalContent: this.env.safemode }).then(function() { |
| | | $(selector).addClass('mailvelope').find('.message-part, .part-notice').hide(); |
| | | ref.hide_message(msgid); |
| | | setTimeout(function() { $(window).resize(); }, 10); |
| | | }).catch(function(err) { |
| | | console.error(err); |
| | | ref.hide_message(msgid); |
| | | ref.display_message('Message decryption failed: ' + err.message, 'error') |
| | | }); |
| | | }; |
| | | |
| | | // subroutine to query keyservers for public keys |
| | | this.mailvelope_search_pubkeys = function(emails, resolve) |
| | | { |
| | | // query with publickey.js |
| | | var deferreds = [], |
| | | pk = new PublicKey(), |
| | | lock = ref.display_message(ref.get_label('loading'), 'loading'); |
| | | |
| | | $.each(emails, function(i, email) { |
| | | var d = $.Deferred(); |
| | | pk.search(email, function(results, errorCode) { |
| | | if (errorCode !== null) { |
| | | // rejecting would make all fail |
| | | // d.reject(email); |
| | | d.resolve([email]); |
| | | } |
| | | else { |
| | | d.resolve([email].concat(results)); |
| | | } |
| | | }); |
| | | deferreds.push(d); |
| | | }); |
| | | |
| | | $.when.apply($, deferreds).then(function() { |
| | | var missing_keys = [], |
| | | key_selection = []; |
| | | |
| | | // alanyze results of all queries |
| | | $.each(arguments, function(i, result) { |
| | | var email = result.shift(); |
| | | if (!result.length) { |
| | | missing_keys.push(email); |
| | | } |
| | | else { |
| | | key_selection = key_selection.concat(result); |
| | | } |
| | | }); |
| | | |
| | | ref.hide_message(lock); |
| | | resolve(true); |
| | | |
| | | // show key import dialog |
| | | if (key_selection.length) { |
| | | ref.mailvelope_key_import_dialog(key_selection); |
| | | } |
| | | // some keys could not be found |
| | | if (missing_keys.length) { |
| | | ref.display_message(ref.get_label('nopubkeyfor').replace('$email', missing_keys.join(', ')), 'warning'); |
| | | } |
| | | }, function() { |
| | | console.error('Pubkey lookup failed with', arguments); |
| | | ref.hide_message(lock); |
| | | ref.display_message('pubkeysearcherror', 'error'); |
| | | resolve(false); |
| | | }); |
| | | }; |
| | | |
| | | // list the given public keys in a dialog with options to import |
| | | // them into the local Maivelope keyring |
| | | this.mailvelope_key_import_dialog = function(candidates) |
| | | { |
| | | var ul = $('<div>').addClass('listing mailvelopekeyimport'); |
| | | $.each(candidates, function(i, keyrec) { |
| | | var li = $('<div>').addClass('key'); |
| | | if (keyrec.revoked) li.addClass('revoked'); |
| | | if (keyrec.disabled) li.addClass('disabled'); |
| | | if (keyrec.expired) li.addClass('expired'); |
| | | |
| | | li.append($('<label>').addClass('keyid').text(ref.get_label('keyid'))); |
| | | li.append($('<a>').text(keyrec.keyid.substr(-8).toUpperCase()) |
| | | .attr('href', keyrec.info) |
| | | .attr('target', '_blank') |
| | | .attr('tabindex', '-1')); |
| | | |
| | | li.append($('<label>').addClass('keylen').text(ref.get_label('keylength'))); |
| | | li.append($('<span>').text(keyrec.keylen)); |
| | | |
| | | if (keyrec.expirationdate) { |
| | | li.append($('<label>').addClass('keyexpired').text(ref.get_label('keyexpired'))); |
| | | li.append($('<span>').text(new Date(keyrec.expirationdate * 1000).toDateString())); |
| | | } |
| | | |
| | | if (keyrec.revoked) { |
| | | li.append($('<span>').addClass('keyrevoked').text(ref.get_label('keyrevoked'))); |
| | | } |
| | | |
| | | var ul_ = $('<ul>').addClass('uids'); |
| | | $.each(keyrec.uids, function(j, uid) { |
| | | var li_ = $('<li>').addClass('uid'); |
| | | if (uid.revoked) li_.addClass('revoked'); |
| | | if (uid.disabled) li_.addClass('disabled'); |
| | | if (uid.expired) li_.addClass('expired'); |
| | | |
| | | ul_.append(li_.text(uid.uid)); |
| | | }); |
| | | |
| | | li.append(ul_); |
| | | li.append($('<input>') |
| | | .attr('type', 'button') |
| | | .attr('rel', keyrec.keyid) |
| | | .attr('value', ref.get_label('import')) |
| | | .addClass('button importkey') |
| | | .prop('disabled', keyrec.revoked || keyrec.disabled || keyrec.expired)); |
| | | |
| | | ul.append(li); |
| | | }); |
| | | |
| | | // display dialog with missing keys |
| | | ref.show_popup_dialog( |
| | | $('<div>') |
| | | .append($('<p>').html(ref.get_label('encryptpubkeysfound'))) |
| | | .append(ul), |
| | | ref.get_label('importpubkeys'), |
| | | [{ |
| | | text: ref.get_label('close'), |
| | | click: function(){ |
| | | $(this).dialog('close'); |
| | | } |
| | | }] |
| | | ); |
| | | |
| | | // delegate handler for import button clicks |
| | | ul.on('click', 'input.button.importkey', function() { |
| | | var btn = $(this), |
| | | keyid = btn.attr('rel'), |
| | | pk = new PublicKey(), |
| | | lock = ref.display_message(ref.get_label('loading'), 'loading'); |
| | | |
| | | // fetch from keyserver and import to Mailvelope keyring |
| | | pk.get(keyid, function(armored, errorCode) { |
| | | ref.hide_message(lock); |
| | | |
| | | if (errorCode) { |
| | | ref.display_message('Failed to get key from keyserver', 'error'); |
| | | return; |
| | | } |
| | | |
| | | // import to keyring |
| | | ref.mailvelope_keyring.importPublicKey(armored).then(function(status) { |
| | | if (status === 'REJECTED') { |
| | | // alert(ref.get_label('Key import was rejected')); |
| | | } |
| | | else { |
| | | btn.closest('.key').fadeOut(); |
| | | ref.display_message(ref.get_label('Public key $key successfully imported into your key ring') |
| | | .replace('$key', keyid.substr(-8).toUpperCase()), 'confirmation'); |
| | | } |
| | | }).catch(function(err) { |
| | | console.log(err); |
| | | }); |
| | | }); |
| | | }); |
| | | |
| | | }; |
| | | |
| | | |
| | | /*********************************************************/ |
| | | /********* mailbox folders methods *********/ |
| | | /*********************************************************/ |
| | |
| | | } |
| | | }] |
| | | ); |
| | | } |
| | | |
| | | // delegate sending to Mailvelope routine |
| | | if (this.mailvelope_editor) { |
| | | return this.mailvelope_submit_messageform(draft, saveonly); |
| | | } |
| | | |
| | | // all checks passed, send message |
| | |
| | | |
| | | // reset history of hidden iframe used for saving draft (#1489643) |
| | | // but don't do this on timer-triggered draft-autosaving (#1489789) |
| | | if (window.frames['savetarget'] && window.frames['savetarget'].history && !this.draft_autosave_submit) { |
| | | if (window.frames['savetarget'] && window.frames['savetarget'].history && !this.draft_autosave_submit && !this.mailvelope_editor) { |
| | | window.frames['savetarget'].history.back(); |
| | | } |
| | | |
| | |
| | | for (id in this.env.attachments) |
| | | str += id; |
| | | |
| | | // we can't detect changes in the Mailvelope editor so assume it changed |
| | | if (this.mailvelope_editor) { |
| | | str += ';' + new Date().getTime(); |
| | | } |
| | | |
| | | if (save) |
| | | this.cmp_hash = str; |
| | | |
| | |
| | | ], |
| | | icann_addr = 'mailtest\\x40('+icann_domains.join('|')+')', |
| | | word = '('+atom+'|'+quoted_string+')', |
| | | delim = '[,;\s\n]', |
| | | delim = '[,;\\s\\n]', |
| | | local_part = word+'(\\x2e'+word+')*', |
| | | addr_spec = '(('+local_part+'\\x40'+domain+')|('+icann_addr+'))', |
| | | reg1 = inline ? new RegExp('(^|<|'+delim+')'+addr_spec+'($|>|'+delim+')', 'i') : new RegExp('^'+addr_spec+'$', 'i'); |
New file |
| | |
| | | "use strict"; |
| | | |
| | | (function(context){ |
| | | /* |
| | | Default keyservers (HTTPS and CORS enabled) |
| | | */ |
| | | var DEFAULT_KEYSERVERS = [ |
| | | "https://keys.fedoraproject.org/", |
| | | "https://keybase.io/", |
| | | ]; |
| | | |
| | | /* |
| | | Initialization to create an PublicKey object. |
| | | |
| | | Arguments: |
| | | |
| | | * keyservers - Array of keyserver domains, default is: |
| | | ["https://keys.fedoraproject.org/", "https://keybase.io/"] |
| | | |
| | | Examples: |
| | | |
| | | //Initialize with the default keyservers |
| | | var hkp = new PublicKey(); |
| | | |
| | | //Initialize only with a specific keyserver |
| | | var hkp = new PublicKey(["https://key.ip6.li/"]); |
| | | */ |
| | | var PublicKey = function(keyservers){ |
| | | this.keyservers = keyservers || DEFAULT_KEYSERVERS; |
| | | }; |
| | | |
| | | /* |
| | | Get a public key from any keyserver based on keyId. |
| | | |
| | | Arguments: |
| | | |
| | | * keyId - String key id of the public key (this is usually a fingerprint) |
| | | |
| | | * callback - Function that is called when finished. Two arguments are |
| | | passed to the callback: publicKey and errorCode. publicKey is |
| | | an ASCII armored OpenPGP public key. errorCode is the error code |
| | | (either HTTP status code or keybase error code) returned by the |
| | | last keyserver that was tried. If a publicKey was found, |
| | | errorCode is null. If no publicKey was found, publicKey is null |
| | | and errorCode is not null. |
| | | |
| | | Examples: |
| | | |
| | | //Get a valid public key |
| | | var hkp = new PublicKey(); |
| | | hkp.get("F75BE4E6EF6E9DD203679E94E7F6FAD172EFEE3D", function(publicKey, errorCode){ |
| | | errorCode !== null ? console.log(errorCode) : console.log(publicKey); |
| | | }); |
| | | |
| | | //Try to get an invalid public key |
| | | var hkp = new PublicKey(); |
| | | hkp.get("bogus_id", function(publicKey, errorCode){ |
| | | errorCode !== null ? console.log(errorCode) : console.log(publicKey); |
| | | }); |
| | | */ |
| | | PublicKey.prototype.get = function(keyId, callback, keyserverIndex, err){ |
| | | //default starting point is at the first keyserver |
| | | if(keyserverIndex === undefined){ |
| | | keyserverIndex = 0; |
| | | } |
| | | |
| | | //no more keyservers to check, so no key found |
| | | if(keyserverIndex >= this.keyservers.length){ |
| | | return callback(null, err || 404); |
| | | } |
| | | |
| | | //set the keyserver to try next |
| | | var ks = this.keyservers[keyserverIndex]; |
| | | var _this = this; |
| | | |
| | | //special case for keybase |
| | | if(ks.indexOf("https://keybase.io/") === 0){ |
| | | |
| | | //don't need 0x prefix for keybase searches |
| | | if(keyId.indexOf("0x") === 0){ |
| | | keyId = keyId.substr(2); |
| | | } |
| | | |
| | | //request the public key from keybase |
| | | var xhr = new XMLHttpRequest(); |
| | | xhr.open("get", "https://keybase.io/_/api/1.0/user/lookup.json" + |
| | | "?fields=public_keys&key_fingerprint=" + keyId); |
| | | xhr.onload = function(){ |
| | | if(xhr.status === 200){ |
| | | var result = JSON.parse(xhr.responseText); |
| | | |
| | | //keybase error returns HTTP 200 status, which is silly |
| | | if(result['status']['code'] !== 0){ |
| | | return _this.get(keyId, callback, keyserverIndex + 1, result['status']['code']); |
| | | } |
| | | |
| | | //no public key found |
| | | if(result['them'].length === 0){ |
| | | return _this.get(keyId, callback, keyserverIndex + 1, 404); |
| | | } |
| | | |
| | | //found the public key |
| | | var publicKey = result['them'][0]['public_keys']['primary']['bundle']; |
| | | return callback(publicKey, null); |
| | | } |
| | | else{ |
| | | return _this.get(keyId, callback, keyserverIndex + 1, xhr.status); |
| | | } |
| | | }; |
| | | xhr.send(); |
| | | } |
| | | |
| | | //normal HKP keyserver |
| | | else{ |
| | | //add the 0x prefix if absent |
| | | if(keyId.indexOf("0x") !== 0){ |
| | | keyId = "0x" + keyId; |
| | | } |
| | | |
| | | //request the public key from the hkp server |
| | | var xhr = new XMLHttpRequest(); |
| | | xhr.open("get", ks + "pks/lookup?op=get&options=mr&search=" + keyId); |
| | | xhr.onload = function(){ |
| | | if(xhr.status === 200){ |
| | | return callback(xhr.responseText, null); |
| | | } |
| | | else{ |
| | | return _this.get(keyId, callback, keyserverIndex + 1, xhr.status); |
| | | } |
| | | }; |
| | | xhr.send(); |
| | | } |
| | | }; |
| | | |
| | | /* |
| | | Search for a public key in the keyservers. |
| | | |
| | | Arguments: |
| | | |
| | | * query - String to search for (usually an email, name, or username). |
| | | |
| | | * callback - Function that is called when finished. Two arguments are |
| | | passed to the callback: results and errorCode. results is an |
| | | Array of users that were returned by the search. errorCode is |
| | | the error code (either HTTP status code or keybase error code) |
| | | returned by the last keyserver that was tried. If any results |
| | | were found, errorCode is null. If no results are found, results |
| | | is null and errorCode is not null. |
| | | |
| | | Examples: |
| | | |
| | | //Search for diafygi's key id |
| | | var hkp = new PublicKey(); |
| | | hkp.search("diafygi", function(results, errorCode){ |
| | | errorCode !== null ? console.log(errorCode) : console.log(results); |
| | | }); |
| | | |
| | | //Search for a nonexistent key id |
| | | var hkp = new PublicKey(); |
| | | hkp.search("doesntexist123", function(results, errorCode){ |
| | | errorCode !== null ? console.log(errorCode) : console.log(results); |
| | | }); |
| | | */ |
| | | PublicKey.prototype.search = function(query, callback, keyserverIndex, results, err){ |
| | | //default starting point is at the first keyserver |
| | | if(keyserverIndex === undefined){ |
| | | keyserverIndex = 0; |
| | | } |
| | | |
| | | //initialize the results array |
| | | if(results === undefined){ |
| | | results = []; |
| | | } |
| | | |
| | | //no more keyservers to check |
| | | if(keyserverIndex >= this.keyservers.length){ |
| | | |
| | | //return error if no results |
| | | if(results.length === 0){ |
| | | return callback(null, err || 404); |
| | | } |
| | | |
| | | //return results |
| | | else{ |
| | | |
| | | //merge duplicates |
| | | var merged = {}; |
| | | for(var i = 0; i < results.length; i++){ |
| | | var k = results[i]; |
| | | |
| | | //see if there's duplicate key ids to merge |
| | | if(merged[k['keyid']] !== undefined){ |
| | | |
| | | for(var u = 0; u < k['uids'].length; u++){ |
| | | var has_this_uid = false; |
| | | |
| | | for(var m = 0; m < merged[k['keyid']]['uids'].length; m++){ |
| | | if(merged[k['keyid']]['uids'][m]['uid'] === k['uids'][u]){ |
| | | has_this_uid = true; |
| | | break; |
| | | } |
| | | } |
| | | |
| | | if(!has_this_uid){ |
| | | merged[k['keyid']]['uids'].push(k['uids'][u]) |
| | | } |
| | | } |
| | | } |
| | | |
| | | //no duplicate found, so add it to the dict |
| | | else{ |
| | | merged[k['keyid']] = k; |
| | | } |
| | | } |
| | | |
| | | //return a list of the merged results in the same order |
| | | var merged_list = []; |
| | | for(var i = 0; i < results.length; i++){ |
| | | var k = results[i]; |
| | | if(merged[k['keyid']] !== undefined){ |
| | | merged_list.push(merged[k['keyid']]); |
| | | delete(merged[k['keyid']]); |
| | | } |
| | | } |
| | | return callback(merged_list, null); |
| | | } |
| | | } |
| | | |
| | | //set the keyserver to try next |
| | | var ks = this.keyservers[keyserverIndex]; |
| | | var _this = this; |
| | | |
| | | //special case for keybase |
| | | if(ks.indexOf("https://keybase.io/") === 0){ |
| | | |
| | | //request a list of users from keybase |
| | | var xhr = new XMLHttpRequest(); |
| | | xhr.open("get", "https://keybase.io/_/api/1.0/user/autocomplete.json?q=" + encodeURIComponent(query)); |
| | | xhr.onload = function(){ |
| | | if(xhr.status === 200){ |
| | | var kb_json = JSON.parse(xhr.responseText); |
| | | |
| | | //keybase error returns HTTP 200 status, which is silly |
| | | if(kb_json['status']['code'] !== 0){ |
| | | return _this.search(query, callback, keyserverIndex + 1, results, kb_json['status']['code']); |
| | | } |
| | | |
| | | //no public key found |
| | | if(kb_json['completions'].length === 0){ |
| | | return _this.search(query, callback, keyserverIndex + 1, results, 404); |
| | | } |
| | | |
| | | //compose keybase user results |
| | | var kb_results = []; |
| | | for(var i = 0; i < kb_json['completions'].length; i++){ |
| | | var user = kb_json['completions'][i]['components']; |
| | | |
| | | //skip if no public key fingerprint |
| | | if(user['key_fingerprint'] === undefined){ |
| | | continue; |
| | | } |
| | | |
| | | //build keybase user result |
| | | var kb_result = { |
| | | "keyid": user['key_fingerprint']['val'].toUpperCase(), |
| | | "href": "https://keybase.io/" + user['username']['val'] + "/key.asc", |
| | | "info": "https://keybase.io/" + user['username']['val'], |
| | | "algo": user['key_fingerprint']['algo'], |
| | | "keylen": user['key_fingerprint']['nbits'], |
| | | "creationdate": null, |
| | | "expirationdate": null, |
| | | "revoked": false, |
| | | "disabled": false, |
| | | "expired": false, |
| | | "uids": [{ |
| | | "uid": user['username']['val'] + |
| | | " on Keybase <https://keybase.io/" + |
| | | user['username']['val'] + ">", |
| | | "creationdate": null, |
| | | "expirationdate": null, |
| | | "revoked": false, |
| | | "disabled": false, |
| | | "expired": false, |
| | | }] |
| | | }; |
| | | |
| | | //add full name |
| | | if(user['full_name'] !== undefined){ |
| | | kb_result['uids'].push({ |
| | | "uid": "Full Name: " + user['full_name']['val'], |
| | | "creationdate": null, |
| | | "expirationdate": null, |
| | | "revoked": false, |
| | | "disabled": false, |
| | | "expired": false, |
| | | }); |
| | | } |
| | | |
| | | //add twitter |
| | | if(user['twitter'] !== undefined){ |
| | | kb_result['uids'].push({ |
| | | "uid": user['twitter']['val'] + |
| | | " on Twitter <https://twitter.com/" + |
| | | user['twitter']['val'] + ">", |
| | | "creationdate": null, |
| | | "expirationdate": null, |
| | | "revoked": false, |
| | | "disabled": false, |
| | | "expired": false, |
| | | }); |
| | | } |
| | | |
| | | //add github |
| | | if(user['github'] !== undefined){ |
| | | kb_result['uids'].push({ |
| | | "uid": user['github']['val'] + |
| | | " on Github <https://github.com/" + |
| | | user['github']['val'] + ">", |
| | | "creationdate": null, |
| | | "expirationdate": null, |
| | | "revoked": false, |
| | | "disabled": false, |
| | | "expired": false, |
| | | }); |
| | | } |
| | | |
| | | //add reddit |
| | | if(user['reddit'] !== undefined){ |
| | | kb_result['uids'].push({ |
| | | "uid": user['reddit']['val'] + |
| | | " on Github <https://reddit.com/u/" + |
| | | user['reddit']['val'] + ">", |
| | | "creationdate": null, |
| | | "expirationdate": null, |
| | | "revoked": false, |
| | | "disabled": false, |
| | | "expired": false, |
| | | }); |
| | | } |
| | | |
| | | //add hackernews |
| | | if(user['hackernews'] !== undefined){ |
| | | kb_result['uids'].push({ |
| | | "uid": user['hackernews']['val'] + |
| | | " on Hacker News <https://news.ycombinator.com/user?id=" + |
| | | user['hackernews']['val'] + ">", |
| | | "creationdate": null, |
| | | "expirationdate": null, |
| | | "revoked": false, |
| | | "disabled": false, |
| | | "expired": false, |
| | | }); |
| | | } |
| | | |
| | | //add coinbase |
| | | if(user['coinbase'] !== undefined){ |
| | | kb_result['uids'].push({ |
| | | "uid": user['coinbase']['val'] + |
| | | " on Coinbase <https://www.coinbase.com/" + |
| | | user['coinbase']['val'] + ">", |
| | | "creationdate": null, |
| | | "expirationdate": null, |
| | | "revoked": false, |
| | | "disabled": false, |
| | | "expired": false, |
| | | }); |
| | | } |
| | | |
| | | //add websites |
| | | if(user['websites'] !== undefined){ |
| | | for(var w = 0; w < user['websites'].length; w++){ |
| | | kb_result['uids'].push({ |
| | | "uid": "Owns " + user['websites'][w]['val'], |
| | | "creationdate": null, |
| | | "expirationdate": null, |
| | | "revoked": false, |
| | | "disabled": false, |
| | | "expired": false, |
| | | }); |
| | | } |
| | | } |
| | | |
| | | kb_results.push(kb_result); |
| | | } |
| | | |
| | | results = results.concat(kb_results); |
| | | return _this.search(query, callback, keyserverIndex + 1, results, null); |
| | | } |
| | | else{ |
| | | return _this.search(query, callback, keyserverIndex + 1, results, xhr.status); |
| | | } |
| | | }; |
| | | xhr.send(); |
| | | } |
| | | |
| | | //normal HKP keyserver |
| | | else{ |
| | | var xhr = new XMLHttpRequest(); |
| | | xhr.open("get", ks + "pks/lookup?op=index&options=mr&fingerprint=on&search=" + encodeURIComponent(query)); |
| | | xhr.onload = function(){ |
| | | if(xhr.status === 200){ |
| | | var ks_results = []; |
| | | var raw = xhr.responseText.split("\n"); |
| | | var curKey = undefined; |
| | | for(var i = 0; i < raw.length; i++){ |
| | | var line = raw[i].trim(); |
| | | |
| | | //pub:<keyid>:<algo>:<keylen>:<creationdate>:<expirationdate>:<flags> |
| | | if(line.indexOf("pub:") == 0){ |
| | | if(curKey !== undefined){ |
| | | ks_results.push(curKey); |
| | | } |
| | | var vals = line.split(":"); |
| | | curKey = { |
| | | "keyid": vals[1], |
| | | "href": ks + "pks/lookup?op=get&options=mr&search=0x" + vals[1], |
| | | "info": ks + "pks/lookup?op=vindex&search=0x" + vals[1], |
| | | "algo": vals[2] === "" ? null : parseInt(vals[2]), |
| | | "keylen": vals[3] === "" ? null : parseInt(vals[3]), |
| | | "creationdate": vals[4] === "" ? null : parseInt(vals[4]), |
| | | "expirationdate": vals[5] === "" ? null : parseInt(vals[5]), |
| | | "revoked": vals[6].indexOf("r") !== -1, |
| | | "disabled": vals[6].indexOf("d") !== -1, |
| | | "expired": vals[6].indexOf("e") !== -1, |
| | | "uids": [], |
| | | } |
| | | } |
| | | |
| | | //uid:<escaped uid string>:<creationdate>:<expirationdate>:<flags> |
| | | if(line.indexOf("uid:") == 0){ |
| | | var vals = line.split(":"); |
| | | curKey['uids'].push({ |
| | | "uid": decodeURIComponent(vals[1]), |
| | | "creationdate": vals[2] === "" ? null : parseInt(vals[2]), |
| | | "expirationdate": vals[3] === "" ? null : parseInt(vals[3]), |
| | | "revoked": vals[4].indexOf("r") !== -1, |
| | | "disabled": vals[4].indexOf("d") !== -1, |
| | | "expired": vals[4].indexOf("e") !== -1, |
| | | }); |
| | | } |
| | | } |
| | | ks_results.push(curKey); |
| | | |
| | | results = results.concat(ks_results); |
| | | return _this.search(query, callback, keyserverIndex + 1, results, null); |
| | | } |
| | | else{ |
| | | return _this.search(query, callback, keyserverIndex + 1, results, xhr.status); |
| | | } |
| | | }; |
| | | xhr.send(); |
| | | } |
| | | }; |
| | | |
| | | context.PublicKey = PublicKey; |
| | | })(typeof exports === "undefined" ? this : exports); |
| | |
| | | $labels['addimage'] = 'Add image'; |
| | | $labels['selectmedia'] = 'Select movie'; |
| | | $labels['addmedia'] = 'Add movie'; |
| | | $labels['encrypt'] = 'Encrypt'; |
| | | $labels['encryptmessage'] = 'Encrypt message'; |
| | | $labels['importpubkeys'] = 'Import public keys'; |
| | | $labels['encryptedsendialog'] = 'Sending encrypted message'; |
| | | $labels['keyid'] = 'Key ID'; |
| | | $labels['keylength'] = 'Bits'; |
| | | $labels['keyexpired'] = 'Expired'; |
| | | $labels['keyrevoked'] = 'Revoked'; |
| | | |
| | | $labels['editidents'] = 'Edit identities'; |
| | | $labels['spellcheck'] = 'Spell'; |
| | |
| | | $messages['contactnameexists'] = 'A contact with the same name already exists.'; |
| | | $messages['blockedimages'] = 'To protect your privacy, remote images are blocked in this message.'; |
| | | $messages['encryptedmessage'] = 'This is an encrypted message and can not be displayed. Sorry!'; |
| | | $messages['externalmessagedecryption'] = 'This is an encrypted message and can be decrypted with your browser extension.'; |
| | | $messages['nopubkeyfor'] = 'No valid public key found for $email'; |
| | | $messages['nopubkeyforsender'] = 'No valid public key found for your sender identity. Do you want to encrypt the message for the recipients only?'; |
| | | $messages['encryptnoattachments'] = 'Already uploaded attachments cannot be encrypted. Please re-add them in the encryption editor.'; |
| | | $messages['searchpubkeyservers'] = 'Do you want to search public key servers for the missing keys?'; |
| | | $messages['encryptpubkeysfound'] = 'The following public keys have been found:'; |
| | | $messages['nocontactsfound'] = 'No contacts found.'; |
| | | $messages['contactnotfound'] = 'The requested contact was not found.'; |
| | | $messages['contactsearchonly'] = 'Enter some search terms to find contacts'; |
| | |
| | | 'messagesaved', 'converting', 'editorwarning', 'searching', 'uploading', 'uploadingmany', |
| | | 'fileuploaderror', 'sendmessage', 'newresponse', 'responsename', 'responsetext', 'save', |
| | | 'savingresponse', 'restoresavedcomposedata', 'restoremessage', 'delete', 'restore', 'ignore', |
| | | 'selectimportfile', 'messageissent'); |
| | | 'selectimportfile', 'messageissent', 'loadingdata', 'nopubkeyfor', 'nopubkeyforsender', |
| | | 'encryptnoattachments','encryptedsendialog','searchpubkeyservers', 'importpubkeys', |
| | | 'encryptpubkeysfound', 'search', 'close', 'import', 'keyid', 'keylength', 'keyexpired', |
| | | 'keyrevoked'); |
| | | |
| | | $OUTPUT->set_pagetitle($RCMAIL->gettext('compose')); |
| | | |
| | |
| | | if (!empty($sql_arr['bcc'])) { |
| | | $identities[$identity_id]['bcc'] = $sql_arr['bcc']; |
| | | } |
| | | |
| | | $identities[$identity_id]['email'] = $sql_arr['email']; |
| | | } |
| | | |
| | | $out = $select_from->show($MESSAGE->compose['from']); |
| | |
| | | } |
| | | |
| | | foreach ($MESSAGE->parts as $part) { |
| | | if ($part->realtype == 'multipart/encrypted') { |
| | | // find the encrypted message payload part |
| | | foreach ($MESSAGE->mime_parts as $mime_id => $mpart) { |
| | | if ($mpart->mimetype == 'application/octet-stream' || !empty($mpart->filename)) { |
| | | $RCMAIL->output->set_env('pgp_mime_message', array( |
| | | '_mbox' => $RCMAIL->storage->get_folder(), '_uid' => $MESSAGE->uid, '_part' => $mime_id, |
| | | )); |
| | | $RCMAIL->output->set_env('compose_mode', $compose_mode); |
| | | $MESSAGE->pgp_mime = true; |
| | | break; |
| | | } |
| | | } |
| | | continue; |
| | | } |
| | | |
| | | // skip no-content and attachment parts (#1488557) |
| | | if ($part->type != 'content' || !$part->size || $MESSAGE->is_attachment($part)) { |
| | | continue; |
| | |
| | | } |
| | | |
| | | // compose reply-body |
| | | if ($COMPOSE['mode'] == RCUBE_COMPOSE_REPLY) |
| | | if ($COMPOSE['mode'] == RCUBE_COMPOSE_REPLY) { |
| | | $body = rcmail_create_reply_body($body, $isHtml); |
| | | |
| | | if ($MESSAGE->pgp_mime) { |
| | | $RCMAIL->output->set_env('compose_reply_header', rcmail_get_reply_header($MESSAGE)); |
| | | } |
| | | } |
| | | // forward message body inline |
| | | else if ($COMPOSE['mode'] == RCUBE_COMPOSE_FORWARD) |
| | | else if ($COMPOSE['mode'] == RCUBE_COMPOSE_FORWARD) { |
| | | $body = rcmail_create_forward_body($body, $isHtml); |
| | | } |
| | | // load draft message body |
| | | else if ($COMPOSE['mode'] == RCUBE_COMPOSE_DRAFT || $COMPOSE['mode'] == RCUBE_COMPOSE_EDIT) |
| | | else if ($COMPOSE['mode'] == RCUBE_COMPOSE_DRAFT || $COMPOSE['mode'] == RCUBE_COMPOSE_EDIT) { |
| | | $body = rcmail_create_draft_body($body, $isHtml); |
| | | } |
| | | } |
| | | else { // new message |
| | | $isHtml = rcmail_compose_editor_mode(); |
| | |
| | | |
| | | function rcmail_compose_part_body($part, $isHtml = false) |
| | | { |
| | | global $RCMAIL, $MESSAGE, $LINE_LENGTH; |
| | | global $RCMAIL, $COMPOSE, $MESSAGE, $LINE_LENGTH; |
| | | |
| | | // Check if we have enough memory to handle the message in it |
| | | // #1487424: we need up to 10x more memory than the body |
| | |
| | | // message is cached but not exists (#1485443), or other error |
| | | if ($body === false) { |
| | | return ''; |
| | | } |
| | | |
| | | // register this part as pgp encrypted |
| | | if (strpos($body, 'BEGIN PGP MESSAGE') !== false) { |
| | | $MESSAGE->pgp_mime = true; |
| | | $RCMAIL->output->set_env('pgp_mime_message', array( |
| | | '_mbox' => $RCMAIL->storage->get_folder(), '_uid' => $MESSAGE->uid, '_part' => $part->mime_id, |
| | | )); |
| | | } |
| | | |
| | | if ($isHtml) { |
| | |
| | | { |
| | | global $RCMAIL, $MESSAGE, $LINE_LENGTH; |
| | | |
| | | // build reply prefix |
| | | $from = array_pop(rcube_mime::decode_address_list($MESSAGE->get_header('from'), 1, false, $MESSAGE->headers->charset)); |
| | | $prefix = $RCMAIL->gettext(array( |
| | | 'name' => 'mailreplyintro', |
| | | 'vars' => array( |
| | | 'date' => $RCMAIL->format_date($MESSAGE->headers->date, $RCMAIL->config->get('date_long')), |
| | | 'sender' => $from['name'] ? $from['name'] : rcube_utils::idn_to_utf8($from['mailto']), |
| | | ) |
| | | )); |
| | | |
| | | $prefix = rcmail_get_reply_header($MESSAGE); |
| | | $reply_mode = intval($RCMAIL->config->get('reply_mode')); |
| | | |
| | | if (!$bodyIsHtml) { |
| | |
| | | return $prefix . $body . $suffix; |
| | | } |
| | | |
| | | function rcmail_get_reply_header($message) |
| | | { |
| | | global $RCMAIL; |
| | | |
| | | $from = array_pop(rcube_mime::decode_address_list($message->get_header('from'), 1, false, $message->headers->charset)); |
| | | return $RCMAIL->gettext(array( |
| | | 'name' => 'mailreplyintro', |
| | | 'vars' => array( |
| | | 'date' => $RCMAIL->format_date($message->headers->date, $RCMAIL->config->get('date_long')), |
| | | 'sender' => $from['name'] ?: rcube_utils::idn_to_utf8($from['mailto']), |
| | | ) |
| | | )); |
| | | } |
| | | |
| | | function rcmail_create_forward_body($body, $bodyIsHtml) |
| | | { |
| | |
| | | $cid_map = array(); |
| | | $messages = array(); |
| | | |
| | | if ($message->pgp_mime) { |
| | | return $cid_map; |
| | | } |
| | | |
| | | foreach ((array)$message->mime_parts as $pid => $part) { |
| | | if ($part->disposition == 'attachment' || ($part->disposition == 'inline' && $bodyIsHtml) || $part->filename) { |
| | | // skip parts that aren't valid attachments |
| | |
| | | global $RCMAIL, $COMPOSE; |
| | | |
| | | $cid_map = array(); |
| | | |
| | | if ($message->pgp_mime) { |
| | | return $cid_map; |
| | | } |
| | | |
| | | foreach ((array)$message->mime_parts as $pid => $part) { |
| | | if (($part->content_id || $part->content_location) && $part->filename) { |
| | | if ($attachment = rcmail_save_attachment($message, $pid)) { |
| | |
| | | $names = array(); |
| | | $refs = array(); |
| | | |
| | | if ($MESSAGE->pgp_mime) { |
| | | return; |
| | | } |
| | | |
| | | $loaded_attachments = array(); |
| | | foreach ((array)$COMPOSE['attachments'] as $attachment) { |
| | | $loaded_attachments[$attachment['name'] . $attachment['mimetype']] = $attachment; |
| | |
| | | // unsupported (e.g. encrypted) |
| | | if ($part->realtype) { |
| | | if ($part->realtype == 'multipart/encrypted' || $part->realtype == 'application/pkcs7-mime') { |
| | | if (!empty($_SESSION['browser_caps']['pgpmime']) && $part->realtype == 'multipart/encrypted') { |
| | | // find the encrypted message payload part |
| | | foreach ($MESSAGE->mime_parts as $mime_id => $mpart) { |
| | | if ($mpart->mimetype == 'application/octet-stream' || !empty($mpart->filename)) { |
| | | $out .= html::span('part-notice', $RCMAIL->gettext('externalmessagedecryption')); |
| | | $OUTPUT->set_env('pgp_mime_part', $mime_id); |
| | | $OUTPUT->set_env('pgp_mime_container', '#' . $attrib['id']); |
| | | $OUTPUT->add_label('loadingdata'); |
| | | $MESSAGE->encrypted_part = $mime_id; |
| | | break; |
| | | } |
| | | } |
| | | } |
| | | |
| | | if (!$MESSAGE->encrypted_part) { |
| | | $out .= html::span('part-notice', $RCMAIL->gettext('encryptedmessage')); |
| | | } |
| | | } |
| | | continue; |
| | | } |
| | |
| | | rcmail_message_error($MESSAGE->uid); |
| | | } |
| | | |
| | | // check if the message body is PGP encrypted |
| | | if (strpos($body, 'BEGIN PGP MESSAGE') !== false) { |
| | | $OUTPUT->set_env('is_pgp_content', '#' . $attrib['id']); |
| | | } |
| | | |
| | | $plugin = $RCMAIL->plugins->exec_hook('message_body_prefix', |
| | | array('part' => $part, 'prefix' => '')); |
| | | |
| | |
| | | // fetch message body |
| | | $message_body = rcube_utils::get_input_value('_message', rcube_utils::INPUT_POST, TRUE, $message_charset); |
| | | |
| | | if (isset($_POST['_pgpmime'])) { |
| | | $pgp_mime = rcube_utils::get_input_value('_pgpmime', rcube_utils::INPUT_POST); |
| | | $message_body = 'This is an OpenPGP/MIME encrypted message (RFC 2440 and 3156)'; |
| | | $isHtml = false; |
| | | |
| | | // clear unencrypted attachments |
| | | foreach ($COMPOSE['attachments'] as $attach) { |
| | | $RCMAIL->plugins->exec_hook('attachment_delete', $attach); |
| | | } |
| | | $COMPOSE['attachments'] = array(); |
| | | } |
| | | |
| | | if ($isHtml) { |
| | | $bstyle = array(); |
| | | |
| | |
| | | $text_charset .= ";\r\n format=flowed"; |
| | | } |
| | | |
| | | // compose PGP/Mime message |
| | | if ($pgp_mime) { |
| | | $MAIL_MIME->addAttachment( |
| | | 'Version: 1', |
| | | 'application/pgp-encrypted', |
| | | 'version.txt', // required by Mail_mime::addAttachment() |
| | | false, |
| | | '8bit', |
| | | '', // $disposition |
| | | '', // $charset |
| | | '', // $language |
| | | '', // $location |
| | | null, // $n_encoding |
| | | null, // $f_encoding |
| | | 'PGP/MIME version identification' |
| | | ); |
| | | |
| | | // patch filename out of the version part |
| | | foreach ($MAIL_MIME->_parts as $_i => $_part) { |
| | | if ($_part['c_type'] == 'application/pgp-encrypted') { |
| | | $MAIL_MIME->_parts[$_i]['name'] = ''; |
| | | break; |
| | | } |
| | | } |
| | | |
| | | $MAIL_MIME->addAttachment( |
| | | $pgp_mime, |
| | | 'application/octet-stream', |
| | | 'encrypted.asc', |
| | | false, |
| | | '8bit', |
| | | 'inline' |
| | | ); |
| | | |
| | | $MAIL_MIME->setContentType('multipart/encrypted', array('protocol' => "application/pgp-encrypted")); |
| | | } |
| | | |
| | | // encoding settings for mail composing |
| | | $MAIL_MIME->setParam('text_encoding', $transfer_encoding); |
| | | $MAIL_MIME->setParam('html_encoding', 'quoted-printable'); |
| | |
| | | margin: 8px; |
| | | } |
| | | |
| | | #messagebody.mailvelope > iframe { |
| | | width: 99% !important; |
| | | } |
| | | |
| | | #message-objects div, |
| | | #messagebody span.part-notice { |
| | | margin: 8px; |
| | |
| | | bottom: 42px; |
| | | } |
| | | |
| | | #composebodycontainer.mailvelope { |
| | | right: 0; |
| | | z-index: 10; |
| | | } |
| | | |
| | | #composebodycontainer.mailvelope > iframe[scrolling='no'] { |
| | | position: relative; |
| | | top: -12px; |
| | | } |
| | | |
| | | #composebody { |
| | | position: absolute; |
| | | top: 0; |
| | |
| | | #uploadform form div { |
| | | margin: 4px 0; |
| | | } |
| | | |
| | | .mailvelopekeyimport div.key { |
| | | position: relative; |
| | | margin-bottom: 2px; |
| | | padding: 1em; |
| | | background-color: #ebebeb; |
| | | } |
| | | |
| | | .mailvelopekeyimport div.key.revoked, |
| | | .mailvelopekeyimport div.key.disabled { |
| | | color: #a0a0a0; |
| | | } |
| | | |
| | | .mailvelopekeyimport div.key label { |
| | | display: inline-block; |
| | | margin-right: 0.5em; |
| | | } |
| | | |
| | | .mailvelopekeyimport div.key label:after { |
| | | content: ":"; |
| | | } |
| | | |
| | | .mailvelopekeyimport div.key label + a, |
| | | .mailvelopekeyimport div.key label + span { |
| | | display: inline-block; |
| | | margin-right: 2em; |
| | | white-space: nowrap; |
| | | } |
| | | |
| | | .mailvelopekeyimport div.key label + a { |
| | | font-weight: bold; |
| | | } |
| | | |
| | | .mailvelopekeyimport ul.uids { |
| | | margin: 1em 0 0 0; |
| | | padding: 0; |
| | | } |
| | | |
| | | .mailvelopekeyimport li.uid { |
| | | border: 0; |
| | | padding: 0.3em; |
| | | } |
| | | |
| | | .mailvelopekeyimport div.key input.button.importkey { |
| | | position: absolute; |
| | | top: 0.8em; |
| | | right: 0.8em; |
| | | padding: 4px 6px; |
| | | } |
| | | |
| | | .mailvelopekeyimport div.key input.button[disabled] { |
| | | display: none; |
| | | } |
| | |
| | | opacity: 0.4; |
| | | } |
| | | |
| | | .toolbar a.button.selected { |
| | | color: #1978a1; |
| | | } |
| | | |
| | | .toolbar a.button.hidden { |
| | | display: none; |
| | | } |
| | | |
| | | .dropbutton { |
| | | display: inline-block; |
| | | position: relative; |
| | |
| | | background-position: center -1932px; |
| | | } |
| | | |
| | | .toolbar a.button.encrypt { |
| | | background-position: center -2025px; |
| | | } |
| | | |
| | | .toolbar a.button.encrypt.selected { |
| | | background-position: center -2065px; |
| | | } |
| | | |
| | | a.menuselector { |
| | | display: inline-block; |
| | | border: 1px solid #ababab; |
| | |
| | | <roundcube:button name="addattachment" type="link" class="button attach" label="attach" title="addattachment" onclick="UI.show_uploadform(event);return false" aria-haspopup="true" aria-expanded="false"tabindex="2" /> |
| | | <roundcube:button command="insert-sig" type="link" class="button insertsig disabled" classAct="button insertsig" label="signature" title="insertsignature" tabindex="2" /> |
| | | <a href="#responses" class="button responses" label="responses" title="<roundcube:label name='insertresponse' />" id="responsesmenulink" unselectable="on" onmousedown="return false" onclick="UI.toggle_popup('responsesmenu',event);return false" tabindex="2" aria-haspopup="true" aria-expanded="false" aria-owns="textresponsesmenu"><roundcube:label name="responses" /></a> |
| | | <roundcube:button command="compose-encrypted" type="link" class="button encrypt hidden" classAct="button encrypt" classSel="button encrypt selected" label="encrypt" title="encryptmessage" tabindex="2" /> |
| | | <roundcube:container name="toolbar" id="compose-toolbar" /> |
| | | </div> |
| | | |
| | |
| | | $('div.rightcol').hide().attr('aria-hidden', 'true'); |
| | | $('div.leftcol').css('margin-right', '0'); |
| | | } |
| | | |
| | | var mvlpe = $('#messagebody.mailvelope'); |
| | | if (mvlpe.length) { |
| | | var h = $('#messagecontent').length ? |
| | | $('#messagecontent').height() - 16 : |
| | | $(window).height() - mvlpe.offset().top - 10; |
| | | mvlpe.height(h); |
| | | } |
| | | } |
| | | |
| | | |