mirror of
https://github.com/the-djmaze/snappymail.git
synced 2026-07-13 03:27:39 +03:00
#89 also encrypt draft on save
This commit is contained in:
parent
384230f7bf
commit
d7ed6ce423
1 changed files with 143 additions and 233 deletions
|
|
@ -337,9 +337,33 @@ class ComposePopupView extends AbstractViewPopup {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
getMessageRequestParams(sSaveFolder)
|
async getMessageRequestParams(sSaveFolder, draft)
|
||||||
{
|
{
|
||||||
let TextIsHtml = this.oEditor.isHtml(),
|
let
|
||||||
|
identity = this.currentIdentity(),
|
||||||
|
params = {
|
||||||
|
IdentityID: identity.id(),
|
||||||
|
MessageFolder: this.draftsFolder(),
|
||||||
|
MessageUid: this.draftUid(),
|
||||||
|
SaveFolder: sSaveFolder,
|
||||||
|
To: this.to(),
|
||||||
|
Cc: this.cc(),
|
||||||
|
Bcc: this.bcc(),
|
||||||
|
ReplyTo: this.replyTo(),
|
||||||
|
Subject: this.subject(),
|
||||||
|
DraftInfo: this.aDraftInfo,
|
||||||
|
InReplyTo: this.sInReplyTo,
|
||||||
|
References: this.sReferences,
|
||||||
|
MarkAsImportant: this.markAsImportant() ? 1 : 0,
|
||||||
|
Attachments: this.prepareAttachmentsForSendOrSave(),
|
||||||
|
// Only used at send, not at save:
|
||||||
|
Dsn: this.requestDsn() ? 1 : 0,
|
||||||
|
ReadReceiptRequest: this.requestReadReceipt() ? 1 : 0
|
||||||
|
},
|
||||||
|
recipients = draft ? [identity.email()] : this.allRecipients(),
|
||||||
|
sign = !draft && this.pgpSign() && this.canPgpSign(),
|
||||||
|
encrypt = this.pgpEncrypt() && this.canPgpEncrypt(),
|
||||||
|
TextIsHtml = this.oEditor.isHtml(),
|
||||||
Text = this.oEditor.getData(true);
|
Text = this.oEditor.getData(true);
|
||||||
if (TextIsHtml) {
|
if (TextIsHtml) {
|
||||||
let l;
|
let l;
|
||||||
|
|
@ -352,27 +376,37 @@ class ComposePopupView extends AbstractViewPopup {
|
||||||
.replace(/(<[^>]+)\s+data-hs-[a-z-]+=("[^"]+"|'[^']+')/gi, '$1');
|
.replace(/(<[^>]+)\s+data-hs-[a-z-]+=("[^"]+"|'[^']+')/gi, '$1');
|
||||||
} while (l != Text.length)
|
} while (l != Text.length)
|
||||||
}
|
}
|
||||||
return {
|
if (this.mailvelope && 'mailvelope' === this.viewArea()) {
|
||||||
IdentityID: this.currentIdentity() ? this.currentIdentity().id() : '',
|
params.Encrypted = draft
|
||||||
MessageFolder: this.draftsFolder(),
|
? await this.mailvelope.createDraft()
|
||||||
MessageUid: this.draftUid(),
|
: await this.mailvelope.encrypt(recipients);
|
||||||
SaveFolder: sSaveFolder,
|
} else if (encrypt) {
|
||||||
To: this.to(),
|
if ('openpgp' != encrypt) {
|
||||||
Cc: this.cc(),
|
throw 'Encryption with ' + encrypt + ' not yet implemented';
|
||||||
Bcc: this.bcc(),
|
}
|
||||||
ReplyTo: this.replyTo(),
|
if (sign && 'openpgp' != sign[0]) {
|
||||||
Subject: this.subject(),
|
throw 'Signing with ' + sign[0] + ' not yet implemented';
|
||||||
Html: TextIsHtml ? Text : '',
|
}
|
||||||
Text: TextIsHtml ? '' : Text,
|
if (sign && sign[1]) {
|
||||||
DraftInfo: this.aDraftInfo,
|
if (TextIsHtml) {
|
||||||
InReplyTo: this.sInReplyTo,
|
throw 'Encrypt HTML with ' + encrypt + ' not yet implemented';
|
||||||
References: this.sReferences,
|
}
|
||||||
MarkAsImportant: this.markAsImportant() ? 1 : 0,
|
Text = await OpenPGPUserStore.encrypt(Text, recipients, sign[1]);
|
||||||
Attachments: this.prepareAttachmentsForSendOrSave(),
|
// throw i18n('PGP_NOTIFICATIONS/PGP_ERROR', { ERROR: 'Encryption failed' })
|
||||||
// Only used at send, not at save:
|
} else {
|
||||||
Dsn: this.requestDsn() ? 1 : 0,
|
let data = [
|
||||||
ReadReceiptRequest: this.requestReadReceipt() ? 1 : 0
|
'Content-Transfer-Encoding: base64',
|
||||||
};
|
'Content-Type: text/'+(TextIsHtml?'html':'plain')+'; charset="utf-8"',
|
||||||
|
'',
|
||||||
|
base64_encode(Text)
|
||||||
|
].join("\r\n");
|
||||||
|
params.Encrypted = await OpenPGPUserStore.encrypt(data, recipients, sign && sign[1]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
params.Html = TextIsHtml ? Text : '';
|
||||||
|
params.Text = TextIsHtml ? '' : Text;
|
||||||
|
}
|
||||||
|
return params;
|
||||||
}
|
}
|
||||||
|
|
||||||
sendCommand() {
|
sendCommand() {
|
||||||
|
|
@ -423,14 +457,7 @@ class ComposePopupView extends AbstractViewPopup {
|
||||||
|
|
||||||
sSentFolder = UNUSED_OPTION_VALUE === sSentFolder ? '' : sSentFolder;
|
sSentFolder = UNUSED_OPTION_VALUE === sSentFolder ? '' : sSentFolder;
|
||||||
|
|
||||||
setFolderHash(this.draftsFolder(), '');
|
this.getMessageRequestParams(sSentFolder).then(params => {
|
||||||
setFolderHash(sSentFolder, '');
|
|
||||||
|
|
||||||
const
|
|
||||||
params = this.getMessageRequestParams(sSentFolder),
|
|
||||||
sign = this.pgpSign() && this.canPgpSign(),
|
|
||||||
encrypt = this.pgpEncrypt() && this.canPgpEncrypt(),
|
|
||||||
send = () =>
|
|
||||||
Remote.request('SendMessage',
|
Remote.request('SendMessage',
|
||||||
(iError, data) => {
|
(iError, data) => {
|
||||||
this.sending(false);
|
this.sending(false);
|
||||||
|
|
@ -448,123 +475,19 @@ class ComposePopupView extends AbstractViewPopup {
|
||||||
this.closeCommand();
|
this.closeCommand();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
setFolderHash(this.draftsFolder(), '');
|
||||||
|
setFolderHash(sSentFolder, '');
|
||||||
this.reloadDraftFolder();
|
this.reloadDraftFolder();
|
||||||
},
|
},
|
||||||
params,
|
params,
|
||||||
30000
|
30000
|
||||||
);
|
);
|
||||||
|
}).catch(e => {
|
||||||
if (this.mailvelope && 'mailvelope' === this.viewArea()) {
|
console.error(e);
|
||||||
this.mailvelope.encrypt(this.allRecipients()).then(armored => {
|
|
||||||
params.Text = params.Html = '';
|
|
||||||
params.Encrypted = armored;
|
|
||||||
send();
|
|
||||||
});
|
|
||||||
} else if (encrypt) {
|
|
||||||
if ('openpgp' != encrypt) {
|
|
||||||
throw 'Encryption with ' + encrypt + ' not yet implemented';
|
|
||||||
}
|
|
||||||
if (sign && 'openpgp' != sign[0]) {
|
|
||||||
throw 'Signing with ' + sign[0] + ' not yet implemented';
|
|
||||||
}
|
|
||||||
if (sign && sign[1]) {
|
|
||||||
if (params.Html) {
|
|
||||||
throw 'Encrypt HTML with ' + encrypt + ' not yet implemented';
|
|
||||||
}
|
|
||||||
OpenPGPUserStore.encrypt(params.Text, this.allRecipients(), sign[1]).then(armored => {
|
|
||||||
if (armored) {
|
|
||||||
params.Text = armored;
|
|
||||||
send();
|
|
||||||
} else {
|
|
||||||
this.sendError(true);
|
this.sendError(true);
|
||||||
this.sendErrorDesc(i18n('PGP_NOTIFICATIONS/PGP_ERROR', { ERROR: 'Encryption failed' }));
|
this.sendErrorDesc(e);
|
||||||
this.sending(false);
|
this.sending(false);
|
||||||
}
|
|
||||||
});
|
});
|
||||||
} else {
|
|
||||||
let data = [
|
|
||||||
'Content-Transfer-Encoding: base64',
|
|
||||||
'Content-Type: text/'+(params.Html?'html':'plain')+'; charset="utf-8"',
|
|
||||||
'',
|
|
||||||
base64_encode(params.Html || params.Text)
|
|
||||||
].join("\r\n");
|
|
||||||
OpenPGPUserStore.encrypt(data, this.allRecipients(), sign && sign[1]).then(armored => {
|
|
||||||
if (armored) {
|
|
||||||
params.Text = params.Html = '';
|
|
||||||
params.Encrypted = armored;
|
|
||||||
send();
|
|
||||||
} else {
|
|
||||||
this.sendError(true);
|
|
||||||
this.sendErrorDesc(i18n('PGP_NOTIFICATIONS/PGP_ERROR', { ERROR: 'Encryption failed' }));
|
|
||||||
this.sending(false);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} else if (sign) {
|
|
||||||
if (params.Html) {
|
|
||||||
throw 'Signing HTML with ' + sign[0] + ' not yet implemented';
|
|
||||||
}
|
|
||||||
if ('openpgp' != sign[0]) {
|
|
||||||
throw 'Signing with ' + sign[0] + ' not yet implemented';
|
|
||||||
}
|
|
||||||
const detached = false;
|
|
||||||
if (detached) {
|
|
||||||
// Append headers
|
|
||||||
params.Text = [
|
|
||||||
'Content-Type: text/plain; charset="utf-8"; protected-headers="v1"',
|
|
||||||
'Content-Transfer-Encoding: base64',
|
|
||||||
// 'From: Demo <demo@snappymail.eu>',
|
|
||||||
// 'To: Demo <demo@snappymail.eu>',
|
|
||||||
// 'Subject: text detached signed'
|
|
||||||
''
|
|
||||||
]
|
|
||||||
// Now the body in base64
|
|
||||||
.concat(base64_encode(params.Text))
|
|
||||||
.join("\r\n");
|
|
||||||
}
|
|
||||||
OpenPGPUserStore.sign(params.Text, sign[1]).then(text => {
|
|
||||||
if (text) {
|
|
||||||
if (detached) {
|
|
||||||
params.Signature = text;
|
|
||||||
} else {
|
|
||||||
params.Text = text;
|
|
||||||
}
|
|
||||||
send();
|
|
||||||
} else {
|
|
||||||
this.sendError(true);
|
|
||||||
this.sendErrorDesc(i18n('PGP_NOTIFICATIONS/PGP_ERROR', { ERROR: 'Signing failed' }));
|
|
||||||
this.sending(false);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
send();
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
if (encrypt && sign && encrypt != sign) {
|
|
||||||
// error 'sign and encrypt must be same engine';
|
|
||||||
} else if ('openpgp' == encrypt) {
|
|
||||||
this.allRecipients().forEach(recEmail => {
|
|
||||||
cfg.publicKeys = cfg.publicKeys.concat(OpenPGPUserStore.getPublicKeyFor(recEmail));
|
|
||||||
});
|
|
||||||
pgpPromise = openpgp.encrypt(cfg);
|
|
||||||
} else if ('openpgp' == sign) {
|
|
||||||
pgpPromise = openpgp.sign(cfg);
|
|
||||||
} else {
|
|
||||||
params.Sign = sign;
|
|
||||||
params.Encrypt = encrypt;
|
|
||||||
}
|
|
||||||
pgpPromise
|
|
||||||
? pgpPromise
|
|
||||||
.then(mData => {
|
|
||||||
params.Text = mData.data;
|
|
||||||
send();
|
|
||||||
})
|
|
||||||
.catch(e => {
|
|
||||||
this.sendError(true);
|
|
||||||
this.sendErrorDesc(i18n('PGP_NOTIFICATIONS/PGP_ERROR', { ERROR: '' + e }));
|
|
||||||
})
|
|
||||||
: send();
|
|
||||||
*/
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e);
|
console.error(e);
|
||||||
this.sendError(true);
|
this.sendError(true);
|
||||||
|
|
@ -580,14 +503,8 @@ class ComposePopupView extends AbstractViewPopup {
|
||||||
} else {
|
} else {
|
||||||
this.savedError(false);
|
this.savedError(false);
|
||||||
this.saving(true);
|
this.saving(true);
|
||||||
|
|
||||||
this.autosaveStart();
|
this.autosaveStart();
|
||||||
|
this.getMessageRequestParams(FolderUserStore.draftsFolder(), 1).then(params => {
|
||||||
setFolderHash(FolderUserStore.draftsFolder(), '');
|
|
||||||
|
|
||||||
const
|
|
||||||
params = this.getMessageRequestParams(FolderUserStore.draftsFolder()),
|
|
||||||
save = () =>
|
|
||||||
Remote.request('SaveMessage',
|
Remote.request('SaveMessage',
|
||||||
(iError, oData) => {
|
(iError, oData) => {
|
||||||
let result = false;
|
let result = false;
|
||||||
|
|
@ -613,6 +530,7 @@ class ComposePopupView extends AbstractViewPopup {
|
||||||
if (this.bFromDraft) {
|
if (this.bFromDraft) {
|
||||||
setFolderHash(this.draftsFolder(), '');
|
setFolderHash(this.draftsFolder(), '');
|
||||||
}
|
}
|
||||||
|
setFolderHash(FolderUserStore.draftsFolder(), '');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -626,20 +544,10 @@ class ComposePopupView extends AbstractViewPopup {
|
||||||
params,
|
params,
|
||||||
200000
|
200000
|
||||||
);
|
);
|
||||||
|
|
||||||
if (this.mailvelope && 'mailvelope' === this.viewArea()) {
|
|
||||||
this.mailvelope.createDraft().then(armored => {
|
|
||||||
params.Text = armored;
|
|
||||||
save();
|
|
||||||
});
|
});
|
||||||
} else {
|
|
||||||
save();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
deleteCommand() {
|
deleteCommand() {
|
||||||
if (!isPopupVisible(AskPopupView) && this.modalVisibility()) {
|
if (!isPopupVisible(AskPopupView) && this.modalVisibility()) {
|
||||||
showScreenPopup(AskPopupView, [
|
showScreenPopup(AskPopupView, [
|
||||||
|
|
@ -1229,13 +1137,13 @@ class ComposePopupView extends AbstractViewPopup {
|
||||||
// initUploader
|
// initUploader
|
||||||
|
|
||||||
if (this.composeUploaderButton()) {
|
if (this.composeUploaderButton()) {
|
||||||
const uploadCache = {},
|
const oJua = new Jua({
|
||||||
attachmentSizeLimit = pInt(SettingsGet('AttachmentLimit')),
|
|
||||||
oJua = new Jua({
|
|
||||||
action: serverRequest('Upload'),
|
action: serverRequest('Upload'),
|
||||||
clickElement: this.composeUploaderButton(),
|
clickElement: this.composeUploaderButton(),
|
||||||
dragAndDropElement: this.composeUploaderDropPlace()
|
dragAndDropElement: this.composeUploaderDropPlace()
|
||||||
});
|
}),
|
||||||
|
uploadCache = {},
|
||||||
|
attachmentSizeLimit = pInt(SettingsGet('AttachmentLimit'));
|
||||||
|
|
||||||
oJua
|
oJua
|
||||||
// .on('onLimitReached', (limit) => {
|
// .on('onLimitReached', (limit) => {
|
||||||
|
|
@ -1593,12 +1501,14 @@ class ComposePopupView extends AbstractViewPopup {
|
||||||
}
|
}
|
||||||
|
|
||||||
mailvelopeArea() {
|
mailvelopeArea() {
|
||||||
|
if (!this.mailvelope) {
|
||||||
/**
|
/**
|
||||||
* Creates an iframe with an editor for a new encrypted mail.
|
* Creates an iframe with an editor for a new encrypted mail.
|
||||||
* The iframe will be injected into the container identified by selector.
|
* The iframe will be injected into the container identified by selector.
|
||||||
* https://mailvelope.github.io/mailvelope/Editor.html
|
* https://mailvelope.github.io/mailvelope/Editor.html
|
||||||
*/
|
*/
|
||||||
let text = this.oEditor.getData(true),
|
let text = this.oEditor.getData(true),
|
||||||
|
encrypted = text.includes('-----BEGIN PGP MESSAGE-----'),
|
||||||
size = SettingsGet('PhpUploadSizes')['post_max_size'],
|
size = SettingsGet('PhpUploadSizes')['post_max_size'],
|
||||||
quota = pInt(size);
|
quota = pInt(size);
|
||||||
switch (size.slice(-1)) {
|
switch (size.slice(-1)) {
|
||||||
|
|
@ -1606,20 +1516,20 @@ class ComposePopupView extends AbstractViewPopup {
|
||||||
case 'M': quota *= 1024; // fallthrough
|
case 'M': quota *= 1024; // fallthrough
|
||||||
case 'K': quota *= 1024;
|
case 'K': quota *= 1024;
|
||||||
}
|
}
|
||||||
this.mailvelope ||
|
|
||||||
mailvelope.createEditorContainer('#mailvelope-editor', PgpUserStore.mailvelopeKeyring, {
|
mailvelope.createEditorContainer('#mailvelope-editor', PgpUserStore.mailvelopeKeyring, {
|
||||||
// https://mailvelope.github.io/mailvelope/global.html#EditorContainerOptions
|
// https://mailvelope.github.io/mailvelope/global.html#EditorContainerOptions
|
||||||
quota: Math.max(2048, (quota / 1024)) - 48, // (text + attachments) limit in kilobytes
|
quota: Math.max(2048, (quota / 1024)) - 48, // (text + attachments) limit in kilobytes
|
||||||
predefinedText: this.oEditor.isHtml() ? htmlToPlain(text) : text
|
armoredDraft: encrypted ? text : '', // Ascii Armored PGP Text Block
|
||||||
|
predefinedText: encrypted ? '' : (this.oEditor.isHtml() ? htmlToPlain(text) : text),
|
||||||
/*
|
/*
|
||||||
signMsg: false, // if true then the mail will be signed (default: false)
|
|
||||||
armoredDraft: '', // Ascii Armored PGP Text Block
|
|
||||||
quotedMail: '', // Ascii Armored PGP Text Block mail that should be quoted
|
quotedMail: '', // Ascii Armored PGP Text Block mail that should be quoted
|
||||||
quotedMailIndent: true, // if true the quoted mail will be indented (default: true)
|
quotedMailIndent: true, // if true the quoted mail will be indented (default: true)
|
||||||
quotedMailHeader: '', // header to be added before the quoted mail
|
quotedMailHeader: '', // header to be added before the quoted mail
|
||||||
keepAttachments: false, // add attachments of quotedMail to editor (default: false)
|
keepAttachments: false, // add attachments of quotedMail to editor (default: false)
|
||||||
*/
|
*/
|
||||||
|
signMsg: confirm('Also sign this message?')
|
||||||
}).then(editor => this.mailvelope = editor);
|
}).then(editor => this.mailvelope = editor);
|
||||||
|
}
|
||||||
this.viewArea('mailvelope');
|
this.viewArea('mailvelope');
|
||||||
}
|
}
|
||||||
attachmentsArea() {
|
attachmentsArea() {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue