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) {
721 key.protocol() == GpgME::OpenPGP ?
i18nc(
"@title:window",
"OpenPGP Key Expired") :
i18nc(
"@title:window",
"S/MIME Certificate Expired");
723 title = key.protocol() == GpgME::OpenPGP ?
i18nc(
"@title:window",
"OpenPGP Key Expires Soon")
724 :
i18nc(
"@title:window",
"S/MIME Certificate Expires Soon");
733 new Kleo::KeyResolver(
true, showKeyApprovalDialog(),
id.pgpAutoEncrypt(), m_cryptoMessageFormat, expiryChecker()));
735 keyResolver->setAutocryptEnabled(autocryptEnabled());
736 keyResolver->setAkonadiLookupEnabled(m_akonadiLookupEnabled);
741 bool signSomething = m_sign;
742 bool doSignCompletely = m_sign;
743 bool encryptSomething = m_encrypt;
744 bool doEncryptCompletely = m_encrypt;
747 if (!
id.pgpEncryptionKey().isEmpty()) {
750 if (!
id.smimeEncryptionKey().isEmpty()) {
753 if (canceled || keyResolver->setEncryptToSelfKeys(encryptToSelfKeys) != Kleo::Ok) {
754 qCDebug(MESSAGECOMPOSER_LOG) <<
"Failed to set encryptoToSelf keys!";
759 if (!
id.pgpSigningKey().isEmpty()) {
762 if (!
id.smimeSigningKey().isEmpty()) {
765 if (canceled || keyResolver->setSigningKeys(signKeys) != Kleo::Ok) {
766 qCDebug(MESSAGECOMPOSER_LOG) <<
"Failed to set signing keys!";
770 if (m_attachmentModel) {
771 const auto attachments = m_attachmentModel->attachments();
773 if (attachment->isSigned()) {
774 signSomething =
true;
776 doEncryptCompletely =
false;
778 if (attachment->isEncrypted()) {
779 encryptSomething =
true;
781 doSignCompletely =
false;
786 const QStringList recipients = mExpandedTo + mExpandedCc;
789 keyResolver->setPrimaryRecipients(recipients);
790 keyResolver->setSecondaryRecipients(bcc);
794 signSomething = determineWhetherToSign(doSignCompletely, keyResolver.data(), signSomething, result, canceled);
797 qCDebug(MESSAGECOMPOSER_LOG) <<
"determineWhetherToSign: failed to resolve keys! oh noes";
803 wasCanceled = canceled;
808 encryptSomething = determineWhetherToEncrypt(doEncryptCompletely, keyResolver.data(), encryptSomething, signSomething, result, canceled);
811 qCDebug(MESSAGECOMPOSER_LOG) <<
"determineWhetherToEncrypt: failed to resolve keys! oh noes";
818 wasCanceled = canceled;
825 if (!signSomething && !encryptSomething) {
827 if (m_cryptoMessageFormat & Kleo::OpenPGPMIMEFormat) {
828 composer->setAutocryptEnabled(autocryptEnabled());
829 if (keyResolver->encryptToSelfKeysFor(Kleo::OpenPGPMIMEFormat).size() > 0) {
830 composer->setSenderEncryptionKey(keyResolver->encryptToSelfKeysFor(Kleo::OpenPGPMIMEFormat)[0]);
833 composers.
append(composer);
838 const Kleo::Result kpgpResult = keyResolver->resolveAllKeys(signSomething, encryptSomething);
839 if (kpgpResult == Kleo::Canceled || canceled) {
840 qCDebug(MESSAGECOMPOSER_LOG) <<
"resolveAllKeys: one key resolution canceled by user";
842 }
else if (kpgpResult != Kleo::Ok) {
844 qCDebug(MESSAGECOMPOSER_LOG) <<
"resolveAllKeys: failed to resolve keys! oh noes";
849 qCDebug(MESSAGECOMPOSER_LOG) <<
"done resolving keys.";
851 if (encryptSomething || signSomething) {
852 Kleo::CryptoMessageFormat concreteFormat = Kleo::AutoFormat;
853 for (
unsigned int i = 0; i < numConcreteCryptoMessageFormats; ++i) {
854 concreteFormat = concreteCryptoMessageFormats[i];
855 const auto encData = keyResolver->encryptionItems(concreteFormat);
856 if (encData.empty()) {
860 if (!(concreteFormat & m_cryptoMessageFormat)) {
866 if (encryptSomething || autocryptEnabled()) {
867 auto end(encData.end());
870 for (
auto it = encData.begin(); it != end; ++it) {
871 QPair<QStringList, std::vector<GpgME::Key>> p(it->recipients, it->keys);
873 qCDebug(MESSAGECOMPOSER_LOG) <<
"got resolved keys for:" << it->recipients;
875 composer->setEncryptionKeys(data);
876 if (concreteFormat & Kleo::OpenPGPMIMEFormat && autocryptEnabled()) {
877 composer->setAutocryptEnabled(autocryptEnabled());
878 composer->setSenderEncryptionKey(keyResolver->encryptToSelfKeysFor(concreteFormat)[0]);
880 bool specialGnupgHome = addKeysToContext(
dir.path(), data, keyResolver->useAutocrypt());
881 if (specialGnupgHome) {
882 dir.setAutoRemove(
false);
883 composer->setGnupgHome(
dir.path());
890 std::vector<GpgME::Key> signingKeys = keyResolver->signingKeys(concreteFormat);
891 composer->setSigningKeys(signingKeys);
894 composer->setCryptoMessageFormat(concreteFormat);
895 composer->setSignAndEncrypt(signSomething, encryptSomething);
897 composers.
append(composer);
901 composers.
append(composer);
903 markAllAttachmentsForSigning(
false);
904 markAllAttachmentsForEncryption(
false);
907 if (composers.
isEmpty() && (signSomething || encryptSomething)) {
908 Q_ASSERT_X(
false,
"ComposerViewBase::generateCryptoMessages",
"No concrete sign or encrypt method selected");
916 globalPart->setParentWidgetForGui(m_parentWidget);
917 globalPart->setMDNRequested(m_mdnRequested);
918 globalPart->setRequestDeleveryConfirmation(m_requestDeleveryConfirmation);
929 if (m_fccCollection.
isValid()) {
935 if (expansion == UseExpandedRecipients) {
936 infoPart->setFrom(mExpandedFrom);
937 infoPart->setTo(mExpandedTo);
938 infoPart->setCc(mExpandedCc);
939 infoPart->setBcc(mExpandedBcc);
940 infoPart->setReplyTo(mExpandedReplyTo);
942 infoPart->setFrom(from());
943 infoPart->setTo(m_recipientsEditor->recipientStringList(MessageComposer::Recipient::To));
944 infoPart->setCc(m_recipientsEditor->recipientStringList(MessageComposer::Recipient::Cc));
945 infoPart->setBcc(m_recipientsEditor->recipientStringList(MessageComposer::Recipient::Bcc));
946 infoPart->setReplyTo(m_recipientsEditor->recipientStringList(MessageComposer::Recipient::ReplyTo));
948 infoPart->setSubject(subject());
949 infoPart->setUserAgent(QStringLiteral(
"KMail"));
950 infoPart->setUrgent(m_urgent);
952 if (
auto inReplyTo = m_msg->inReplyTo(
false)) {
953 infoPart->setInReplyTo(inReplyTo->asUnicodeString());
956 if (
auto references = m_msg->references(
false)) {
957 infoPart->setReferences(references->asUnicodeString());
961 if (
auto hdr = m_msg->headerByType(
"X-KMail-SignatureActionEnabled")) {
964 if (
auto hdr = m_msg->headerByType(
"X-KMail-EncryptActionEnabled")) {
967 if (
auto hdr = m_msg->headerByType(
"X-KMail-CryptoMessageFormat")) {
970 if (
auto hdr = m_msg->headerByType(
"X-KMail-UnExpanded-To")) {
973 if (
auto hdr = m_msg->headerByType(
"X-KMail-UnExpanded-CC")) {
976 if (
auto hdr = m_msg->headerByType(
"X-KMail-UnExpanded-BCC")) {
979 if (
auto hdr = m_msg->headerByType(
"X-KMail-UnExpanded-Reply-To")) {
982 if (
auto hdr = m_msg->organization(
false)) {
985 if (
auto hdr = m_msg->headerByType(
"X-KMail-Identity")) {
988 if (
auto hdr = m_msg->headerByType(
"X-KMail-Transport")) {
991 if (
auto hdr = m_msg->headerByType(
"X-KMail-Fcc")) {
994 if (
auto hdr = m_msg->headerByType(
"X-KMail-Drafts")) {
997 if (
auto hdr = m_msg->headerByType(
"X-KMail-Templates")) {
1000 if (
auto hdr = m_msg->headerByType(
"X-KMail-Link-Message")) {
1003 if (
auto hdr = m_msg->headerByType(
"X-KMail-Link-Type")) {
1006 if (
auto hdr = m_msg->headerByType(
"X-Face")) {
1009 if (
auto hdr = m_msg->headerByType(
"Face")) {
1012 if (
auto hdr = m_msg->headerByType(
"X-KMail-FccDisabled")) {
1015 if (
auto hdr = m_msg->headerByType(
"X-KMail-Identity-Name")) {
1018 if (
auto hdr = m_msg->headerByType(
"X-KMail-Transport-Name")) {
1022 infoPart->setExtraHeaders(extras);
1025void ComposerViewBase::slotSendComposeResult(
KJob *job)
1029 if (composer->error() != MessageComposer::Composer::NoError) {
1030 qCDebug(MESSAGECOMPOSER_LOG) <<
"compose job might have error: " << job->
error() <<
" errorString: " << job->
errorString();
1033 if (composer->error() == MessageComposer::Composer::NoError) {
1034 Q_ASSERT(m_composers.
contains(composer));
1036 qCDebug(MESSAGECOMPOSER_LOG) <<
"NoError.";
1037 const int numberOfMessage(composer->resultMessages().size());
1038 for (
int i = 0; i < numberOfMessage; ++i) {
1039 if (mSaveIn == MessageComposer::MessageSender::SaveInNone) {
1040 queueMessage(composer->resultMessages().at(i), composer);
1042 saveMessage(composer->resultMessages().at(i), mSaveIn);
1045 saveRecentAddresses(composer->resultMessages().at(0));
1046 }
else if (composer->error() == MessageComposer::Composer::UserCancelledError) {
1049 qCDebug(MESSAGECOMPOSER_LOG) <<
"UserCancelledError.";
1052 qCDebug(MESSAGECOMPOSER_LOG) <<
"other Error." << composer->error();
1054 if (composer->error() == MessageComposer::Composer::BugError) {
1055 msg =
i18n(
"Could not compose message: %1 \n Please report this bug.", job->
errorString());
1062 if (!composer->gnupgHome().isEmpty()) {
1063 QDir dir(composer->gnupgHome());
1064 dir.removeRecursively();
1072 KConfig *config = MessageComposer::MessageComposerSettings::self()->config();
1073 if (
auto to = msg->to(
false)) {
1074 const auto toAddresses =
to->mailboxes();
1075 for (
const auto &address : toAddresses) {
1079 if (
auto cc = msg->cc(
false)) {
1080 const auto ccAddresses = cc->mailboxes();
1081 for (
const auto &address : ccAddresses) {
1085 if (
auto bcc = msg->bcc(
false)) {
1086 const auto bccAddresses = bcc->mailboxes();
1087 for (
const auto &address : bccAddresses) {
1097 qjob->setMessage(message);
1098 qjob->transportAttribute().setTransportId(infoPart->transportId());
1099 if (mSendMethod == MessageComposer::MessageSender::SendLater) {
1103 if (message->hasHeader(
"X-KMail-FccDisabled")) {
1109 qjob->sentBehaviourAttribute().setMoveToCollection(sentCollection);
1115 if (transport && transport->specifySenderOverwriteAddress()) {
1116 qjob->addressAttribute().setFrom(
1124 qjob->addressAttribute().setTo(MessageComposer::Util::cleanUpEmailListAndEncoding(realTo->asUnicodeString().split(
QLatin1Char(
'%'))));
1125 message->removeHeader(
"X-KMail-EncBccRecipients");
1126 message->assemble();
1127 qCDebug(MESSAGECOMPOSER_LOG) <<
"sending with-bcc encr mail to a/n recipient:" << qjob->addressAttribute().to();
1129 qjob->addressAttribute().setTo(MessageComposer::Util::cleanUpEmailListAndEncoding(infoPart->
to()));
1130 qjob->addressAttribute().setCc(MessageComposer::Util::cleanUpEmailListAndEncoding(infoPart->
cc()));
1131 qjob->addressAttribute().setBcc(MessageComposer::Util::cleanUpEmailListAndEncoding(infoPart->
bcc()));
1133 if (m_requestDeleveryConfirmation) {
1134 qjob->addressAttribute().setDeliveryStatusNotification(
true);
1136 MessageComposer::Util::addSendReplyForwardAction(message, qjob);
1139 MessageComposer::Util::addCustomHeaders(message, m_customHeader);
1140 message->assemble();
1142 m_pendingQueueJobs++;
1145 qCDebug(MESSAGECOMPOSER_LOG) <<
"Queued a message.";
1148void ComposerViewBase::slotQueueResult(
KJob *job)
1150 m_pendingQueueJobs--;
1152 qCDebug(MESSAGECOMPOSER_LOG) <<
"mPendingQueueJobs" << m_pendingQueueJobs;
1153 Q_ASSERT(m_pendingQueueJobs >= 0);
1156 qCDebug(MESSAGECOMPOSER_LOG) <<
"Failed to queue a message:" << job->
errorString();
1160 const QString msg =
i18n(
"There were problems trying to queue the message for sending: %1", job->
errorString());
1162 if (m_pendingQueueJobs == 0) {
1168 if (m_pendingQueueJobs == 0) {
1169 addFollowupReminder(qjob->message()->messageID(
false)->asUnicodeString());
1174void ComposerViewBase::initAutoSave()
1176 qCDebug(MESSAGECOMPOSER_LOG) <<
"initialising autosave";
1180 if (!dataDirectory.exists(QStringLiteral(
"autosave"))) {
1181 qCDebug(MESSAGECOMPOSER_LOG) <<
"Creating autosave directory.";
1182 dataDirectory.mkdir(QStringLiteral(
"autosave"));
1186 if (m_autoSaveUUID.
isEmpty()) {
1195 return mFollowUpCollection;
1198void ComposerViewBase::setFollowUpCollection(
const Akonadi::Collection &followUpCollection)
1200 mFollowUpCollection = followUpCollection;
1203QDate ComposerViewBase::followUpDate()
const
1205 return mFollowUpDate;
1208void ComposerViewBase::setFollowUpDate(
const QDate &followUpDate)
1210 mFollowUpDate = followUpDate;
1215 return m_dictionary;
1220 m_dictionary = dictionary;
1225 if (m_autoSaveInterval == 0) {
1226 delete m_autoSaveTimer;
1227 m_autoSaveTimer =
nullptr;
1229 if (!m_autoSaveTimer) {
1230 m_autoSaveTimer =
new QTimer(
this);
1231 if (m_parentWidget) {
1237 m_autoSaveTimer->
start(m_autoSaveInterval);
1243 delete m_autoSaveTimer;
1244 m_autoSaveTimer =
nullptr;
1245 if (!m_autoSaveUUID.
isEmpty()) {
1246 qCDebug(MESSAGECOMPOSER_LOG) <<
"deleting autosave files" << m_autoSaveUUID;
1257 qCDebug(MESSAGECOMPOSER_LOG) <<
"There are" << autoSaveFiles.
count() <<
"to be deleted.";
1260 for (
const QString &file : autoSaveFiles) {
1261 autoSaveDir.
remove(file);
1263 m_autoSaveUUID.
clear();
1270 qCDebug(MESSAGECOMPOSER_LOG) <<
"Autosaving message";
1272 if (m_autoSaveTimer) {
1273 m_autoSaveTimer->
stop();
1278 qCDebug(MESSAGECOMPOSER_LOG) <<
"Autosave: Called while composer active; ignoring. Number of composer " << m_composers.
count();
1283 fillComposer(composer);
1285 composer->setAutocryptEnabled(autocryptEnabled());
1286 m_composers.
append(composer);
1293 m_autoSaveUUID = fileName;
1298void ComposerViewBase::slotAutoSaveComposeResult(
KJob *job)
1302 Q_ASSERT(
dynamic_cast<Composer *
>(job));
1303 auto composer =
static_cast<Composer *
>(job);
1305 if (composer->
error() == Composer::NoError) {
1306 Q_ASSERT(m_composers.
contains(composer));
1310 qCDebug(MESSAGECOMPOSER_LOG) <<
"NoError.";
1311 writeAutoSaveToDisk(composer->resultMessages().constFirst());
1312 Q_ASSERT(composer->resultMessages().size() == 1);
1314 if (m_autoSaveInterval > 0) {
1317 }
else if (composer->
error() == MessageComposer::Composer::UserCancelledError) {
1320 qCDebug(MESSAGECOMPOSER_LOG) <<
"UserCancelledError.";
1323 qCDebug(MESSAGECOMPOSER_LOG) <<
"other Error.";
1334 const QString filename = autosavePath + m_autoSaveUUID;
1337 qCDebug(MESSAGECOMPOSER_LOG) <<
"Writing message to disk as" << filename;
1342 if (file.write(message->encodedContent()) !=
static_cast<qint64
>(message->encodedContent().size())) {
1345 if (!file.commit()) {
1354 qCWarning(MESSAGECOMPOSER_LOG) <<
"Auto saving failed:" <<
errorMessage << file.errorString() <<
" m_autoSaveUUID" << m_autoSaveUUID;
1355 if (!m_autoSaveErrorShown) {
1357 i18n(
"Autosaving the message as %1 failed.\n"
1362 file.errorString()),
1363 i18nc(
"@title:window",
"Autosaving Message Failed"));
1366 m_autoSaveErrorShown =
true;
1370 m_autoSaveErrorShown =
false;
1376void ComposerViewBase::saveMessage(
const KMime::Message::Ptr &message, MessageComposer::MessageSender::SaveIn saveIn)
1379 const auto identity = currentIdentity();
1381 if (!identity.
isNull()) {
1382 if (
auto header = message->headerByType(
"X-KMail-Fcc")) {
1383 const int sentCollectionId = header->asUnicodeString().toInt();
1385 message->removeHeader(
"X-KMail-Fcc");
1389 MessageComposer::Util::addCustomHeaders(message, m_customHeader);
1391 message->assemble();
1394 item.
setMimeType(QStringLiteral(
"message/rfc822"));
1398 if (!identity.
isNull()) {
1400 case MessageComposer::MessageSender::SaveInTemplates:
1405 case MessageComposer::MessageSender::SaveInDrafts:
1410 case MessageComposer::MessageSender::SaveInOutbox:
1413 case MessageComposer::MessageSender::SaveInNone:
1422 target = defaultSpecialTarget();
1425 ++m_pendingQueueJobs;
1429void ComposerViewBase::slotSaveMessage(
KJob *job)
1434 target = defaultSpecialTarget();
1438 target = defaultSpecialTarget();
1445 ++m_pendingQueueJobs;
1452 case MessageComposer::MessageSender::SaveInNone:
1454 case MessageComposer::MessageSender::SaveInDrafts:
1457 case MessageComposer::MessageSender::SaveInTemplates:
1460 case MessageComposer::MessageSender::SaveInOutbox:
1468void ComposerViewBase::slotCreateItemResult(
KJob *job)
1470 --m_pendingQueueJobs;
1471 qCDebug(MESSAGECOMPOSER_LOG) <<
"mPendingCreateItemJobs" << m_pendingQueueJobs;
1472 Q_ASSERT(m_pendingQueueJobs >= 0);
1475 qCWarning(MESSAGECOMPOSER_LOG) <<
"Failed to save a message:" << job->
errorString();
1481 if (mSendLaterInfo) {
1486 addSendLaterItem(item);
1490 if (m_pendingQueueJobs == 0) {
1498 qCDebug(MESSAGECOMPOSER_LOG) <<
"adding attachment with url:" << url;
1500 m_attachmentController->addAttachmentUrlSync(url);
1510 attachment->setName(name);
1511 attachment->setFileName(filename);
1512 attachment->setData(data);
1513 attachment->setCharset(charset.
toLatin1());
1514 attachment->setMimeType(mimeType);
1521void ComposerViewBase::addAttachmentPart(
KMime::Content *partToAttach)
1533 part->setDescription(cd->asUnicodeString());
1536 if (ct->hasParameter(
"name")) {
1537 part->setName(ct->parameter(
"name"));
1541 part->setFileName(cd->filename());
1542 part->setInline(cd->disposition() == KMime::Headers::CDinline);
1544 if (part->name().isEmpty() && !part->fileName().isEmpty()) {
1545 part->setName(part->fileName());
1547 if (part->fileName().isEmpty() && !part->name().isEmpty()) {
1548 part->setFileName(part->name());
1555 fillComposer(composer, UseUnExpandedRecipients,
false);
1558void ComposerViewBase::fillComposer(
MessageComposer::Composer *composer, ComposerViewBase::RecipientExpansion expansion,
bool autoresize)
1560 fillGlobalPart(composer->globalPart());
1561 m_editor->fillComposerTextPart(composer->textPart());
1562 fillInfoPart(composer->infoPart(), expansion);
1563 if (m_attachmentModel) {
1564 composer->addAttachmentParts(m_attachmentModel->attachments(), autoresize);
1571 if (m_recipientsEditor) {
1572 return MessageComposer::Util::cleanedUpHeaderString(m_recipientsEditor->recipientString(MessageComposer::Recipient::To));
1578QString ComposerViewBase::cc()
const
1580 if (m_recipientsEditor) {
1581 return MessageComposer::Util::cleanedUpHeaderString(m_recipientsEditor->recipientString(MessageComposer::Recipient::Cc));
1587QString ComposerViewBase::bcc()
const
1589 if (m_recipientsEditor) {
1590 return MessageComposer::Util::cleanedUpHeaderString(m_recipientsEditor->recipientString(MessageComposer::Recipient::Bcc));
1595QString ComposerViewBase::from()
const
1597 return MessageComposer::Util::cleanedUpHeaderString(m_from);
1600QString ComposerViewBase::replyTo()
const
1602 if (m_recipientsEditor) {
1603 return MessageComposer::Util::cleanedUpHeaderString(m_recipientsEditor->recipientString(MessageComposer::Recipient::ReplyTo));
1608QString ComposerViewBase::subject()
const
1610 return MessageComposer::Util::cleanedUpHeaderString(m_subject);
1618bool ComposerViewBase::autocryptEnabled()
const
1623void ComposerViewBase::setParentWidgetForGui(
QWidget *w)
1630 m_attachmentController = controller;
1635 return m_attachmentController;
1640 m_attachmentModel = model;
1645 return m_attachmentModel;
1650 m_recipientsEditor = recEditor;
1655 return m_recipientsEditor;
1660 m_signatureController = sigController;
1665 return m_signatureController;
1670 m_identityCombo = identCombo;
1675 return m_identityCombo;
1680 MessageComposer::Recipient::Type type)
1685 case MessageComposer::Recipient::Bcc: {
1686 oldIdentList = oldIdent.
bcc();
1687 newIdentList = ident.
bcc();
1690 case MessageComposer::Recipient::Cc: {
1691 oldIdentList = oldIdent.
cc();
1692 newIdentList = ident.
cc();
1695 case MessageComposer::Recipient::ReplyTo: {
1700 case MessageComposer::Recipient::To:
1701 case MessageComposer::Recipient::Undefined:
1705 if (oldIdentList != newIdentList) {
1713 m_recipientsEditor->
addRecipient(recipient.prettyAddress(), type);
1715 m_recipientsEditor->setFocusBottom();
1721 updateRecipients(ident, oldIdent, MessageComposer::Recipient::Bcc);
1722 updateRecipients(ident, oldIdent, MessageComposer::Recipient::Cc);
1723 updateRecipients(ident, oldIdent, MessageComposer::Recipient::ReplyTo);
1728 const bool replaced = editor()->composerSignature()->replaceSignature(oldSig, newSig);
1734 attachmentController()->setIdentityHasOwnVcard(!vcardFileName.
isEmpty());
1735 attachmentController()->setAttachOwnVcard(ident.
attachVcard());
1743 m_editor->
document()->setModified(
false);
1753 m_transport = transpCombo;
1763 m_identMan = identMan;
1776 m_fccCollection = fccCollection;
1779 connect(checkFccCollectionJob, &
KJob::result,
this, &ComposerViewBase::slotFccCollectionCheckResult);
1782void ComposerViewBase::slotFccCollectionCheckResult(
KJob *job)
1785 qCWarning(MESSAGECOMPOSER_LOG) <<
" void ComposerViewBase::slotFccCollectionCheckResult(KJob *job) error " << job->
errorString();
1790 m_fccCollection = sentMailCol;
1810void ComposerViewBase::setSubject(
const QString &subject)
1812 m_subject = subject;
1813 if (mSendLaterInfo) {
1814 mSendLaterInfo->setSubject(m_subject);
1815 mSendLaterInfo->setTo(
to());
1819void ComposerViewBase::setAutoSaveInterval(
int interval)
1821 m_autoSaveInterval = interval;
1827 m_encrypt = encrypt;
1828 m_cryptoMessageFormat = format;
1829 m_neverEncrypt = neverEncryptDrafts;
1832void ComposerViewBase::setMDNRequested(
bool mdnRequested)
1834 m_mdnRequested = mdnRequested;
1837void ComposerViewBase::setUrgent(
bool urgent)
1842int ComposerViewBase::autoSaveInterval()
const
1844 return m_autoSaveInterval;
1850 if (
KMime::Content *n = Util::findTypeInMessage(root,
"multipart",
"alternative")) {
1853 const auto nodes = parentnode->
contents();
1854 for (
auto node : nodes) {
1855 if (node->contentType()->isImage()) {
1856 qCDebug(MESSAGECOMPOSER_LOG) <<
"found image in multipart/related : " << node->contentType()->name();
1859 m_editor->composerControler()->composerImages()->loadImage(
1862 node->contentType()->name());
1870bool ComposerViewBase::inlineSigningEncryptionSelected()
const
1872 if (!m_sign && !m_encrypt) {
1875 return m_cryptoMessageFormat == Kleo::InlineOpenPGPFormat;
1878bool ComposerViewBase::hasMissingAttachments(
const QStringList &attachmentKeywords)
1880 if (attachmentKeywords.
isEmpty()) {
1883 if (m_attachmentModel && m_attachmentModel->rowCount() > 0) {
1887 return MessageComposer::Util::hasMissingAttachments(attachmentKeywords, m_editor->
document(), subject());
1892 if (!hasMissingAttachments(attachmentKeywords)) {
1893 return NoMissingAttachmentFound;
1897 i18n(
"The message you have composed seems to refer to an "
1898 "attached file but you have not attached anything.\n"
1899 "Do you want to attach a file to your message?"),
1900 i18nc(
"@title:window",
"File Attachment Reminder"),
1904 return FoundMissingAttachmentAndCancel;
1906 if (rc == KMessageBox::ButtonCode::PrimaryAction) {
1907 m_attachmentController->showAddAttachmentFileDialog();
1908 return FoundMissingAttachmentAndAddedAttachment;
1911 return FoundMissingAttachmentAndSending;
1914void ComposerViewBase::markAllAttachmentsForSigning(
bool sign)
1916 if (m_attachmentModel) {
1917 const auto attachments = m_attachmentModel->attachments();
1919 attachment->setSigned(sign);
1924void ComposerViewBase::markAllAttachmentsForEncryption(
bool encrypt)
1926 if (m_attachmentModel) {
1927 const auto attachments = m_attachmentModel->attachments();
1929 attachment->setEncrypted(encrypt);
1934bool ComposerViewBase::determineWhetherToSign(
bool doSignCompletely,
Kleo::KeyResolver *keyResolver,
bool signSomething,
bool &result,
bool &canceled)
1937 switch (keyResolver->checkSigningPreferences(signSomething)) {
1939 if (!signSomething) {
1940 markAllAttachmentsForSigning(
true);
1945 case Kleo::DontDoIt:
1948 case Kleo::AskOpportunistic:
1954 "Examination of the recipient's signing preferences "
1955 "yielded that you be asked whether or not to sign "
1957 "Sign this message?");
1960 i18nc(
"@title:window",
"Sign Message?"),
1967 case KMessageBox::ButtonCode::PrimaryAction:
1968 markAllAttachmentsForSigning(
true);
1970 case KMessageBox::ButtonCode::SecondaryAction:
1971 markAllAttachmentsForSigning(
false);
1974 qCWarning(MESSAGECOMPOSER_LOG) <<
"Unhandled MessageBox response";
1979 case Kleo::Conflict: {
1983 "There are conflicting signing preferences "
1984 "for these recipients.\n"
1985 "Sign this message?");
1988 i18nc(
"@title:window",
"Sign Message?"),
1995 case KMessageBox::ButtonCode::PrimaryAction:
1996 markAllAttachmentsForSigning(
true);
1998 case KMessageBox::ButtonCode::SecondaryAction:
1999 markAllAttachmentsForSigning(
false);
2002 qCWarning(MESSAGECOMPOSER_LOG) <<
"Unhandled MessageBox response";
2007 case Kleo::Impossible: {
2010 "You have requested to sign this message, "
2011 "but no valid signing keys have been configured "
2012 "for this identity.");
2015 i18nc(
"@title:window",
"Send Unsigned?"),
2021 markAllAttachmentsForSigning(
false);
2027 if (!sign || !doSignCompletely) {
2028 if (cryptoWarningUnsigned(currentIdentity())) {
2030 const QString msg = sign && !doSignCompletely ?
i18n(
2031 "Some parts of this message will not be signed.\n"
2032 "Sending only partially signed messages might violate site policy.\n"
2033 "Sign all parts instead?")
2035 "This message will not be signed.\n"
2036 "Sending unsigned message might violate site policy.\n"
2037 "Sign message instead?");
2038 const QString buttonText = sign && !doSignCompletely ?
i18n(
"&Sign All Parts") :
i18n(
"&Sign");
2041 i18nc(
"@title:window",
"Unsigned-Message Warning"),
2048 case KMessageBox::ButtonCode::PrimaryAction:
2049 markAllAttachmentsForSigning(
true);
2051 case KMessageBox::ButtonCode::SecondaryAction:
2052 return sign || doSignCompletely;
2054 qCWarning(MESSAGECOMPOSER_LOG) <<
"Unhandled MessageBox response";
2059 return sign || doSignCompletely;
2062bool ComposerViewBase::determineWhetherToEncrypt(
bool doEncryptCompletely,
2064 bool encryptSomething,
2069 bool encrypt =
false;
2070 bool opportunistic =
false;
2071 switch (keyResolver->checkEncryptionPreferences(encryptSomething)) {
2073 if (!encryptSomething) {
2074 markAllAttachmentsForEncryption(
true);
2079 case Kleo::DontDoIt:
2082 case Kleo::AskOpportunistic:
2083 opportunistic =
true;
2090 "Valid trusted encryption keys were found for all recipients.\n"
2091 "Encrypt this message?")
2093 "Examination of the recipient's encryption preferences "
2094 "yielded that you be asked whether or not to encrypt "
2096 "Encrypt this message?");
2099 i18n(
"Encrypt Message?"),
2106 case KMessageBox::ButtonCode::PrimaryAction:
2107 markAllAttachmentsForEncryption(
true);
2109 case KMessageBox::ButtonCode::SecondaryAction:
2110 markAllAttachmentsForEncryption(
false);
2113 qCWarning(MESSAGECOMPOSER_LOG) <<
"Unhandled MessageBox response";
2118 case Kleo::Conflict: {
2122 "There are conflicting encryption preferences "
2123 "for these recipients.\n"
2124 "Encrypt this message?");
2129 i18n(
"Encrypt Message?"),
2136 case KMessageBox::ButtonCode::PrimaryAction:
2137 markAllAttachmentsForEncryption(
true);
2139 case KMessageBox::ButtonCode::SecondaryAction:
2140 markAllAttachmentsForEncryption(
false);
2143 qCWarning(MESSAGECOMPOSER_LOG) <<
"Unhandled MessageBox response";
2148 case Kleo::Impossible: {
2151 "You have requested to encrypt this message, "
2152 "and to encrypt a copy to yourself, "
2153 "but no valid trusted encryption keys have been "
2154 "configured for this identity.");
2157 i18nc(
"@title:window",
"Send Unencrypted?"),
2163 markAllAttachmentsForEncryption(
false);
2169 if (!encrypt || !doEncryptCompletely) {
2170 if (cryptoWarningUnencrypted(currentIdentity())) {
2173 "Some parts of this message will not be encrypted.\n"
2174 "Sending only partially encrypted messages might violate "
2175 "site policy and/or leak sensitive information.\n"
2176 "Encrypt all parts instead?")
2178 "This message will not be encrypted.\n"
2179 "Sending unencrypted messages might violate site policy and/or "
2180 "leak sensitive information.\n"
2181 "Encrypt messages instead?");
2182 const QString buttonText = !doEncryptCompletely ?
i18n(
"&Encrypt All Parts") :
i18n(
"&Encrypt");
2185 i18nc(
"@title:window",
"Unencrypted Message Warning"),
2192 case KMessageBox::ButtonCode::PrimaryAction:
2193 markAllAttachmentsForEncryption(
true);
2195 case KMessageBox::ButtonCode::SecondaryAction:
2196 return encrypt || doEncryptCompletely;
2198 qCWarning(MESSAGECOMPOSER_LOG) <<
"Unhandled MessageBox response";
2204 return encrypt || doEncryptCompletely;
2209 mSendLaterInfo.reset(info);
2214 return mSendLaterInfo.get();
2217void ComposerViewBase::addFollowupReminder(
const QString &messageId)
2220 if (mFollowUpDate.
isValid()) {
2222 job->setSubject(m_subject);
2223 job->setMessageId(messageId);
2225 job->setFollowUpReminderDate(mFollowUpDate);
2226 job->setCollectionToDo(mFollowUpCollection);
2232void ComposerViewBase::addSendLaterItem(
const Akonadi::Item &item)
2234 mSendLaterInfo->setItemId(item.
id());
2236 auto job =
new MessageComposer::SendLaterCreateJob(*mSendLaterInfo,
this);
2240bool ComposerViewBase::requestDeleveryConfirmation()
const
2242 return m_requestDeleveryConfirmation;
2245void ComposerViewBase::setRequestDeleveryConfirmation(
bool requestDeleveryConfirmation)
2247 m_requestDeleveryConfirmation = requestDeleveryConfirmation;
2255std::shared_ptr<Kleo::ExpiryChecker> ComposerViewBase::expiryChecker()
2257 if (!mExpiryChecker) {
2258 mExpiryChecker.reset(
new Kleo::ExpiryChecker{Kleo::ExpiryCheckerSettings{encryptOwnKeyNearExpiryWarningThresholdInDays(),
2259 encryptKeyNearExpiryWarningThresholdInDays(),
2260 encryptRootCertNearExpiryWarningThresholdInDays(),
2261 encryptChainCertNearExpiryWarningThresholdInDays()}});
2263 return mExpiryChecker;
2266#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
const Headers::ContentType * contentType() const
QByteArray decodedContent() const
void setContent(const QByteArray &s)
const Headers::ContentDisposition * contentDisposition() const
QList< Content * > attachments()
QByteArray encodedContent(bool useCrLf=false) const
QList< Content * > contents()
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)