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");
734 keyResolver->setAutocryptEnabled(autocryptEnabled());
735 keyResolver->setAkonadiLookupEnabled(m_akonadiLookupEnabled);
740 bool signSomething = m_sign;
741 bool doSignCompletely = m_sign;
742 bool encryptSomething = m_encrypt;
743 bool doEncryptCompletely = m_encrypt;
746 if (!
id.pgpEncryptionKey().isEmpty()) {
749 if (!
id.smimeEncryptionKey().isEmpty()) {
752 if (canceled || keyResolver->setEncryptToSelfKeys(encryptToSelfKeys) != ResolverResult::Ok) {
753 qCDebug(MESSAGECOMPOSER_LOG) <<
"Failed to set encryptoToSelf keys!";
758 if (!
id.pgpSigningKey().isEmpty()) {
761 if (!
id.smimeSigningKey().isEmpty()) {
764 if (canceled || keyResolver->setSigningKeys(signKeys) != ResolverResult::Ok) {
765 qCDebug(MESSAGECOMPOSER_LOG) <<
"Failed to set signing keys!";
769 if (m_attachmentModel) {
770 const auto attachments = m_attachmentModel->attachments();
772 if (attachment->isSigned()) {
773 signSomething =
true;
775 doEncryptCompletely =
false;
777 if (attachment->isEncrypted()) {
778 encryptSomething =
true;
780 doSignCompletely =
false;
785 const QStringList recipients = mExpandedTo + mExpandedCc;
788 keyResolver->setPrimaryRecipients(recipients);
789 keyResolver->setSecondaryRecipients(bcc);
793 signSomething = determineWhetherToSign(doSignCompletely, keyResolver.data(), signSomething, result, canceled);
796 qCDebug(MESSAGECOMPOSER_LOG) <<
"determineWhetherToSign: failed to resolve keys! oh noes";
802 wasCanceled = canceled;
807 encryptSomething = determineWhetherToEncrypt(doEncryptCompletely, keyResolver.data(), encryptSomething, signSomething, result, canceled);
810 qCDebug(MESSAGECOMPOSER_LOG) <<
"determineWhetherToEncrypt: failed to resolve keys! oh noes";
817 wasCanceled = canceled;
824 if (!signSomething && !encryptSomething) {
826 if (m_cryptoMessageFormat & Kleo::OpenPGPMIMEFormat) {
827 composer->setAutocryptEnabled(autocryptEnabled());
828 if (keyResolver->encryptToSelfKeysFor(Kleo::OpenPGPMIMEFormat).size() > 0) {
829 composer->setSenderEncryptionKey(keyResolver->encryptToSelfKeysFor(Kleo::OpenPGPMIMEFormat)[0]);
832 composers.
append(composer);
837 const auto kpgpResult = keyResolver->resolveAllKeys(signSomething, encryptSomething);
838 if (kpgpResult == ResolverResult::Canceled || canceled) {
839 qCDebug(MESSAGECOMPOSER_LOG) <<
"resolveAllKeys: one key resolution canceled by user";
841 }
else if (kpgpResult != ResolverResult::Ok) {
843 qCDebug(MESSAGECOMPOSER_LOG) <<
"resolveAllKeys: failed to resolve keys! oh noes";
848 qCDebug(MESSAGECOMPOSER_LOG) <<
"done resolving keys.";
850 if (encryptSomething || signSomething) {
851 Kleo::CryptoMessageFormat concreteFormat = Kleo::AutoFormat;
852 for (
unsigned int i = 0; i < numConcreteCryptoMessageFormats; ++i) {
853 concreteFormat = concreteCryptoMessageFormats[i];
854 const auto encData = keyResolver->encryptionItems(concreteFormat);
855 if (encData.empty()) {
859 if (!(concreteFormat & m_cryptoMessageFormat)) {
865 if (encryptSomething || autocryptEnabled()) {
866 auto end(encData.end());
869 for (
auto it = encData.begin(); it != end; ++it) {
870 QPair<QStringList, std::vector<GpgME::Key>> p(it->recipients, it->keys);
872 qCDebug(MESSAGECOMPOSER_LOG) <<
"got resolved keys for:" << it->recipients;
874 composer->setEncryptionKeys(data);
875 if (concreteFormat & Kleo::OpenPGPMIMEFormat && autocryptEnabled()) {
876 composer->setAutocryptEnabled(autocryptEnabled());
877 composer->setSenderEncryptionKey(keyResolver->encryptToSelfKeysFor(concreteFormat)[0]);
879 bool specialGnupgHome = addKeysToContext(
dir.path(), data, keyResolver->useAutocrypt());
880 if (specialGnupgHome) {
881 dir.setAutoRemove(
false);
882 composer->setGnupgHome(
dir.path());
889 std::vector<GpgME::Key> signingKeys = keyResolver->signingKeys(concreteFormat);
890 composer->setSigningKeys(signingKeys);
893 composer->setCryptoMessageFormat(concreteFormat);
894 composer->setSignAndEncrypt(signSomething, encryptSomething);
896 composers.
append(composer);
900 composers.
append(composer);
902 markAllAttachmentsForSigning(
false);
903 markAllAttachmentsForEncryption(
false);
906 if (composers.
isEmpty() && (signSomething || encryptSomething)) {
907 Q_ASSERT_X(
false,
"ComposerViewBase::generateCryptoMessages",
"No concrete sign or encrypt method selected");
915 globalPart->setParentWidgetForGui(m_parentWidget);
916 globalPart->setMDNRequested(m_mdnRequested);
917 globalPart->setRequestDeleveryConfirmation(m_requestDeleveryConfirmation);
928 if (m_fccCollection.
isValid()) {
934 if (expansion == UseExpandedRecipients) {
935 infoPart->setFrom(mExpandedFrom);
936 infoPart->setTo(mExpandedTo);
937 infoPart->setCc(mExpandedCc);
938 infoPart->setBcc(mExpandedBcc);
939 infoPart->setReplyTo(mExpandedReplyTo);
941 infoPart->setFrom(from());
942 infoPart->setTo(m_recipientsEditor->recipientStringList(MessageComposer::Recipient::To));
943 infoPart->setCc(m_recipientsEditor->recipientStringList(MessageComposer::Recipient::Cc));
944 infoPart->setBcc(m_recipientsEditor->recipientStringList(MessageComposer::Recipient::Bcc));
945 infoPart->setReplyTo(m_recipientsEditor->recipientStringList(MessageComposer::Recipient::ReplyTo));
947 infoPart->setSubject(subject());
948 infoPart->setUserAgent(QStringLiteral(
"KMail"));
949 infoPart->setUrgent(m_urgent);
951 if (
auto inReplyTo = m_msg->inReplyTo(
false)) {
952 infoPart->setInReplyTo(inReplyTo->asUnicodeString());
955 if (
auto references = m_msg->references(
false)) {
956 infoPart->setReferences(references->asUnicodeString());
960 if (
auto hdr = m_msg->headerByType(
"X-KMail-SignatureActionEnabled")) {
963 if (
auto hdr = m_msg->headerByType(
"X-KMail-EncryptActionEnabled")) {
966 if (
auto hdr = m_msg->headerByType(
"X-KMail-CryptoMessageFormat")) {
969 if (
auto hdr = m_msg->headerByType(
"X-KMail-UnExpanded-To")) {
972 if (
auto hdr = m_msg->headerByType(
"X-KMail-UnExpanded-CC")) {
975 if (
auto hdr = m_msg->headerByType(
"X-KMail-UnExpanded-BCC")) {
978 if (
auto hdr = m_msg->headerByType(
"X-KMail-UnExpanded-Reply-To")) {
981 if (
auto hdr = m_msg->organization(
false)) {
984 if (
auto hdr = m_msg->headerByType(
"X-KMail-Identity")) {
987 if (
auto hdr = m_msg->headerByType(
"X-KMail-Transport")) {
990 if (
auto hdr = m_msg->headerByType(
"X-KMail-Fcc")) {
993 if (
auto hdr = m_msg->headerByType(
"X-KMail-Drafts")) {
996 if (
auto hdr = m_msg->headerByType(
"X-KMail-Templates")) {
999 if (
auto hdr = m_msg->headerByType(
"X-KMail-Link-Message")) {
1002 if (
auto hdr = m_msg->headerByType(
"X-KMail-Link-Type")) {
1005 if (
auto hdr = m_msg->headerByType(
"X-Face")) {
1008 if (
auto hdr = m_msg->headerByType(
"Face")) {
1011 if (
auto hdr = m_msg->headerByType(
"X-KMail-FccDisabled")) {
1014 if (
auto hdr = m_msg->headerByType(
"X-KMail-Identity-Name")) {
1017 if (
auto hdr = m_msg->headerByType(
"X-KMail-Transport-Name")) {
1021 infoPart->setExtraHeaders(extras);
1024void ComposerViewBase::slotSendComposeResult(
KJob *job)
1028 if (composer->error() != MessageComposer::Composer::NoError) {
1029 qCDebug(MESSAGECOMPOSER_LOG) <<
"compose job might have error: " << job->
error() <<
" errorString: " << job->
errorString();
1032 if (composer->error() == MessageComposer::Composer::NoError) {
1033 Q_ASSERT(m_composers.
contains(composer));
1035 qCDebug(MESSAGECOMPOSER_LOG) <<
"NoError.";
1036 const int numberOfMessage(composer->resultMessages().size());
1037 for (
int i = 0; i < numberOfMessage; ++i) {
1038 if (mSaveIn == MessageComposer::MessageSender::SaveInNone) {
1039 queueMessage(composer->resultMessages().at(i), composer);
1041 saveMessage(composer->resultMessages().at(i), mSaveIn);
1044 saveRecentAddresses(composer->resultMessages().at(0));
1045 }
else if (composer->error() == MessageComposer::Composer::UserCancelledError) {
1048 qCDebug(MESSAGECOMPOSER_LOG) <<
"UserCancelledError.";
1051 qCDebug(MESSAGECOMPOSER_LOG) <<
"other Error." << composer->error();
1053 if (composer->error() == MessageComposer::Composer::BugError) {
1054 msg =
i18n(
"Could not compose message: %1 \n Please report this bug.", job->
errorString());
1061 if (!composer->gnupgHome().isEmpty()) {
1062 QDir dir(composer->gnupgHome());
1063 dir.removeRecursively();
1071 KConfig *config = MessageComposer::MessageComposerSettings::self()->config();
1072 if (
auto to = msg->to(
false)) {
1073 const auto toAddresses =
to->mailboxes();
1074 for (
const auto &address : toAddresses) {
1078 if (
auto cc = msg->cc(
false)) {
1079 const auto ccAddresses = cc->mailboxes();
1080 for (
const auto &address : ccAddresses) {
1084 if (
auto bcc = msg->bcc(
false)) {
1085 const auto bccAddresses = bcc->mailboxes();
1086 for (
const auto &address : bccAddresses) {
1096 qjob->setMessage(message);
1097 qjob->transportAttribute().setTransportId(infoPart->transportId());
1098 if (mSendMethod == MessageComposer::MessageSender::SendLater) {
1102 if (message->hasHeader(
"X-KMail-FccDisabled")) {
1108 qjob->sentBehaviourAttribute().setMoveToCollection(sentCollection);
1114 if (transport && transport->specifySenderOverwriteAddress()) {
1115 qjob->addressAttribute().setFrom(
1123 qjob->addressAttribute().setTo(MessageComposer::Util::cleanUpEmailListAndEncoding(realTo->asUnicodeString().split(
QLatin1Char(
'%'))));
1124 message->removeHeader(
"X-KMail-EncBccRecipients");
1125 message->assemble();
1126 qCDebug(MESSAGECOMPOSER_LOG) <<
"sending with-bcc encr mail to a/n recipient:" << qjob->addressAttribute().to();
1128 qjob->addressAttribute().setTo(MessageComposer::Util::cleanUpEmailListAndEncoding(infoPart->
to()));
1129 qjob->addressAttribute().setCc(MessageComposer::Util::cleanUpEmailListAndEncoding(infoPart->
cc()));
1130 qjob->addressAttribute().setBcc(MessageComposer::Util::cleanUpEmailListAndEncoding(infoPart->
bcc()));
1132 if (m_requestDeleveryConfirmation) {
1133 qjob->addressAttribute().setDeliveryStatusNotification(
true);
1135 MessageComposer::Util::addSendReplyForwardAction(message, qjob);
1138 MessageComposer::Util::addCustomHeaders(message, m_customHeader);
1139 message->assemble();
1141 m_pendingQueueJobs++;
1144 qCDebug(MESSAGECOMPOSER_LOG) <<
"Queued a message.";
1147void ComposerViewBase::slotQueueResult(
KJob *job)
1149 m_pendingQueueJobs--;
1151 qCDebug(MESSAGECOMPOSER_LOG) <<
"mPendingQueueJobs" << m_pendingQueueJobs;
1152 Q_ASSERT(m_pendingQueueJobs >= 0);
1155 qCDebug(MESSAGECOMPOSER_LOG) <<
"Failed to queue a message:" << job->
errorString();
1159 const QString msg =
i18n(
"There were problems trying to queue the message for sending: %1", job->
errorString());
1161 if (m_pendingQueueJobs == 0) {
1167 if (m_pendingQueueJobs == 0) {
1168 addFollowupReminder(qjob->message()->messageID(
false)->asUnicodeString());
1173void ComposerViewBase::initAutoSave()
1175 qCDebug(MESSAGECOMPOSER_LOG) <<
"initialising autosave";
1179 if (!dataDirectory.exists(QStringLiteral(
"autosave"))) {
1180 qCDebug(MESSAGECOMPOSER_LOG) <<
"Creating autosave directory.";
1181 dataDirectory.mkdir(QStringLiteral(
"autosave"));
1185 if (m_autoSaveUUID.
isEmpty()) {
1194 return mFollowUpCollection;
1197void ComposerViewBase::setFollowUpCollection(
const Akonadi::Collection &followUpCollection)
1199 mFollowUpCollection = followUpCollection;
1202QDate ComposerViewBase::followUpDate()
const
1204 return mFollowUpDate;
1207void ComposerViewBase::setFollowUpDate(
const QDate &followUpDate)
1209 mFollowUpDate = followUpDate;
1214 return m_dictionary;
1219 m_dictionary = dictionary;
1224 if (m_autoSaveInterval == 0) {
1225 delete m_autoSaveTimer;
1226 m_autoSaveTimer =
nullptr;
1228 if (!m_autoSaveTimer) {
1229 m_autoSaveTimer =
new QTimer(
this);
1230 if (m_parentWidget) {
1236 m_autoSaveTimer->
start(m_autoSaveInterval);
1242 delete m_autoSaveTimer;
1243 m_autoSaveTimer =
nullptr;
1244 if (!m_autoSaveUUID.
isEmpty()) {
1245 qCDebug(MESSAGECOMPOSER_LOG) <<
"deleting autosave files" << m_autoSaveUUID;
1256 qCDebug(MESSAGECOMPOSER_LOG) <<
"There are" << autoSaveFiles.
count() <<
"to be deleted.";
1259 for (
const QString &file : autoSaveFiles) {
1260 autoSaveDir.
remove(file);
1262 m_autoSaveUUID.
clear();
1269 qCDebug(MESSAGECOMPOSER_LOG) <<
"Autosaving message";
1271 if (m_autoSaveTimer) {
1272 m_autoSaveTimer->
stop();
1277 qCDebug(MESSAGECOMPOSER_LOG) <<
"Autosave: Called while composer active; ignoring. Number of composer " << m_composers.
count();
1282 fillComposer(composer);
1284 composer->setAutocryptEnabled(autocryptEnabled());
1285 m_composers.
append(composer);
1292 m_autoSaveUUID = fileName;
1297void ComposerViewBase::slotAutoSaveComposeResult(
KJob *job)
1301 Q_ASSERT(
dynamic_cast<Composer *
>(job));
1302 auto composer =
static_cast<Composer *
>(job);
1304 if (composer->
error() == Composer::NoError) {
1305 Q_ASSERT(m_composers.
contains(composer));
1309 qCDebug(MESSAGECOMPOSER_LOG) <<
"NoError.";
1310 writeAutoSaveToDisk(composer->resultMessages().constFirst());
1311 Q_ASSERT(composer->resultMessages().size() == 1);
1313 if (m_autoSaveInterval > 0) {
1316 }
else if (composer->
error() == MessageComposer::Composer::UserCancelledError) {
1319 qCDebug(MESSAGECOMPOSER_LOG) <<
"UserCancelledError.";
1322 qCDebug(MESSAGECOMPOSER_LOG) <<
"other Error.";
1333 const QString filename = autosavePath + m_autoSaveUUID;
1336 qCDebug(MESSAGECOMPOSER_LOG) <<
"Writing message to disk as" << filename;
1341 if (file.write(message->encodedContent()) !=
static_cast<qint64
>(message->encodedContent().size())) {
1344 if (!file.commit()) {
1353 qCWarning(MESSAGECOMPOSER_LOG) <<
"Auto saving failed:" <<
errorMessage << file.errorString() <<
" m_autoSaveUUID" << m_autoSaveUUID;
1354 if (!m_autoSaveErrorShown) {
1356 i18n(
"Autosaving the message as %1 failed.\n"
1361 file.errorString()),
1362 i18nc(
"@title:window",
"Autosaving Message Failed"));
1365 m_autoSaveErrorShown =
true;
1369 m_autoSaveErrorShown =
false;
1375void ComposerViewBase::saveMessage(
const KMime::Message::Ptr &message, MessageComposer::MessageSender::SaveIn saveIn)
1378 const auto identity = currentIdentity();
1380 if (!identity.
isNull()) {
1381 if (
auto header = message->headerByType(
"X-KMail-Fcc")) {
1382 const int sentCollectionId = header->asUnicodeString().toInt();
1384 message->removeHeader(
"X-KMail-Fcc");
1388 MessageComposer::Util::addCustomHeaders(message, m_customHeader);
1390 message->assemble();
1393 item.
setMimeType(QStringLiteral(
"message/rfc822"));
1397 if (!identity.
isNull()) {
1399 case MessageComposer::MessageSender::SaveInTemplates:
1404 case MessageComposer::MessageSender::SaveInDrafts:
1409 case MessageComposer::MessageSender::SaveInOutbox:
1412 case MessageComposer::MessageSender::SaveInNone:
1418 QObject::connect(saveMessageJob, &Akonadi::CollectionFetchJob::result,
this, &ComposerViewBase::slotSaveMessage);
1421 target = defaultSpecialTarget();
1423 connect(create, &Akonadi::ItemCreateJob::result,
this, &ComposerViewBase::slotCreateItemResult);
1424 ++m_pendingQueueJobs;
1428void ComposerViewBase::slotSaveMessage(
KJob *job)
1433 target = defaultSpecialTarget();
1437 target = defaultSpecialTarget();
1443 connect(create, &Akonadi::ItemCreateJob::result,
this, &ComposerViewBase::slotCreateItemResult);
1444 ++m_pendingQueueJobs;
1451 case MessageComposer::MessageSender::SaveInNone:
1453 case MessageComposer::MessageSender::SaveInDrafts:
1456 case MessageComposer::MessageSender::SaveInTemplates:
1459 case MessageComposer::MessageSender::SaveInOutbox:
1467void ComposerViewBase::slotCreateItemResult(
KJob *job)
1469 --m_pendingQueueJobs;
1470 qCDebug(MESSAGECOMPOSER_LOG) <<
"mPendingCreateItemJobs" << m_pendingQueueJobs;
1471 Q_ASSERT(m_pendingQueueJobs >= 0);
1474 qCWarning(MESSAGECOMPOSER_LOG) <<
"Failed to save a message:" << job->
errorString();
1480 if (mSendLaterInfo) {
1485 addSendLaterItem(item);
1489 if (m_pendingQueueJobs == 0) {
1497 qCDebug(MESSAGECOMPOSER_LOG) <<
"adding attachment with url:" << url;
1499 m_attachmentController->addAttachmentUrlSync(url);
1509 attachment->setName(name);
1510 attachment->setFileName(filename);
1511 attachment->setData(data);
1512 attachment->setCharset(charset.
toLatin1());
1513 attachment->setMimeType(mimeType);
1520void ComposerViewBase::addAttachmentPart(
KMime::Content *partToAttach)
1532 part->setDescription(cd->asUnicodeString());
1535 if (ct->hasParameter(
"name")) {
1536 part->setName(ct->parameter(
"name"));
1540 part->setFileName(cd->filename());
1541 part->setInline(cd->disposition() == KMime::Headers::CDinline);
1543 if (part->name().isEmpty() && !part->fileName().isEmpty()) {
1544 part->setName(part->fileName());
1546 if (part->fileName().isEmpty() && !part->name().isEmpty()) {
1547 part->setFileName(part->name());
1554 fillComposer(composer, UseUnExpandedRecipients,
false);
1557void ComposerViewBase::fillComposer(
MessageComposer::Composer *composer, ComposerViewBase::RecipientExpansion expansion,
bool autoresize)
1559 fillGlobalPart(composer->globalPart());
1560 m_editor->fillComposerTextPart(composer->textPart());
1561 fillInfoPart(composer->infoPart(), expansion);
1562 if (m_attachmentModel) {
1563 composer->addAttachmentParts(m_attachmentModel->attachments(), autoresize);
1570 if (m_recipientsEditor) {
1571 return MessageComposer::Util::cleanedUpHeaderString(m_recipientsEditor->recipientString(MessageComposer::Recipient::To));
1577QString ComposerViewBase::cc()
const
1579 if (m_recipientsEditor) {
1580 return MessageComposer::Util::cleanedUpHeaderString(m_recipientsEditor->recipientString(MessageComposer::Recipient::Cc));
1586QString ComposerViewBase::bcc()
const
1588 if (m_recipientsEditor) {
1589 return MessageComposer::Util::cleanedUpHeaderString(m_recipientsEditor->recipientString(MessageComposer::Recipient::Bcc));
1594QString ComposerViewBase::from()
const
1596 return MessageComposer::Util::cleanedUpHeaderString(m_from);
1599QString ComposerViewBase::replyTo()
const
1601 if (m_recipientsEditor) {
1602 return MessageComposer::Util::cleanedUpHeaderString(m_recipientsEditor->recipientString(MessageComposer::Recipient::ReplyTo));
1607QString ComposerViewBase::subject()
const
1609 return MessageComposer::Util::cleanedUpHeaderString(m_subject);
1617bool ComposerViewBase::autocryptEnabled()
const
1622void ComposerViewBase::setParentWidgetForGui(
QWidget *w)
1629 m_attachmentController = controller;
1634 return m_attachmentController;
1639 m_attachmentModel = model;
1644 return m_attachmentModel;
1649 m_recipientsEditor = recEditor;
1654 return m_recipientsEditor;
1659 m_signatureController = sigController;
1664 return m_signatureController;
1669 m_identityCombo = identCombo;
1674 return m_identityCombo;
1679 MessageComposer::Recipient::Type type)
1684 case MessageComposer::Recipient::Bcc: {
1685 oldIdentList = oldIdent.
bcc();
1686 newIdentList = ident.
bcc();
1689 case MessageComposer::Recipient::Cc: {
1690 oldIdentList = oldIdent.
cc();
1691 newIdentList = ident.
cc();
1694 case MessageComposer::Recipient::ReplyTo: {
1699 case MessageComposer::Recipient::To:
1700 case MessageComposer::Recipient::Undefined:
1704 if (oldIdentList != newIdentList) {
1712 m_recipientsEditor->
addRecipient(recipient.prettyAddress(), type);
1714 m_recipientsEditor->setFocusBottom();
1720 updateRecipients(ident, oldIdent, MessageComposer::Recipient::Bcc);
1721 updateRecipients(ident, oldIdent, MessageComposer::Recipient::Cc);
1722 updateRecipients(ident, oldIdent, MessageComposer::Recipient::ReplyTo);
1727 const bool replaced = editor()->composerSignature()->replaceSignature(oldSig, newSig);
1733 attachmentController()->setIdentityHasOwnVcard(!vcardFileName.
isEmpty());
1734 attachmentController()->setAttachOwnVcard(ident.
attachVcard());
1742 m_editor->
document()->setModified(
false);
1752 m_transport = transpCombo;
1762 m_identMan = identMan;
1775 m_fccCollection = fccCollection;
1778 connect(checkFccCollectionJob, &
KJob::result,
this, &ComposerViewBase::slotFccCollectionCheckResult);
1781void ComposerViewBase::slotFccCollectionCheckResult(
KJob *job)
1784 qCWarning(MESSAGECOMPOSER_LOG) <<
" void ComposerViewBase::slotFccCollectionCheckResult(KJob *job) error " << job->
errorString();
1789 m_fccCollection = sentMailCol;
1809void ComposerViewBase::setSubject(
const QString &subject)
1811 m_subject = subject;
1812 if (mSendLaterInfo) {
1813 mSendLaterInfo->setSubject(m_subject);
1814 mSendLaterInfo->setTo(
to());
1818void ComposerViewBase::setAutoSaveInterval(
int interval)
1820 m_autoSaveInterval = interval;
1826 m_encrypt = encrypt;
1827 m_cryptoMessageFormat = format;
1828 m_neverEncrypt = neverEncryptDrafts;
1831void ComposerViewBase::setMDNRequested(
bool mdnRequested)
1833 m_mdnRequested = mdnRequested;
1836void ComposerViewBase::setUrgent(
bool urgent)
1841int ComposerViewBase::autoSaveInterval()
const
1843 return m_autoSaveInterval;
1849 if (
KMime::Content *n = Util::findTypeInMessage(root,
"multipart",
"alternative")) {
1852 const auto nodes = parentnode->
contents();
1853 for (
auto node : nodes) {
1854 if (node->contentType()->isImage()) {
1855 qCDebug(MESSAGECOMPOSER_LOG) <<
"found image in multipart/related : " << node->contentType()->name();
1858 m_editor->composerControler()->composerImages()->loadImage(
1861 node->contentType()->name());
1869bool ComposerViewBase::inlineSigningEncryptionSelected()
const
1871 if (!m_sign && !m_encrypt) {
1874 return m_cryptoMessageFormat == Kleo::InlineOpenPGPFormat;
1877bool ComposerViewBase::hasMissingAttachments(
const QStringList &attachmentKeywords)
1879 if (attachmentKeywords.
isEmpty()) {
1882 if (m_attachmentModel && m_attachmentModel->rowCount() > 0) {
1886 return MessageComposer::Util::hasMissingAttachments(attachmentKeywords, m_editor->
document(), subject());
1891 if (!hasMissingAttachments(attachmentKeywords)) {
1892 return NoMissingAttachmentFound;
1896 i18n(
"The message you have composed seems to refer to an "
1897 "attached file but you have not attached anything.\n"
1898 "Do you want to attach a file to your message?"),
1899 i18nc(
"@title:window",
"File Attachment Reminder"),
1903 return FoundMissingAttachmentAndCancel;
1905 if (rc == KMessageBox::ButtonCode::PrimaryAction) {
1906 m_attachmentController->showAddAttachmentFileDialog();
1907 return FoundMissingAttachmentAndAddedAttachment;
1910 return FoundMissingAttachmentAndSending;
1913void ComposerViewBase::markAllAttachmentsForSigning(
bool sign)
1915 if (m_attachmentModel) {
1916 const auto attachments = m_attachmentModel->attachments();
1918 attachment->setSigned(sign);
1923void ComposerViewBase::markAllAttachmentsForEncryption(
bool encrypt)
1925 if (m_attachmentModel) {
1926 const auto attachments = m_attachmentModel->attachments();
1928 attachment->setEncrypted(encrypt);
1933bool ComposerViewBase::determineWhetherToSign(
bool doSignCompletely,
KeyResolver *keyResolver,
bool signSomething,
bool &result,
bool &canceled)
1938 if (!signSomething) {
1939 markAllAttachmentsForSigning(
true);
1944 case Kleo::DontDoIt:
1947 case Kleo::AskOpportunistic:
1953 "Examination of the recipient's signing preferences "
1954 "yielded that you be asked whether or not to sign "
1956 "Sign this message?");
1959 i18nc(
"@title:window",
"Sign Message?"),
1966 case KMessageBox::ButtonCode::PrimaryAction:
1967 markAllAttachmentsForSigning(
true);
1969 case KMessageBox::ButtonCode::SecondaryAction:
1970 markAllAttachmentsForSigning(
false);
1973 qCWarning(MESSAGECOMPOSER_LOG) <<
"Unhandled MessageBox response";
1978 case Kleo::Conflict: {
1982 "There are conflicting signing preferences "
1983 "for these recipients.\n"
1984 "Sign this message?");
1987 i18nc(
"@title:window",
"Sign Message?"),
1994 case KMessageBox::ButtonCode::PrimaryAction:
1995 markAllAttachmentsForSigning(
true);
1997 case KMessageBox::ButtonCode::SecondaryAction:
1998 markAllAttachmentsForSigning(
false);
2001 qCWarning(MESSAGECOMPOSER_LOG) <<
"Unhandled MessageBox response";
2006 case Kleo::Impossible: {
2009 "You have requested to sign this message, "
2010 "but no valid signing keys have been configured "
2011 "for this identity.");
2014 i18nc(
"@title:window",
"Send Unsigned?"),
2020 markAllAttachmentsForSigning(
false);
2026 if (!sign || !doSignCompletely) {
2027 if (cryptoWarningUnsigned(currentIdentity())) {
2029 const QString msg = sign && !doSignCompletely ?
i18n(
2030 "Some parts of this message will not be signed.\n"
2031 "Sending only partially signed messages might violate site policy.\n"
2032 "Sign all parts instead?")
2034 "This message will not be signed.\n"
2035 "Sending unsigned message might violate site policy.\n"
2036 "Sign message instead?");
2037 const QString buttonText = sign && !doSignCompletely ?
i18n(
"&Sign All Parts") :
i18n(
"&Sign");
2040 i18nc(
"@title:window",
"Unsigned-Message Warning"),
2047 case KMessageBox::ButtonCode::PrimaryAction:
2048 markAllAttachmentsForSigning(
true);
2050 case KMessageBox::ButtonCode::SecondaryAction:
2051 return sign || doSignCompletely;
2053 qCWarning(MESSAGECOMPOSER_LOG) <<
"Unhandled MessageBox response";
2058 return sign || doSignCompletely;
2061bool ComposerViewBase::determineWhetherToEncrypt(
bool doEncryptCompletely,
2063 bool encryptSomething,
2068 bool encrypt =
false;
2069 bool opportunistic =
false;
2072 if (!encryptSomething) {
2073 markAllAttachmentsForEncryption(
true);
2078 case Kleo::DontDoIt:
2081 case Kleo::AskOpportunistic:
2082 opportunistic =
true;
2089 "Valid trusted encryption keys were found for all recipients.\n"
2090 "Encrypt this message?")
2092 "Examination of the recipient's encryption preferences "
2093 "yielded that you be asked whether or not to encrypt "
2095 "Encrypt this message?");
2098 i18n(
"Encrypt Message?"),
2105 case KMessageBox::ButtonCode::PrimaryAction:
2106 markAllAttachmentsForEncryption(
true);
2108 case KMessageBox::ButtonCode::SecondaryAction:
2109 markAllAttachmentsForEncryption(
false);
2112 qCWarning(MESSAGECOMPOSER_LOG) <<
"Unhandled MessageBox response";
2117 case Kleo::Conflict: {
2121 "There are conflicting encryption preferences "
2122 "for these recipients.\n"
2123 "Encrypt this message?");
2128 i18n(
"Encrypt Message?"),
2135 case KMessageBox::ButtonCode::PrimaryAction:
2136 markAllAttachmentsForEncryption(
true);
2138 case KMessageBox::ButtonCode::SecondaryAction:
2139 markAllAttachmentsForEncryption(
false);
2142 qCWarning(MESSAGECOMPOSER_LOG) <<
"Unhandled MessageBox response";
2147 case Kleo::Impossible: {
2150 "You have requested to encrypt this message, "
2151 "and to encrypt a copy to yourself, "
2152 "but no valid trusted encryption keys have been "
2153 "configured for this identity.");
2156 i18nc(
"@title:window",
"Send Unencrypted?"),
2162 markAllAttachmentsForEncryption(
false);
2168 if (!encrypt || !doEncryptCompletely) {
2169 if (cryptoWarningUnencrypted(currentIdentity())) {
2172 "Some parts of this message will not be encrypted.\n"
2173 "Sending only partially encrypted messages might violate "
2174 "site policy and/or leak sensitive information.\n"
2175 "Encrypt all parts instead?")
2177 "This message will not be encrypted.\n"
2178 "Sending unencrypted messages might violate site policy and/or "
2179 "leak sensitive information.\n"
2180 "Encrypt messages instead?");
2181 const QString buttonText = !doEncryptCompletely ?
i18n(
"&Encrypt All Parts") :
i18n(
"&Encrypt");
2184 i18nc(
"@title:window",
"Unencrypted Message Warning"),
2191 case KMessageBox::ButtonCode::PrimaryAction:
2192 markAllAttachmentsForEncryption(
true);
2194 case KMessageBox::ButtonCode::SecondaryAction:
2195 return encrypt || doEncryptCompletely;
2197 qCWarning(MESSAGECOMPOSER_LOG) <<
"Unhandled MessageBox response";
2203 return encrypt || doEncryptCompletely;
2208 mSendLaterInfo.reset(info);
2213 return mSendLaterInfo.get();
2216void ComposerViewBase::addFollowupReminder(
const QString &messageId)
2219 if (mFollowUpDate.
isValid()) {
2221 job->setSubject(m_subject);
2222 job->setMessageId(messageId);
2224 job->setFollowUpReminderDate(mFollowUpDate);
2225 job->setCollectionToDo(mFollowUpCollection);
2231void ComposerViewBase::addSendLaterItem(
const Akonadi::Item &item)
2233 mSendLaterInfo->setItemId(item.
id());
2235 auto job =
new MessageComposer::SendLaterCreateJob(*mSendLaterInfo,
this);
2239bool ComposerViewBase::requestDeleveryConfirmation()
const
2241 return m_requestDeleveryConfirmation;
2244void ComposerViewBase::setRequestDeleveryConfirmation(
bool requestDeleveryConfirmation)
2246 m_requestDeleveryConfirmation = requestDeleveryConfirmation;
2254std::shared_ptr<Kleo::ExpiryChecker> ComposerViewBase::expiryChecker()
2256 if (!mExpiryChecker) {
2257 mExpiryChecker.reset(
new Kleo::ExpiryChecker{Kleo::ExpiryCheckerSettings{encryptOwnKeyNearExpiryWarningThresholdInDays(),
2258 encryptKeyNearExpiryWarningThresholdInDays(),
2259 encryptRootCertNearExpiryWarningThresholdInDays(),
2260 encryptChainCertNearExpiryWarningThresholdInDays()}});
2262 return mExpiryChecker;
2265#include "moc_composerviewbase.cpp"
Akonadi::Collection currentCollection() const
void setDefaultCollection(const Collection &collection)
Collection::List collections() const
void setMimeType(const QString &mimeType)
void setPayload(const T &p)
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.
A class to resolve signing/encryption keys w.r.t.
Kleo::Action checkEncryptionPreferences(bool encryptionRequested) const
Determine whether to encrypt or not, depending on the per-recipient encryption preferences,...
Kleo::Action checkSigningPreferences(bool signingRequested) const
Determine whether to sign or not, depending on the per-recipient signing preferences,...
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)
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)
QAction * create(StandardAction id, const Receiver *recvr, Func slot, QObject *parent, std::optional< Qt::ConnectionType > connectionType=std::nullopt)
const QList< QKeySequence > & end()
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)