8#include "composerviewbase.h"
10#include "attachment/attachmentcontrollerbase.h"
11#include "attachment/attachmentmodel.h"
12#include "composer-ng/richtextcomposerng.h"
13#include "composer-ng/richtextcomposersignatures.h"
15#include "composer/keyresolver.h"
16#include "composer/signaturecontroller.h"
17#include "draftstatus/draftstatus.h"
18#include "imagescaling/imagescalingutils.h"
19#include "job/emailaddressresolvejob.h"
20#include "part/globalpart.h"
21#include "part/infopart.h"
22#include "utils/kleo_util.h"
23#include "utils/util.h"
24#include "utils/util_p.h"
25#include <KPIMTextEdit/RichTextComposerControler>
26#include <KPIMTextEdit/RichTextComposerImages>
28#include "sendlater/sendlatercreatejob.h"
29#include "sendlater/sendlaterinfo.h"
31#include <PimCommonAkonadi/RecentAddresses>
33#include "settings/messagecomposersettings.h"
34#include <MessageComposer/RecipientsEditor>
36#include <KCursorSaver>
37#include <KIdentityManagementCore/Identity>
38#include <MimeTreeParser/ObjectTreeParser>
39#include <MimeTreeParser/SimpleObjectTreeSource>
40#include <Sonnet/DictionaryComboBox>
42#include <MessageCore/AutocryptStorage>
43#include <MessageCore/StringUtil>
45#include <Akonadi/MessageQueueJob>
46#include <MailTransport/TransportComboBox>
47#include <MailTransport/TransportManager>
49#include <Akonadi/CollectionComboBox>
50#include <Akonadi/CollectionFetchJob>
51#include <Akonadi/ItemCreateJob>
52#include <Akonadi/MessageFlags>
53#include <Akonadi/SpecialMailCollections>
55#include <KEmailAddress>
56#include <KIdentityManagementCore/IdentityManager>
57#include <KIdentityManagementWidgets/IdentityCombo>
59#include "messagecomposer_debug.h"
61#include <Libkleo/ExpiryChecker>
62#include <Libkleo/ExpiryCheckerSettings>
64#include <QGpgME/ExportJob>
65#include <QGpgME/ImportJob>
66#include <QGpgME/Protocol>
67#include <gpgme++/context.h>
68#include <gpgme++/importresult.h>
70#include <KLocalizedString>
74#include "followupreminder/followupremindercreatejob.h"
76#include <QStandardPaths>
77#include <QTemporaryDir>
83ComposerViewBase::ComposerViewBase(
QObject *parent,
QWidget *parentGui)
85 , m_msg(KMime::Message::Ptr(new KMime::Message))
86 , m_parentWidget(parentGui)
87 , m_cryptoMessageFormat(
Kleo::AutoFormat)
88 , m_autoSaveInterval(60000)
93ComposerViewBase::~ComposerViewBase() =
default;
102 if (m_attachmentModel) {
103 const auto attachments{m_attachmentModel->attachments()};
105 if (!m_attachmentModel->removeAttachment(attachment)) {
106 qCWarning(MESSAGECOMPOSER_LOG) <<
"Attachment not found.";
111 if (m_recipientsEditor) {
112 m_recipientsEditor->
clear();
113 bool resultTooManyRecipients = m_recipientsEditor->setRecipientString(m_msg->to()->mailboxes(), MessageComposer::Recipient::To);
114 if (!resultTooManyRecipients) {
115 resultTooManyRecipients = m_recipientsEditor->setRecipientString(m_msg->cc()->mailboxes(), MessageComposer::Recipient::Cc);
117 if (!resultTooManyRecipients) {
118 resultTooManyRecipients = m_recipientsEditor->setRecipientString(m_msg->bcc()->mailboxes(), MessageComposer::Recipient::Bcc);
120 if (!resultTooManyRecipients) {
121 resultTooManyRecipients = m_recipientsEditor->setRecipientString(m_msg->replyTo()->mailboxes(), MessageComposer::Recipient::ReplyTo);
123 m_recipientsEditor->setFocusBottom();
125 if (!resultTooManyRecipients) {
127 if (
auto hrd = m_msg->headerByType(
"X-KMail-UnExpanded-To")) {
129 for (
const QString &addr : spl) {
130 if (m_recipientsEditor->
addRecipient(addr, MessageComposer::Recipient::To)) {
131 resultTooManyRecipients =
true;
132 qCWarning(MESSAGECOMPOSER_LOG) <<
"Impossible to add recipient.";
138 if (!resultTooManyRecipients) {
139 if (
auto hrd = m_msg->headerByType(
"X-KMail-UnExpanded-CC")) {
141 for (
const QString &addr : spl) {
142 if (m_recipientsEditor->
addRecipient(addr, MessageComposer::Recipient::Cc)) {
143 qCWarning(MESSAGECOMPOSER_LOG) <<
"Impossible to add recipient.";
144 resultTooManyRecipients =
true;
150 if (!resultTooManyRecipients) {
151 if (
auto hrd = m_msg->headerByType(
"X-KMail-UnExpanded-BCC")) {
153 for (
const QString &addr : spl) {
154 if (m_recipientsEditor->
addRecipient(addr, MessageComposer::Recipient::Bcc)) {
155 qCWarning(MESSAGECOMPOSER_LOG) <<
"Impossible to add recipient.";
156 resultTooManyRecipients =
true;
162 if (!resultTooManyRecipients) {
163 if (
auto hrd = m_msg->headerByType(
"X-KMail-UnExpanded-Reply-To")) {
165 for (
const QString &addr : spl) {
166 if (m_recipientsEditor->
addRecipient(addr, MessageComposer::Recipient::ReplyTo)) {
167 qCWarning(MESSAGECOMPOSER_LOG) <<
"Impossible to add recipient.";
168 resultTooManyRecipients =
true;
174 Q_EMIT tooManyRecipient(resultTooManyRecipients);
180 msgContent.
setContent(m_msg->encodedContent());
184 emptySource.setDecryptMessage(allowDecryption);
189 for (
const auto &att : attachmentsOfExtraContents) {
190 addAttachmentPart(att);
193 for (
const auto &att : attachments) {
194 addAttachmentPart(att);
197 int transportId = -1;
198 if (
auto hdr = m_msg->headerByType(
"X-KMail-Transport")) {
199 transportId = hdr->asUnicodeString().toInt();
206 qCWarning(MESSAGECOMPOSER_LOG) <<
"Impossible to find transport id" << transport->id();
218 m_editor->
setHtml(htmlContent);
220 collectImages(m_msg.
data());
223 if (
auto hdr = m_msg->headerByType(
"X-KMail-CursorPos")) {
224 m_editor->setCursorPositionFromStart(hdr->asUnicodeString().toUInt());
238 otp.parseObjectTree(&msgContent);
240 if (!otp.htmlContent().isEmpty()) {
241 m_editor->
setHtml(otp.htmlContent());
243 collectImages(msg.
data());
248 if (
auto hdr = msg->headerByType(
"X-KMail-CursorPos")) {
249 m_editor->setCursorPositionFromStart(hdr->asUnicodeString().toInt());
253void ComposerViewBase::saveMailSettings()
255 const auto identity = currentIdentity();
258 m_msg->setHeader(header);
261 header->fromUnicodeString(m_transport->
currentText());
262 m_msg->setHeader(header);
266 m_msg->setHeader(header);
270 m_msg->setHeader(header);
273 header->fromUnicodeString(identity.identityName());
274 m_msg->setHeader(header);
278 m_msg->setHeader(header);
282 if (m_editor->quotePrefixName().isEmpty()) {
283 m_msg->removeHeader(
"X-KMail-QuotePrefix");
286 header->fromUnicodeString(m_editor->quotePrefixName());
287 m_msg->setHeader(header);
290 if (m_editor->composerControler()->isFormattingUsed()) {
291 qCDebug(MESSAGECOMPOSER_LOG) <<
"HTML mode";
293 header->fromUnicodeString(QStringLiteral(
"true"));
294 m_msg->setHeader(header);
296 m_msg->removeHeader(
"X-KMail-Markup");
297 qCDebug(MESSAGECOMPOSER_LOG) <<
"Plain text";
301void ComposerViewBase::clearFollowUp()
303 mFollowUpDate =
QDate();
307void ComposerViewBase::send(MessageComposer::MessageSender::SendMethod method, MessageComposer::MessageSender::SaveIn saveIn,
bool checkMailDispatcher)
309 mSendMethod = method;
313 const auto identity = currentIdentity();
315 if (identity.attachVcard() && m_attachmentController->attachOwnVcard()) {
316 const QString vcardFileName = identity.vCardFile();
317 if (!vcardFileName.
isEmpty()) {
323 if (m_editor->composerControler()->isFormattingUsed() && inlineSigningEncryptionSelected()) {
325 m_encrypt ? m_sign ?
i18n(
"&Keep markup, do not sign/encrypt") :
i18n(
"&Keep markup, do not encrypt") :
i18n(
"&Keep markup, do not sign");
326 const QString yesBtnText = m_encrypt ? m_sign ?
i18n(
"Sign/Encrypt (delete markup)") :
i18n(
"Encrypt (delete markup)") :
i18n(
"Sign (delete markup)");
328 i18n(
"<qt><p>Inline signing/encrypting of HTML messages is not possible;</p>"
329 "<p>do you want to delete your markup?</p></qt>"),
330 i18nc(
"@title:window",
"Sign/Encrypt Message?"),
336 if (KMessageBox::ButtonCode::SecondaryAction == ret) {
344 if (m_neverEncrypt && saveIn != MessageComposer::MessageSender::SaveInNone) {
347 DraftSignatureState(m_msg).setState(m_sign);
348 DraftEncryptionState(m_msg).setState(m_encrypt);
349 DraftCryptoMessageFormatState(m_msg).setState(m_cryptoMessageFormat);
351 removeDraftCryptoHeaders(m_msg);
354 if (mSendMethod == MessageComposer::MessageSender::SendImmediate && checkMailDispatcher) {
355 if (!MessageComposer::Util::sendMailDispatcherIsOnline(m_parentWidget)) {
356 qCWarning(MESSAGECOMPOSER_LOG) <<
"Impossible to set sendmaildispatcher online. Please verify it";
366 m_customHeader = customHeader;
369void ComposerViewBase::readyForSending()
371 qCDebug(MESSAGECOMPOSER_LOG) <<
"Entering readyForSending";
373 qCDebug(MESSAGECOMPOSER_LOG) <<
"m_msg == 0!";
379 qCDebug(MESSAGECOMPOSER_LOG) <<
"ready for sending: Called while composer active; ignoring. Number of composer " << m_composers.
count();
385 const auto identity = currentIdentity();
386 if (!identity.isNull()) {
387 job->setDefaultDomainName(identity.defaultDomainName());
389 job->setFrom(from());
390 job->setTo(m_recipientsEditor->recipientStringList(MessageComposer::Recipient::To));
391 job->setCc(m_recipientsEditor->recipientStringList(MessageComposer::Recipient::Cc));
392 job->setBcc(m_recipientsEditor->recipientStringList(MessageComposer::Recipient::Bcc));
393 job->setReplyTo(m_recipientsEditor->recipientStringList(MessageComposer::Recipient::ReplyTo));
399void ComposerViewBase::slotEmailAddressResolved(
KJob *job)
402 qCWarning(MESSAGECOMPOSER_LOG) <<
"An error occurred while resolving the email addresses:" << job->
errorString();
407 bool autoresizeImage = MessageComposer::MessageComposerSettings::self()->autoResizeImageEnabled();
410 if (mSaveIn == MessageComposer::MessageSender::SaveInNone) {
416 if (autoresizeImage) {
417 const QStringList listEmails =
QStringList() << mExpandedFrom << mExpandedTo << mExpandedCc << mExpandedBcc << mExpandedReplyTo;
418 MessageComposer::Utils resizeUtils;
419 autoresizeImage = resizeUtils.filterRecipients(listEmails);
422 mExpandedFrom = from();
423 const auto recipients{m_recipientsEditor->recipients()};
426 case MessageComposer::Recipient::To:
427 mExpandedTo << r->email();
429 case MessageComposer::Recipient::Cc:
430 mExpandedCc << r->email();
432 case MessageComposer::Recipient::Bcc:
433 mExpandedBcc << r->email();
435 case MessageComposer::Recipient::ReplyTo:
436 mExpandedReplyTo << r->email();
438 case MessageComposer::Recipient::Undefined:
439 Q_ASSERT(!
"Unknown recipient type!");
447 const auto expandedToLst{resolveJob->
expandedTo()};
448 for (
const QString &exp : expandedToLst) {
453 const auto expandedCcLst{resolveJob->
expandedCc()};
454 for (
const QString &exp : expandedCcLst) {
459 const auto expandedBCcLst{resolveJob->
expandedBcc()};
460 for (
const QString &exp : expandedBCcLst) {
462 unExpandedBcc << exp;
466 for (
const QString &exp : expandedReplyLst) {
467 if (!mExpandedReplyTo.
contains(exp)) {
468 unExpandedReplyTo << exp;
473 m_msg->setHeader(header);
476 m_msg->setHeader(header);
479 m_msg->setHeader(header);
482 m_msg->setHeader(header);
483 autoresizeImage =
false;
486 Q_ASSERT(m_composers.
isEmpty());
493 if (m_neverEncrypt && mSaveIn != MessageComposer::MessageSender::SaveInNone && !mSendLaterInfo) {
495 composer->setNoCrypto(
true);
496 m_composers.
append(composer);
498 bool wasCanceled =
false;
499 m_composers = generateCryptoMessages(wasCanceled);
510 if (autoresizeImage) {
511 if (MessageComposer::MessageComposerSettings::self()->askBeforeResizing()) {
512 if (m_attachmentModel) {
513 MessageComposer::Utils resizeUtils;
514 if (resizeUtils.containsImage(m_attachmentModel->attachments())) {
516 i18n(
"Do you want to resize images?"),
517 i18nc(
"@title:window",
"Auto Resize Images"),
520 if (rc == KMessageBox::ButtonCode::PrimaryAction) {
521 autoresizeImage =
true;
523 autoresizeImage =
false;
526 autoresizeImage =
false;
534 const auto composers = m_composers;
536 fillComposer(composer, UseExpandedRecipients, autoresizeImage);
539 qCDebug(MESSAGECOMPOSER_LOG) <<
"Started a composer for sending!";
547inline Kleo::chrono::days encryptOwnKeyNearExpiryWarningThresholdInDays()
549 if (!MessageComposer::MessageComposerSettings::self()->cryptoWarnWhenNearExpire()) {
550 return Kleo::chrono::days{-1};
552 const int num = MessageComposer::MessageComposerSettings::self()->cryptoWarnOwnEncrKeyNearExpiryThresholdDays();
553 return Kleo::chrono::days{qMax(1, num)};
556inline Kleo::chrono::days encryptKeyNearExpiryWarningThresholdInDays()
558 if (!MessageComposer::MessageComposerSettings::self()->cryptoWarnWhenNearExpire()) {
559 return Kleo::chrono::days{-1};
561 const int num = MessageComposer::MessageComposerSettings::self()->cryptoWarnEncrKeyNearExpiryThresholdDays();
562 return Kleo::chrono::days{qMax(1, num)};
565inline Kleo::chrono::days encryptRootCertNearExpiryWarningThresholdInDays()
567 if (!MessageComposer::MessageComposerSettings::self()->cryptoWarnWhenNearExpire()) {
568 return Kleo::chrono::days{-1};
570 const int num = MessageComposer::MessageComposerSettings::self()->cryptoWarnEncrRootNearExpiryThresholdDays();
571 return Kleo::chrono::days{qMax(1, num)};
574inline Kleo::chrono::days encryptChainCertNearExpiryWarningThresholdInDays()
576 if (!MessageComposer::MessageComposerSettings::self()->cryptoWarnWhenNearExpire()) {
577 return Kleo::chrono::days{-1};
579 const int num = MessageComposer::MessageComposerSettings::self()->cryptoWarnEncrChaincertNearExpiryThresholdDays();
580 return Kleo::chrono::days{qMax(1, num)};
583inline bool showKeyApprovalDialog()
585 return MessageComposer::MessageComposerSettings::self()->cryptoShowKeysForApproval();
593 return MessageComposer::MessageComposerSettings::self()->cryptoWarningUnsigned();
601 return MessageComposer::MessageComposerSettings::self()->cryptoWarningUnencrypted();
605bool ComposerViewBase::addKeysToContext(
const QString &gnupgHome,
607 const std::map<QByteArray, QString> &autocryptMap)
609 bool needSpecialContext =
false;
611 for (
const auto &p : data) {
612 for (
const auto &k : p.second) {
613 const auto it = autocryptMap.find(k.primaryFingerprint());
614 if (it != autocryptMap.end()) {
615 needSpecialContext =
true;
619 if (needSpecialContext) {
624 if (!needSpecialContext) {
627 const QGpgME::Protocol *proto(QGpgME::openpgp());
629 const auto storage = MessageCore::AutocryptStorage::self();
632 for (
const auto &p : data) {
633 for (
const auto &k : p.second) {
634 const auto it = autocryptMap.find(k.primaryFingerprint());
635 if (it == autocryptMap.end()) {
636 qCDebug(MESSAGECOMPOSER_LOG) <<
"Adding " << k.primaryFingerprint() <<
"via Export/Import";
637 auto exportJob = proto->publicKeyExportJob(
false);
639 &QGpgME::ExportJob::result,
641 [&gnupgHome, &proto, &runningJobs, &loop, &k](
const GpgME::Error &result,
644 const GpgME::Error &auditLogError) {
645 Q_UNUSED(auditLogAsHtml);
646 Q_UNUSED(auditLogError);
648 qCWarning(MESSAGECOMPOSER_LOG) <<
"Failed to export " << k.primaryFingerprint() << result.asString();
650 if (runningJobs < 1) {
655 auto importJob = proto->importJob();
656 QGpgME::Job::context(importJob)->setEngineHomeDirectory(gnupgHome.
toUtf8().
constData());
657 importJob->exec(keyData);
658 importJob->deleteLater();
660 if (runningJobs < 1) {
667 exportJob->start(patterns);
668 exportJob->setExportFlags(GpgME::Context::ExportMinimal);
670 qCDebug(MESSAGECOMPOSER_LOG) <<
"Adding " << k.primaryFingerprint() <<
"from Autocrypt storage";
671 const auto recipient = storage->getRecipient(it->second.toUtf8());
672 auto key = recipient->gpgKey();
673 auto keydata = recipient->gpgKeydata();
675 qCDebug(MESSAGECOMPOSER_LOG) <<
"Using gossipkey";
676 keydata = recipient->gossipKeydata();
678 auto importJob = proto->importJob();
679 QGpgME::Job::context(importJob)->setEngineHomeDirectory(gnupgHome.
toUtf8().
constData());
680 const auto result = importJob->exec(keydata);
681 importJob->deleteLater();
689void ComposerViewBase::setAkonadiLookupEnabled(
bool akonadiLookupEnabled)
691 m_akonadiLookupEnabled = akonadiLookupEnabled;
696 const auto id = currentIdentity();
698 bool canceled =
false;
700 qCDebug(MESSAGECOMPOSER_LOG) <<
"filling crypto info";
702 &Kleo::ExpiryChecker::expiryMessage,
704 [&canceled](
const GpgME::Key &key,
QString msg, Kleo::ExpiryChecker::ExpiryInformation info,
bool isNewMessage) {
714 if (info == Kleo::ExpiryChecker::OwnKeyExpired || info == Kleo::ExpiryChecker::OwnKeyNearExpiry) {
715 dontAskAgainName = QStringLiteral(
"own key expires soon warning");
717 dontAskAgainName = QStringLiteral(
"other encryption key near expiry warning");
719 if (info == Kleo::ExpiryChecker::OwnKeyExpired || info == Kleo::ExpiryChecker::OtherKeyExpired) {
720 title = key.protocol() == GpgME::OpenPGP ?
i18n(
"OpenPGP Key Expired") :
i18n(
"S/MIME Certificate Expired");
722 title = key.protocol() == GpgME::OpenPGP ?
i18n(
"OpenPGP Key Expires Soon") :
i18n(
"S/MIME Certificate Expires Soon");
731 new Kleo::KeyResolver(
true, showKeyApprovalDialog(),
id.pgpAutoEncrypt(), m_cryptoMessageFormat, expiryChecker()));
733 keyResolver->setAutocryptEnabled(autocryptEnabled());
734 keyResolver->setAkonadiLookupEnabled(m_akonadiLookupEnabled);
739 bool signSomething = m_sign;
740 bool doSignCompletely = m_sign;
741 bool encryptSomething = m_encrypt;
742 bool doEncryptCompletely = m_encrypt;
745 if (!
id.pgpEncryptionKey().isEmpty()) {
748 if (!
id.smimeEncryptionKey().isEmpty()) {
751 if (canceled || keyResolver->setEncryptToSelfKeys(encryptToSelfKeys) != Kleo::Ok) {
752 qCDebug(MESSAGECOMPOSER_LOG) <<
"Failed to set encryptoToSelf keys!";
757 if (!
id.pgpSigningKey().isEmpty()) {
760 if (!
id.smimeSigningKey().isEmpty()) {
763 if (canceled || keyResolver->setSigningKeys(signKeys) != Kleo::Ok) {
764 qCDebug(MESSAGECOMPOSER_LOG) <<
"Failed to set signing keys!";
768 if (m_attachmentModel) {
769 const auto attachments = m_attachmentModel->attachments();
771 if (attachment->isSigned()) {
772 signSomething =
true;
774 doEncryptCompletely =
false;
776 if (attachment->isEncrypted()) {
777 encryptSomething =
true;
779 doSignCompletely =
false;
784 const QStringList recipients = mExpandedTo + mExpandedCc;
787 keyResolver->setPrimaryRecipients(recipients);
788 keyResolver->setSecondaryRecipients(bcc);
792 signSomething = determineWhetherToSign(doSignCompletely, keyResolver.data(), signSomething, result, canceled);
795 qCDebug(MESSAGECOMPOSER_LOG) <<
"determineWhetherToSign: failed to resolve keys! oh noes";
801 wasCanceled = canceled;
806 encryptSomething = determineWhetherToEncrypt(doEncryptCompletely, keyResolver.data(), encryptSomething, signSomething, result, canceled);
809 qCDebug(MESSAGECOMPOSER_LOG) <<
"determineWhetherToEncrypt: failed to resolve keys! oh noes";
816 wasCanceled = canceled;
823 if (!signSomething && !encryptSomething) {
825 if (m_cryptoMessageFormat & Kleo::OpenPGPMIMEFormat) {
826 composer->setAutocryptEnabled(autocryptEnabled());
827 if (keyResolver->encryptToSelfKeysFor(Kleo::OpenPGPMIMEFormat).size() > 0) {
828 composer->setSenderEncryptionKey(keyResolver->encryptToSelfKeysFor(Kleo::OpenPGPMIMEFormat)[0]);
831 composers.
append(composer);
836 const Kleo::Result kpgpResult = keyResolver->resolveAllKeys(signSomething, encryptSomething);
837 if (kpgpResult == Kleo::Canceled || canceled) {
838 qCDebug(MESSAGECOMPOSER_LOG) <<
"resolveAllKeys: one key resolution canceled by user";
840 }
else if (kpgpResult != Kleo::Ok) {
842 qCDebug(MESSAGECOMPOSER_LOG) <<
"resolveAllKeys: failed to resolve keys! oh noes";
847 qCDebug(MESSAGECOMPOSER_LOG) <<
"done resolving keys.";
849 if (encryptSomething || signSomething) {
850 Kleo::CryptoMessageFormat concreteFormat = Kleo::AutoFormat;
851 for (
unsigned int i = 0; i < numConcreteCryptoMessageFormats; ++i) {
852 concreteFormat = concreteCryptoMessageFormats[i];
853 const auto encData = keyResolver->encryptionItems(concreteFormat);
854 if (encData.empty()) {
858 if (!(concreteFormat & m_cryptoMessageFormat)) {
864 if (encryptSomething || autocryptEnabled()) {
865 auto end(encData.end());
868 for (
auto it = encData.begin(); it !=
end; ++it) {
869 QPair<QStringList, std::vector<GpgME::Key>> p(it->recipients, it->keys);
871 qCDebug(MESSAGECOMPOSER_LOG) <<
"got resolved keys for:" << it->recipients;
873 composer->setEncryptionKeys(data);
874 if (concreteFormat & Kleo::OpenPGPMIMEFormat && autocryptEnabled()) {
875 composer->setAutocryptEnabled(autocryptEnabled());
876 composer->setSenderEncryptionKey(keyResolver->encryptToSelfKeysFor(concreteFormat)[0]);
878 bool specialGnupgHome = addKeysToContext(
dir.path(), data, keyResolver->useAutocrypt());
879 if (specialGnupgHome) {
880 dir.setAutoRemove(
false);
881 composer->setGnupgHome(
dir.path());
888 std::vector<GpgME::Key> signingKeys = keyResolver->signingKeys(concreteFormat);
889 composer->setSigningKeys(signingKeys);
892 composer->setCryptoMessageFormat(concreteFormat);
893 composer->setSignAndEncrypt(signSomething, encryptSomething);
895 composers.
append(composer);
899 composers.
append(composer);
901 markAllAttachmentsForSigning(
false);
902 markAllAttachmentsForEncryption(
false);
905 if (composers.
isEmpty() && (signSomething || encryptSomething)) {
906 Q_ASSERT_X(
false,
"ComposerViewBase::generateCryptoMessages",
"No concrete sign or encrypt method selected");
914 globalPart->setParentWidgetForGui(m_parentWidget);
915 globalPart->setMDNRequested(m_mdnRequested);
916 globalPart->setRequestDeleveryConfirmation(m_requestDeleveryConfirmation);
927 if (m_fccCollection.
isValid()) {
933 if (expansion == UseExpandedRecipients) {
934 infoPart->setFrom(mExpandedFrom);
935 infoPart->setTo(mExpandedTo);
936 infoPart->setCc(mExpandedCc);
937 infoPart->setBcc(mExpandedBcc);
938 infoPart->setReplyTo(mExpandedReplyTo);
940 infoPart->setFrom(from());
941 infoPart->setTo(m_recipientsEditor->recipientStringList(MessageComposer::Recipient::To));
942 infoPart->setCc(m_recipientsEditor->recipientStringList(MessageComposer::Recipient::Cc));
943 infoPart->setBcc(m_recipientsEditor->recipientStringList(MessageComposer::Recipient::Bcc));
944 infoPart->setReplyTo(m_recipientsEditor->recipientStringList(MessageComposer::Recipient::ReplyTo));
946 infoPart->setSubject(subject());
947 infoPart->setUserAgent(QStringLiteral(
"KMail"));
948 infoPart->setUrgent(m_urgent);
950 if (
auto inReplyTo = m_msg->inReplyTo(
false)) {
951 infoPart->setInReplyTo(inReplyTo->asUnicodeString());
954 if (
auto references = m_msg->references(
false)) {
955 infoPart->setReferences(references->asUnicodeString());
959 if (
auto hdr = m_msg->headerByType(
"X-KMail-SignatureActionEnabled")) {
962 if (
auto hdr = m_msg->headerByType(
"X-KMail-EncryptActionEnabled")) {
965 if (
auto hdr = m_msg->headerByType(
"X-KMail-CryptoMessageFormat")) {
968 if (
auto hdr = m_msg->headerByType(
"X-KMail-UnExpanded-To")) {
971 if (
auto hdr = m_msg->headerByType(
"X-KMail-UnExpanded-CC")) {
974 if (
auto hdr = m_msg->headerByType(
"X-KMail-UnExpanded-BCC")) {
977 if (
auto hdr = m_msg->headerByType(
"X-KMail-UnExpanded-Reply-To")) {
980 if (
auto hdr = m_msg->organization(
false)) {
983 if (
auto hdr = m_msg->headerByType(
"X-KMail-Identity")) {
986 if (
auto hdr = m_msg->headerByType(
"X-KMail-Transport")) {
989 if (
auto hdr = m_msg->headerByType(
"X-KMail-Fcc")) {
992 if (
auto hdr = m_msg->headerByType(
"X-KMail-Drafts")) {
995 if (
auto hdr = m_msg->headerByType(
"X-KMail-Templates")) {
998 if (
auto hdr = m_msg->headerByType(
"X-KMail-Link-Message")) {
1001 if (
auto hdr = m_msg->headerByType(
"X-KMail-Link-Type")) {
1004 if (
auto hdr = m_msg->headerByType(
"X-Face")) {
1007 if (
auto hdr = m_msg->headerByType(
"Face")) {
1010 if (
auto hdr = m_msg->headerByType(
"X-KMail-FccDisabled")) {
1013 if (
auto hdr = m_msg->headerByType(
"X-KMail-Identity-Name")) {
1016 if (
auto hdr = m_msg->headerByType(
"X-KMail-Transport-Name")) {
1020 infoPart->setExtraHeaders(extras);
1023void ComposerViewBase::slotSendComposeResult(
KJob *job)
1027 if (composer->error() != MessageComposer::Composer::NoError) {
1028 qCDebug(MESSAGECOMPOSER_LOG) <<
"compose job might have error: " << job->
error() <<
" errorString: " << job->
errorString();
1031 if (composer->error() == MessageComposer::Composer::NoError) {
1032 Q_ASSERT(m_composers.
contains(composer));
1034 qCDebug(MESSAGECOMPOSER_LOG) <<
"NoError.";
1035 const int numberOfMessage(composer->resultMessages().size());
1036 for (
int i = 0; i < numberOfMessage; ++i) {
1037 if (mSaveIn == MessageComposer::MessageSender::SaveInNone) {
1038 queueMessage(composer->resultMessages().at(i), composer);
1040 saveMessage(composer->resultMessages().at(i), mSaveIn);
1043 saveRecentAddresses(composer->resultMessages().at(0));
1044 }
else if (composer->error() == MessageComposer::Composer::UserCancelledError) {
1047 qCDebug(MESSAGECOMPOSER_LOG) <<
"UserCancelledError.";
1050 qCDebug(MESSAGECOMPOSER_LOG) <<
"other Error." << composer->error();
1052 if (composer->error() == MessageComposer::Composer::BugError) {
1053 msg =
i18n(
"Could not compose message: %1 \n Please report this bug.", job->
errorString());
1060 if (!composer->gnupgHome().isEmpty()) {
1061 QDir dir(composer->gnupgHome());
1062 dir.removeRecursively();
1070 KConfig *config = MessageComposer::MessageComposerSettings::self()->config();
1071 if (
auto to = msg->to(
false)) {
1072 const auto toAddresses =
to->mailboxes();
1073 for (
const auto &address : toAddresses) {
1077 if (
auto cc = msg->cc(
false)) {
1078 const auto ccAddresses = cc->mailboxes();
1079 for (
const auto &address : ccAddresses) {
1083 if (
auto bcc = msg->bcc(
false)) {
1084 const auto bccAddresses = bcc->mailboxes();
1085 for (
const auto &address : bccAddresses) {
1095 qjob->setMessage(message);
1096 qjob->transportAttribute().setTransportId(infoPart->transportId());
1097 if (mSendMethod == MessageComposer::MessageSender::SendLater) {
1101 if (message->hasHeader(
"X-KMail-FccDisabled")) {
1107 qjob->sentBehaviourAttribute().setMoveToCollection(sentCollection);
1113 if (transport && transport->specifySenderOverwriteAddress()) {
1114 qjob->addressAttribute().setFrom(
1122 qjob->addressAttribute().setTo(MessageComposer::Util::cleanUpEmailListAndEncoding(realTo->asUnicodeString().split(
QLatin1Char(
'%'))));
1123 message->removeHeader(
"X-KMail-EncBccRecipients");
1124 message->assemble();
1125 qCDebug(MESSAGECOMPOSER_LOG) <<
"sending with-bcc encr mail to a/n recipient:" << qjob->addressAttribute().to();
1127 qjob->addressAttribute().setTo(MessageComposer::Util::cleanUpEmailListAndEncoding(infoPart->
to()));
1128 qjob->addressAttribute().setCc(MessageComposer::Util::cleanUpEmailListAndEncoding(infoPart->
cc()));
1129 qjob->addressAttribute().setBcc(MessageComposer::Util::cleanUpEmailListAndEncoding(infoPart->
bcc()));
1131 if (m_requestDeleveryConfirmation) {
1132 qjob->addressAttribute().setDeliveryStatusNotification(
true);
1134 MessageComposer::Util::addSendReplyForwardAction(message, qjob);
1137 MessageComposer::Util::addCustomHeaders(message, m_customHeader);
1138 message->assemble();
1140 m_pendingQueueJobs++;
1143 qCDebug(MESSAGECOMPOSER_LOG) <<
"Queued a message.";
1146void ComposerViewBase::slotQueueResult(
KJob *job)
1148 m_pendingQueueJobs--;
1150 qCDebug(MESSAGECOMPOSER_LOG) <<
"mPendingQueueJobs" << m_pendingQueueJobs;
1151 Q_ASSERT(m_pendingQueueJobs >= 0);
1154 qCDebug(MESSAGECOMPOSER_LOG) <<
"Failed to queue a message:" << job->
errorString();
1158 const QString msg =
i18n(
"There were problems trying to queue the message for sending: %1", job->
errorString());
1160 if (m_pendingQueueJobs == 0) {
1166 if (m_pendingQueueJobs == 0) {
1167 addFollowupReminder(qjob->message()->messageID(
false)->asUnicodeString());
1172void ComposerViewBase::initAutoSave()
1174 qCDebug(MESSAGECOMPOSER_LOG) <<
"initialising autosave";
1178 if (!dataDirectory.exists(QStringLiteral(
"autosave"))) {
1179 qCDebug(MESSAGECOMPOSER_LOG) <<
"Creating autosave directory.";
1180 dataDirectory.mkdir(QStringLiteral(
"autosave"));
1184 if (m_autoSaveUUID.
isEmpty()) {
1193 return mFollowUpCollection;
1196void ComposerViewBase::setFollowUpCollection(
const Akonadi::Collection &followUpCollection)
1198 mFollowUpCollection = followUpCollection;
1201QDate ComposerViewBase::followUpDate()
const
1203 return mFollowUpDate;
1206void ComposerViewBase::setFollowUpDate(
const QDate &followUpDate)
1208 mFollowUpDate = followUpDate;
1213 return m_dictionary;
1218 m_dictionary = dictionary;
1223 if (m_autoSaveInterval == 0) {
1224 delete m_autoSaveTimer;
1225 m_autoSaveTimer =
nullptr;
1227 if (!m_autoSaveTimer) {
1228 m_autoSaveTimer =
new QTimer(
this);
1229 if (m_parentWidget) {
1235 m_autoSaveTimer->
start(m_autoSaveInterval);
1241 delete m_autoSaveTimer;
1242 m_autoSaveTimer =
nullptr;
1243 if (!m_autoSaveUUID.
isEmpty()) {
1244 qCDebug(MESSAGECOMPOSER_LOG) <<
"deleting autosave files" << m_autoSaveUUID;
1255 qCDebug(MESSAGECOMPOSER_LOG) <<
"There are" << autoSaveFiles.
count() <<
"to be deleted.";
1258 for (
const QString &file : autoSaveFiles) {
1259 autoSaveDir.
remove(file);
1261 m_autoSaveUUID.
clear();
1268 qCDebug(MESSAGECOMPOSER_LOG) <<
"Autosaving message";
1270 if (m_autoSaveTimer) {
1271 m_autoSaveTimer->
stop();
1276 qCDebug(MESSAGECOMPOSER_LOG) <<
"Autosave: Called while composer active; ignoring. Number of composer " << m_composers.
count();
1281 fillComposer(composer);
1283 composer->setAutocryptEnabled(autocryptEnabled());
1284 m_composers.
append(composer);
1291 m_autoSaveUUID = fileName;
1296void ComposerViewBase::slotAutoSaveComposeResult(
KJob *job)
1300 Q_ASSERT(
dynamic_cast<Composer *
>(job));
1301 auto composer =
static_cast<Composer *
>(job);
1303 if (composer->
error() == Composer::NoError) {
1304 Q_ASSERT(m_composers.
contains(composer));
1308 qCDebug(MESSAGECOMPOSER_LOG) <<
"NoError.";
1309 writeAutoSaveToDisk(composer->resultMessages().constFirst());
1310 Q_ASSERT(composer->resultMessages().size() == 1);
1312 if (m_autoSaveInterval > 0) {
1315 }
else if (composer->
error() == MessageComposer::Composer::UserCancelledError) {
1318 qCDebug(MESSAGECOMPOSER_LOG) <<
"UserCancelledError.";
1321 qCDebug(MESSAGECOMPOSER_LOG) <<
"other Error.";
1332 const QString filename = autosavePath + m_autoSaveUUID;
1335 qCDebug(MESSAGECOMPOSER_LOG) <<
"Writing message to disk as" << filename;
1340 if (file.write(message->encodedContent()) !=
static_cast<qint64
>(message->encodedContent().size())) {
1343 if (!file.commit()) {
1352 qCWarning(MESSAGECOMPOSER_LOG) <<
"Auto saving failed:" <<
errorMessage << file.errorString() <<
" m_autoSaveUUID" << m_autoSaveUUID;
1353 if (!m_autoSaveErrorShown) {
1355 i18n(
"Autosaving the message as %1 failed.\n"
1360 file.errorString()),
1361 i18nc(
"@title:window",
"Autosaving Message Failed"));
1364 m_autoSaveErrorShown =
true;
1368 m_autoSaveErrorShown =
false;
1374void ComposerViewBase::saveMessage(
const KMime::Message::Ptr &message, MessageComposer::MessageSender::SaveIn saveIn)
1377 const auto identity = currentIdentity();
1379 if (!identity.
isNull()) {
1380 if (
auto header = message->headerByType(
"X-KMail-Fcc")) {
1381 const int sentCollectionId = header->asUnicodeString().toInt();
1383 message->removeHeader(
"X-KMail-Fcc");
1387 MessageComposer::Util::addCustomHeaders(message, m_customHeader);
1389 message->assemble();
1392 item.
setMimeType(QStringLiteral(
"message/rfc822"));
1396 if (!identity.
isNull()) {
1398 case MessageComposer::MessageSender::SaveInTemplates:
1403 case MessageComposer::MessageSender::SaveInDrafts:
1408 case MessageComposer::MessageSender::SaveInOutbox:
1411 case MessageComposer::MessageSender::SaveInNone:
1420 target = defaultSpecialTarget();
1423 ++m_pendingQueueJobs;
1427void ComposerViewBase::slotSaveMessage(
KJob *job)
1432 target = defaultSpecialTarget();
1436 target = defaultSpecialTarget();
1443 ++m_pendingQueueJobs;
1450 case MessageComposer::MessageSender::SaveInNone:
1452 case MessageComposer::MessageSender::SaveInDrafts:
1455 case MessageComposer::MessageSender::SaveInTemplates:
1458 case MessageComposer::MessageSender::SaveInOutbox:
1466void ComposerViewBase::slotCreateItemResult(
KJob *job)
1468 --m_pendingQueueJobs;
1469 qCDebug(MESSAGECOMPOSER_LOG) <<
"mPendingCreateItemJobs" << m_pendingQueueJobs;
1470 Q_ASSERT(m_pendingQueueJobs >= 0);
1473 qCWarning(MESSAGECOMPOSER_LOG) <<
"Failed to save a message:" << job->
errorString();
1479 if (mSendLaterInfo) {
1484 addSendLaterItem(item);
1488 if (m_pendingQueueJobs == 0) {
1496 qCDebug(MESSAGECOMPOSER_LOG) <<
"adding attachment with url:" << url;
1498 m_attachmentController->addAttachmentUrlSync(url);
1508 attachment->setName(name);
1509 attachment->setFileName(filename);
1510 attachment->setData(data);
1511 attachment->setCharset(charset.
toLatin1());
1512 attachment->setMimeType(mimeType);
1519void ComposerViewBase::addAttachmentPart(
KMime::Content *partToAttach)
1531 part->setDescription(cd->asUnicodeString());
1534 if (ct->hasParameter(
"name")) {
1535 part->setName(ct->parameter(
"name"));
1539 part->setFileName(cd->filename());
1540 part->setInline(cd->disposition() == KMime::Headers::CDinline);
1542 if (part->name().isEmpty() && !part->fileName().isEmpty()) {
1543 part->setName(part->fileName());
1545 if (part->fileName().isEmpty() && !part->name().isEmpty()) {
1546 part->setFileName(part->name());
1553 fillComposer(composer, UseUnExpandedRecipients,
false);
1556void ComposerViewBase::fillComposer(
MessageComposer::Composer *composer, ComposerViewBase::RecipientExpansion expansion,
bool autoresize)
1558 fillGlobalPart(composer->globalPart());
1559 m_editor->fillComposerTextPart(composer->textPart());
1560 fillInfoPart(composer->infoPart(), expansion);
1561 if (m_attachmentModel) {
1562 composer->addAttachmentParts(m_attachmentModel->attachments(), autoresize);
1569 if (m_recipientsEditor) {
1570 return MessageComposer::Util::cleanedUpHeaderString(m_recipientsEditor->recipientString(MessageComposer::Recipient::To));
1576QString ComposerViewBase::cc()
const
1578 if (m_recipientsEditor) {
1579 return MessageComposer::Util::cleanedUpHeaderString(m_recipientsEditor->recipientString(MessageComposer::Recipient::Cc));
1585QString ComposerViewBase::bcc()
const
1587 if (m_recipientsEditor) {
1588 return MessageComposer::Util::cleanedUpHeaderString(m_recipientsEditor->recipientString(MessageComposer::Recipient::Bcc));
1593QString ComposerViewBase::from()
const
1595 return MessageComposer::Util::cleanedUpHeaderString(m_from);
1598QString ComposerViewBase::replyTo()
const
1600 if (m_recipientsEditor) {
1601 return MessageComposer::Util::cleanedUpHeaderString(m_recipientsEditor->recipientString(MessageComposer::Recipient::ReplyTo));
1606QString ComposerViewBase::subject()
const
1608 return MessageComposer::Util::cleanedUpHeaderString(m_subject);
1616bool ComposerViewBase::autocryptEnabled()
const
1621void ComposerViewBase::setParentWidgetForGui(
QWidget *w)
1628 m_attachmentController = controller;
1633 return m_attachmentController;
1638 m_attachmentModel = model;
1643 return m_attachmentModel;
1648 m_recipientsEditor = recEditor;
1653 return m_recipientsEditor;
1658 m_signatureController = sigController;
1663 return m_signatureController;
1668 m_identityCombo = identCombo;
1673 return m_identityCombo;
1678 MessageComposer::Recipient::Type type)
1683 case MessageComposer::Recipient::Bcc: {
1684 oldIdentList = oldIdent.
bcc();
1685 newIdentList = ident.
bcc();
1688 case MessageComposer::Recipient::Cc: {
1689 oldIdentList = oldIdent.
cc();
1690 newIdentList = ident.
cc();
1693 case MessageComposer::Recipient::ReplyTo: {
1698 case MessageComposer::Recipient::To:
1699 case MessageComposer::Recipient::Undefined:
1703 if (oldIdentList != newIdentList) {
1711 m_recipientsEditor->
addRecipient(recipient.prettyAddress(), type);
1713 m_recipientsEditor->setFocusBottom();
1719 updateRecipients(ident, oldIdent, MessageComposer::Recipient::Bcc);
1720 updateRecipients(ident, oldIdent, MessageComposer::Recipient::Cc);
1721 updateRecipients(ident, oldIdent, MessageComposer::Recipient::ReplyTo);
1726 const bool replaced = editor()->composerSignature()->replaceSignature(oldSig, newSig);
1732 attachmentController()->setIdentityHasOwnVcard(!vcardFileName.
isEmpty());
1733 attachmentController()->setAttachOwnVcard(ident.
attachVcard());
1741 m_editor->
document()->setModified(
false);
1751 m_transport = transpCombo;
1761 m_identMan = identMan;
1774 m_fccCollection = fccCollection;
1777 connect(checkFccCollectionJob, &
KJob::result,
this, &ComposerViewBase::slotFccCollectionCheckResult);
1780void ComposerViewBase::slotFccCollectionCheckResult(
KJob *job)
1783 qCWarning(MESSAGECOMPOSER_LOG) <<
" void ComposerViewBase::slotFccCollectionCheckResult(KJob *job) error " << job->
errorString();
1788 m_fccCollection = sentMailCol;
1808void ComposerViewBase::setSubject(
const QString &subject)
1810 m_subject = subject;
1811 if (mSendLaterInfo) {
1812 mSendLaterInfo->setSubject(m_subject);
1813 mSendLaterInfo->setTo(
to());
1817void ComposerViewBase::setAutoSaveInterval(
int interval)
1819 m_autoSaveInterval = interval;
1825 m_encrypt = encrypt;
1826 m_cryptoMessageFormat = format;
1827 m_neverEncrypt = neverEncryptDrafts;
1830void ComposerViewBase::setMDNRequested(
bool mdnRequested)
1832 m_mdnRequested = mdnRequested;
1835void ComposerViewBase::setUrgent(
bool urgent)
1840int ComposerViewBase::autoSaveInterval()
const
1842 return m_autoSaveInterval;
1848 if (
KMime::Content *n = Util::findTypeInMessage(root,
"multipart",
"alternative")) {
1851 const auto nodes = parentnode->
contents();
1852 for (
auto node : nodes) {
1853 if (node->contentType()->isImage()) {
1854 qCDebug(MESSAGECOMPOSER_LOG) <<
"found image in multipart/related : " << node->contentType()->name();
1857 m_editor->composerControler()->composerImages()->loadImage(
1860 node->contentType()->name());
1868bool ComposerViewBase::inlineSigningEncryptionSelected()
const
1870 if (!m_sign && !m_encrypt) {
1873 return m_cryptoMessageFormat == Kleo::InlineOpenPGPFormat;
1876bool ComposerViewBase::hasMissingAttachments(
const QStringList &attachmentKeywords)
1878 if (attachmentKeywords.
isEmpty()) {
1881 if (m_attachmentModel && m_attachmentModel->rowCount() > 0) {
1885 return MessageComposer::Util::hasMissingAttachments(attachmentKeywords, m_editor->
document(), subject());
1890 if (!hasMissingAttachments(attachmentKeywords)) {
1891 return NoMissingAttachmentFound;
1895 i18n(
"The message you have composed seems to refer to an "
1896 "attached file but you have not attached anything.\n"
1897 "Do you want to attach a file to your message?"),
1898 i18nc(
"@title:window",
"File Attachment Reminder"),
1902 return FoundMissingAttachmentAndCancel;
1904 if (rc == KMessageBox::ButtonCode::PrimaryAction) {
1905 m_attachmentController->showAddAttachmentFileDialog();
1906 return FoundMissingAttachmentAndAddedAttachment;
1909 return FoundMissingAttachmentAndSending;
1912void ComposerViewBase::markAllAttachmentsForSigning(
bool sign)
1914 if (m_attachmentModel) {
1915 const auto attachments = m_attachmentModel->attachments();
1917 attachment->setSigned(sign);
1922void ComposerViewBase::markAllAttachmentsForEncryption(
bool encrypt)
1924 if (m_attachmentModel) {
1925 const auto attachments = m_attachmentModel->attachments();
1927 attachment->setEncrypted(encrypt);
1932bool ComposerViewBase::determineWhetherToSign(
bool doSignCompletely,
Kleo::KeyResolver *keyResolver,
bool signSomething,
bool &result,
bool &canceled)
1935 switch (keyResolver->checkSigningPreferences(signSomething)) {
1937 if (!signSomething) {
1938 markAllAttachmentsForSigning(
true);
1943 case Kleo::DontDoIt:
1946 case Kleo::AskOpportunistic:
1952 "Examination of the recipient's signing preferences "
1953 "yielded that you be asked whether or not to sign "
1955 "Sign this message?");
1958 i18nc(
"@title:window",
"Sign Message?"),
1965 case KMessageBox::ButtonCode::PrimaryAction:
1966 markAllAttachmentsForSigning(
true);
1968 case KMessageBox::ButtonCode::SecondaryAction:
1969 markAllAttachmentsForSigning(
false);
1972 qCWarning(MESSAGECOMPOSER_LOG) <<
"Unhandled MessageBox response";
1977 case Kleo::Conflict: {
1981 "There are conflicting signing preferences "
1982 "for these recipients.\n"
1983 "Sign this message?");
1986 i18nc(
"@title:window",
"Sign Message?"),
1993 case KMessageBox::ButtonCode::PrimaryAction:
1994 markAllAttachmentsForSigning(
true);
1996 case KMessageBox::ButtonCode::SecondaryAction:
1997 markAllAttachmentsForSigning(
false);
2000 qCWarning(MESSAGECOMPOSER_LOG) <<
"Unhandled MessageBox response";
2005 case Kleo::Impossible: {
2008 "You have requested to sign this message, "
2009 "but no valid signing keys have been configured "
2010 "for this identity.");
2013 i18nc(
"@title:window",
"Send Unsigned?"),
2019 markAllAttachmentsForSigning(
false);
2025 if (!sign || !doSignCompletely) {
2026 if (cryptoWarningUnsigned(currentIdentity())) {
2028 const QString msg = sign && !doSignCompletely ?
i18n(
2029 "Some parts of this message will not be signed.\n"
2030 "Sending only partially signed messages might violate site policy.\n"
2031 "Sign all parts instead?")
2033 "This message will not be signed.\n"
2034 "Sending unsigned message might violate site policy.\n"
2035 "Sign message instead?");
2036 const QString buttonText = sign && !doSignCompletely ?
i18n(
"&Sign All Parts") :
i18n(
"&Sign");
2039 i18nc(
"@title:window",
"Unsigned-Message Warning"),
2046 case KMessageBox::ButtonCode::PrimaryAction:
2047 markAllAttachmentsForSigning(
true);
2049 case KMessageBox::ButtonCode::SecondaryAction:
2050 return sign || doSignCompletely;
2052 qCWarning(MESSAGECOMPOSER_LOG) <<
"Unhandled MessageBox response";
2057 return sign || doSignCompletely;
2060bool ComposerViewBase::determineWhetherToEncrypt(
bool doEncryptCompletely,
2062 bool encryptSomething,
2067 bool encrypt =
false;
2068 bool opportunistic =
false;
2069 switch (keyResolver->checkEncryptionPreferences(encryptSomething)) {
2071 if (!encryptSomething) {
2072 markAllAttachmentsForEncryption(
true);
2077 case Kleo::DontDoIt:
2080 case Kleo::AskOpportunistic:
2081 opportunistic =
true;
2088 "Valid trusted encryption keys were found for all recipients.\n"
2089 "Encrypt this message?")
2091 "Examination of the recipient's encryption preferences "
2092 "yielded that you be asked whether or not to encrypt "
2094 "Encrypt this message?");
2097 i18n(
"Encrypt Message?"),
2104 case KMessageBox::ButtonCode::PrimaryAction:
2105 markAllAttachmentsForEncryption(
true);
2107 case KMessageBox::ButtonCode::SecondaryAction:
2108 markAllAttachmentsForEncryption(
false);
2111 qCWarning(MESSAGECOMPOSER_LOG) <<
"Unhandled MessageBox response";
2116 case Kleo::Conflict: {
2120 "There are conflicting encryption preferences "
2121 "for these recipients.\n"
2122 "Encrypt this message?");
2127 i18n(
"Encrypt Message?"),
2134 case KMessageBox::ButtonCode::PrimaryAction:
2135 markAllAttachmentsForEncryption(
true);
2137 case KMessageBox::ButtonCode::SecondaryAction:
2138 markAllAttachmentsForEncryption(
false);
2141 qCWarning(MESSAGECOMPOSER_LOG) <<
"Unhandled MessageBox response";
2146 case Kleo::Impossible: {
2149 "You have requested to encrypt this message, "
2150 "and to encrypt a copy to yourself, "
2151 "but no valid trusted encryption keys have been "
2152 "configured for this identity.");
2155 i18nc(
"@title:window",
"Send Unencrypted?"),
2161 markAllAttachmentsForEncryption(
false);
2167 if (!encrypt || !doEncryptCompletely) {
2168 if (cryptoWarningUnencrypted(currentIdentity())) {
2171 "Some parts of this message will not be encrypted.\n"
2172 "Sending only partially encrypted messages might violate "
2173 "site policy and/or leak sensitive information.\n"
2174 "Encrypt all parts instead?")
2176 "This message will not be encrypted.\n"
2177 "Sending unencrypted messages might violate site policy and/or "
2178 "leak sensitive information.\n"
2179 "Encrypt messages instead?");
2180 const QString buttonText = !doEncryptCompletely ?
i18n(
"&Encrypt All Parts") :
i18n(
"&Encrypt");
2183 i18nc(
"@title:window",
"Unencrypted Message Warning"),
2190 case KMessageBox::ButtonCode::PrimaryAction:
2191 markAllAttachmentsForEncryption(
true);
2193 case KMessageBox::ButtonCode::SecondaryAction:
2194 return encrypt || doEncryptCompletely;
2196 qCWarning(MESSAGECOMPOSER_LOG) <<
"Unhandled MessageBox response";
2202 return encrypt || doEncryptCompletely;
2207 mSendLaterInfo.reset(info);
2212 return mSendLaterInfo.get();
2215void ComposerViewBase::addFollowupReminder(
const QString &messageId)
2218 if (mFollowUpDate.
isValid()) {
2220 job->setSubject(m_subject);
2221 job->setMessageId(messageId);
2223 job->setFollowUpReminderDate(mFollowUpDate);
2224 job->setCollectionToDo(mFollowUpCollection);
2230void ComposerViewBase::addSendLaterItem(
const Akonadi::Item &item)
2232 mSendLaterInfo->setItemId(item.
id());
2234 auto job =
new MessageComposer::SendLaterCreateJob(*mSendLaterInfo,
this);
2238bool ComposerViewBase::requestDeleveryConfirmation()
const
2240 return m_requestDeleveryConfirmation;
2243void ComposerViewBase::setRequestDeleveryConfirmation(
bool requestDeleveryConfirmation)
2245 m_requestDeleveryConfirmation = requestDeleveryConfirmation;
2253std::shared_ptr<Kleo::ExpiryChecker> ComposerViewBase::expiryChecker()
2255 if (!mExpiryChecker) {
2256 mExpiryChecker.reset(
new Kleo::ExpiryChecker{Kleo::ExpiryCheckerSettings{encryptOwnKeyNearExpiryWarningThresholdInDays(),
2257 encryptKeyNearExpiryWarningThresholdInDays(),
2258 encryptRootCertNearExpiryWarningThresholdInDays(),
2259 encryptChainCertNearExpiryWarningThresholdInDays()}});
2261 return mExpiryChecker;
2264#include "moc_composerviewbase.cpp"
Akonadi::Collection currentCollection() const
void setDefaultCollection(const Collection &collection)
Collection::List collections() const
void setPayload(const T &p)
void setMimeType(const QString &mimeType)
MoveToDefaultSentCollection
static SpecialMailCollections * self()
Akonadi::Collection defaultCollection(Type type) const
const Identity & identityForUoidOrDefault(uint uoid) const
QString templates() const
bool warnNotEncrypt() const
QString autocorrectionLanguage() const
QString replyToAddr() const
bool autocryptEnabled() const
bool encryptionOverride() const
QString vCardFile() const
QString rawText(bool *ok=nullptr, QString *errorMessage=nullptr) const
virtual QString errorString() const
virtual Q_SCRIPTABLE void start()=0
QList< Content * > attachments() const
const Headers::ContentType * contentType() const
QByteArray decodedContent() const
void setContent(const QByteArray &s)
const Headers::ContentDisposition * contentDisposition() const
QList< Content * > contents() const
QByteArray encodedContent(bool useCrLf=false) const
const Headers::ContentDescription * contentDescription() const
static QList< Mailbox > listFromUnicodeString(QStringView s)
bool setCurrentTransport(int transportId)
int currentTransportId() const
Transport * transportById(Transport::Id id, bool def=true) const
static TransportManager * self()
The AttachmentControllerBase class.
void addAttachment(const MessageCore::AttachmentPart::Ptr &part)
sets sign, encrypt, shows properties dialog if so configured
The AttachmentModel class.
void addAttachment(const QUrl &url, const QString &comment, bool sync)
Add the given attachment to the message.
void setCryptoOptions(bool sign, bool encrypt, Kleo::CryptoMessageFormat format, bool neverEncryptDrafts=false)
The following are various settings the user can modify when composing a message.
void setFrom(const QString &from)
Widgets for editing differ in client classes, so values are set before sending.
QString to() const
Header fields in recipients editor.
void send(MessageComposer::MessageSender::SendMethod method, MessageComposer::MessageSender::SaveIn saveIn, bool checkMailDispatcher=true)
Send the message with the specified method, saving it in the specified folder.
void setMessage(const KMime::Message::Ptr &newMsg, bool allowDecryption)
Set the message to be opened in the composer window, and set the internal data structures to keep tra...
void setAttachmentModel(MessageComposer::AttachmentModel *model)
The following are for setting the various options and widgets in the composer.
bool isComposing() const
Returns true if there is at least one composer job running.
void failed(const QString &errorMessage, MessageComposer::ComposerViewBase::FailedType type=Sending)
Message sending failed with given error message.
void sentSuccessfully(Akonadi::Item::Id id)
Message sending completed successfully.
void updateAutoSave()
Enables/disables autosaving depending on the value of the autosave interval.
void cleanupAutoSave()
Stop autosaving and delete the autosaved message.
ComposerViewBase::MissingAttachment checkForMissingAttachments(const QStringList &attachmentKeywords)
Check if the mail has references to attachments, but no attachments are added to it.
void disableHtml(MessageComposer::ComposerViewBase::Confirmation)
Enabling or disabling HTML in the editor is affected by various client options, so when that would ot...
void setAutoSaveFileName(const QString &fileName)
Sets the filename to use when autosaving something.
void modified(bool isModified)
The composer was modified.
void autoSaveMessage()
Save the message.
void setAutoSave(bool isAutoSave)
Sets if this message being composed is an auto-saved message if so, might need different handling,...
A job to resolve nicknames, distribution lists and email addresses for queued emails.
QStringList expandedReplyTo() const
Returns the expanded Reply-To field.
QString expandedFrom() const
Returns the expanded From field.
QStringList expandedCc() const
Returns the expanded CC field.
QStringList expandedBcc() const
Returns the expanded Bcc field.
QStringList expandedTo() const
Returns the expanded To field.
The FollowupReminderCreateJob class.
The InfoPart class contains the message header.
QStringList cc
Carbon copy: The email address and optionally the name of the secondary recipients.
QString from
The email address and optionally the name of the author of the mail.
QStringList to
The email address and optionally the name of the primary recipients.
QString fcc
The name of a file, to which a copy of the sent message should be appended.
QStringList bcc
Blind Carbon copy: The email address and optionally the name of the secondary recipients.
The RecipientsEditor class.
void removeRecipient(const QString &recipient, Recipient::Type type)
Removes the recipient provided it can be found and has the given type.
bool addRecipient(const QString &recipient, Recipient::Type type)
Adds a recipient (or multiple recipients) to one line of the editor.
The RichTextComposerNg class.
The SignatureController class Controls signature (the footer thing, not the crypto thing) operations ...
void applySignature(const KIdentityManagementCore::Signature &signature)
Adds the given signature to the editor, taking user preferences into account.
A class that encapsulates an attachment.
QSharedPointer< AttachmentPart > Ptr
Defines a pointer to an attachment object.
QList< KMime::Content * > attachmentsOfExtraContents() const
Returns a list of attachments of attached extra content nodes.
Parses messages and generates HTML display code out of them.
void parseObjectTree(KMime::Content *node, bool parseOnlySingleNode=false)
Parse beginning at a given node and recursively parsing the children of that node and it's next sibli...
QString plainTextContent() const
The text of the message, ie.
QString htmlContent() const
Similar to plainTextContent(), but returns the HTML source of the first text/html MIME part.
A very simple ObjectTreeSource.
void add(const QString &entry)
static RecentAddresses * self(KConfig *config=nullptr)
QString currentDictionary() const
KCODECS_EXPORT QString normalizeAddressesAndEncodeIdn(const QString &str)
QString i18nc(const char *context, const char *text, const TYPE &arg...)
QString i18n(const char *text, const TYPE &arg...)
AKONADI_MIME_EXPORT void copyMessageFlags(KMime::Message &from, Akonadi::Item &to)
KCALUTILS_EXPORT QString errorMessage(const KCalendarCore::Exception &exception)
QAction * create(GameStandardAction id, const QObject *recvr, const char *slot, QObject *parent)
QAction * end(const QObject *recvr, const char *slot, QObject *parent)
KIOCORE_EXPORT TransferJob * get(const QUrl &url, LoadType reload=NoReload, JobFlags flags=DefaultFlags)
PostalAddress address(const QVariant &location)
ButtonCode warningContinueCancel(QWidget *parent, const QString &text, const QString &title=QString(), const KGuiItem &buttonContinue=KStandardGuiItem::cont(), const KGuiItem &buttonCancel=KStandardGuiItem::cancel(), const QString &dontAskAgainName=QString(), Options options=Notify)
ButtonCode warningTwoActions(QWidget *parent, const QString &text, const QString &title, const KGuiItem &primaryAction, const KGuiItem &secondaryAction, const QString &dontAskAgainName=QString(), Options options=Options(Notify|Dangerous))
void error(QWidget *parent, const QString &text, const QString &title, const KGuiItem &buttonOk, Options options=Notify)
ButtonCode warningTwoActionsCancel(QWidget *parent, const QString &text, const QString &title, const KGuiItem &primaryAction, const KGuiItem &secondaryAction, const KGuiItem &cancelAction=KStandardGuiItem::cancel(), const QString &dontAskAgainName=QString(), Options options=Options(Notify|Dangerous))
KIOCORE_EXPORT QString dir(const QString &fileClass)
Simple interface that both EncryptJob and SignEncryptJob implement so the composer can extract some e...
void removePrivateHeaderFields(const KMime::Message::Ptr &message, bool cleanUpHeader)
Removes all private header fields (e.g.
const char * constData() const const
bool isEmpty() const const
bool isValid(int year, int month, int day)
QDateTime currentDateTime()
QStringList entryList(Filters filters, SortFlags sort) const const
bool mkpath(const QString &dirPath) const const
bool remove(const QString &fileName)
void setNameFilters(const QStringList &nameFilters)
int exec(ProcessEventsFlags flags)
bool loadFromData(QByteArrayView data, const char *format)
void append(QList< T > &&value)
const_reference at(qsizetype i) const const
bool contains(const AT &value) const const
qsizetype count() const const
bool isEmpty() const const
void push_back(parameter_type value)
qsizetype removeAll(const AT &t)
void reserve(qsizetype size)
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
QVariant property(const char *name) const const
T qobject_cast(QObject *object)
QString writableLocation(StandardLocation type)
QString fromLatin1(QByteArrayView str)
QString fromUtf8(QByteArrayView str)
bool isEmpty() const const
QString number(double n, char format, int precision)
QString & replace(QChar before, QChar after, Qt::CaseSensitivity cs)
QByteArray toLatin1() const const
qlonglong toLongLong(bool *ok, int base) const const
QByteArray toUtf8() const const
bool contains(QLatin1StringView str, Qt::CaseSensitivity cs) const const
QString join(QChar separator) const const
void setHtml(const QString &text)
void setPlainText(const QString &text)
QUrl fromLocalFile(const QString &localFile)
QString toString(StringFormat mode) const const
QVariant fromValue(T &&value)