File indexing completed on 2024-11-24 04:43:49
0001 /* 0002 SPDX-FileCopyrightText: 2015-2017 Krzysztof Nowicki <krissn@op.pl> 0003 0004 SPDX-License-Identifier: LGPL-2.0-or-later 0005 */ 0006 0007 #include "ewscreatemailjob.h" 0008 0009 #include <KLocalizedString> 0010 0011 #include <Akonadi/AgentManager> 0012 #include <Akonadi/Collection> 0013 #include <Akonadi/Item> 0014 #include <Akonadi/SpecialMailCollections> 0015 #include <KMime/Message> 0016 0017 #include "ewscreateitemrequest.h" 0018 #include "ewsmailhandler.h" 0019 #include "ewsmoveitemrequest.h" 0020 #include "ewspropertyfield.h" 0021 #include "ewsresource_debug.h" 0022 0023 using namespace Akonadi; 0024 0025 static const EwsPropertyField propPidMessageFlags(0x0e07, EwsPropTypeInteger); 0026 0027 EwsCreateMailJob::EwsCreateMailJob(EwsClient &client, 0028 const Akonadi::Item &item, 0029 const Akonadi::Collection &collection, 0030 EwsTagStore *tagStore, 0031 EwsResource *parent) 0032 : EwsCreateItemJob(client, item, collection, tagStore, parent) 0033 { 0034 } 0035 0036 EwsCreateMailJob::~EwsCreateMailJob() = default; 0037 0038 void EwsCreateMailJob::doStart() 0039 { 0040 if (!mItem.hasPayload<KMime::Message::Ptr>()) { 0041 setErrorMsg(QStringLiteral("Expected MIME message payload")); 0042 emitResult(); 0043 } 0044 0045 auto req = new EwsCreateItemRequest(mClient, this); 0046 0047 auto msg = mItem.payload<KMime::Message::Ptr>(); 0048 /* Exchange doesn't just store whatever MIME content that was sent to it - it will parse it and send 0049 * further the version assembled back from the parsed parts. It seems that this parsing doesn't work well 0050 * with the quoted-printable encoding, which KMail prefers. This results in malformed encoding, which the 0051 * sender doesn't even see. 0052 * As a workaround force encoding of the body (or in case of multipart - all parts) to Base64. */ 0053 if (msg->contents().isEmpty()) { 0054 msg->changeEncoding(KMime::Headers::CEbase64); 0055 msg->contentTransferEncoding(true)->setEncoding(KMime::Headers::CEbase64); 0056 } else { 0057 const auto contents = msg->contents(); 0058 for (KMime::Content *content : contents) { 0059 content->changeEncoding(KMime::Headers::CEbase64); 0060 content->contentTransferEncoding(true)->setEncoding(KMime::Headers::CEbase64); 0061 } 0062 } 0063 msg->assemble(); 0064 QByteArray mimeContent = msg->encodedContent(true); 0065 bool sentItemsCreateWorkaround = false; 0066 EwsItem item; 0067 item.setType(EwsItemTypeMessage); 0068 item.setField(EwsItemFieldMimeContent, mimeContent); 0069 if (!mSend) { 0070 /* When creating items using the CreateItem request Exchange will by default mark the message 0071 * as draft. Setting the extended property below causes the message to appear normally. */ 0072 item.setProperty(propPidMessageFlags, QStringLiteral("1")); 0073 const Akonadi::AgentInstance &inst = Akonadi::AgentManager::self()->instance(mCollection.resource()); 0074 0075 /* WORKAROUND: The "Sent Items" folder is a little "special" when it comes to creating items. 0076 * Unlike other folders when creating items there the creation date/time is always set to the 0077 * current date/time instead of the value from the MIME Date header. This causes mail that 0078 * was copied from other folders to appear with the current date/time instead of the original one. 0079 * To work around this create the item in the "Drafts" folder first and then move it to "Sent Items". */ 0080 if (mCollection == SpecialMailCollections::self()->collection(SpecialMailCollections::SentMail, inst)) { 0081 qCInfoNC(EWSRES_LOG) << "Move to \"Sent Items\" detected - activating workaround."; 0082 const Collection &draftColl = SpecialMailCollections::self()->collection(SpecialMailCollections::Drafts, inst); 0083 req->setSavedFolderId(EwsId(draftColl.remoteId(), draftColl.remoteRevision())); 0084 sentItemsCreateWorkaround = true; 0085 } else { 0086 req->setSavedFolderId(EwsId(mCollection.remoteId(), mCollection.remoteRevision())); 0087 } 0088 } 0089 // Set flags 0090 QHash<EwsPropertyField, QVariant> propertyHash = EwsMailHandler::writeFlags(mItem.flags()); 0091 for (auto it = propertyHash.cbegin(), end = propertyHash.cend(); it != end; ++it) { 0092 if (!it.value().isNull()) { 0093 if (it.key().type() == EwsPropertyField::ExtendedField) { 0094 item.setProperty(it.key(), it.value()); 0095 } else if (it.key().type() == EwsPropertyField::Field) { 0096 /* TODO: Currently EwsItem distinguishes between regular fields and extended fields 0097 * and keeps them in separate lists. Someday it will make more sense to unify them. 0098 * Until that the code below needs to manually translate the field names into 0099 * EwsItemField enum items. 0100 */ 0101 if (it.key().uri() == QLatin1StringView("message:IsRead")) { 0102 item.setField(EwsItemFieldIsRead, it.value()); 0103 } 0104 } 0105 } 0106 } 0107 0108 populateCommonProperties(item); 0109 0110 req->setItems(EwsItem::List() << item); 0111 req->setMessageDisposition(mSend ? EwsDispSendOnly : EwsDispSaveOnly); 0112 connect(req, 0113 &EwsCreateItemRequest::finished, 0114 this, 0115 sentItemsCreateWorkaround ? &EwsCreateMailJob::mailCreateWorkaroundFinished : &EwsCreateMailJob::mailCreateFinished); 0116 addSubjob(req); 0117 req->start(); 0118 } 0119 0120 void EwsCreateMailJob::mailCreateFinished(KJob *job) 0121 { 0122 auto req = qobject_cast<EwsCreateItemRequest *>(job); 0123 if (job->error()) { 0124 setErrorMsg(job->errorString()); 0125 emitResult(); 0126 return; 0127 } 0128 0129 if (!req) { 0130 setErrorMsg(QStringLiteral("Invalid EwsCreateItemRequest job object")); 0131 emitResult(); 0132 return; 0133 } 0134 0135 if (req->responses().count() != 1) { 0136 setErrorMsg(QStringLiteral("Invalid number of responses received from server.")); 0137 emitResult(); 0138 return; 0139 } 0140 0141 EwsCreateItemRequest::Response resp = req->responses().first(); 0142 if (resp.isSuccess()) { 0143 EwsId id = resp.itemId(); 0144 mItem.setRemoteId(id.id()); 0145 mItem.setRemoteRevision(id.changeKey()); 0146 mItem.setParentCollection(mCollection); 0147 } else { 0148 setErrorMsg(i18n("Failed to create mail item")); 0149 } 0150 0151 emitResult(); 0152 } 0153 0154 void EwsCreateMailJob::mailCreateWorkaroundFinished(KJob *job) 0155 { 0156 auto req = qobject_cast<EwsCreateItemRequest *>(job); 0157 if (job->error()) { 0158 setErrorMsg(job->errorString()); 0159 emitResult(); 0160 return; 0161 } 0162 0163 if (!req) { 0164 setErrorMsg(QStringLiteral("Invalid EwsCreateItemRequest job object")); 0165 emitResult(); 0166 return; 0167 } 0168 0169 if (req->responses().count() != 1) { 0170 setErrorMsg(QStringLiteral("Invalid number of responses received from server.")); 0171 emitResult(); 0172 return; 0173 } 0174 0175 EwsCreateItemRequest::Response resp = req->responses().first(); 0176 if (resp.isSuccess()) { 0177 EwsId id = resp.itemId(); 0178 auto moveItemReq = new EwsMoveItemRequest(mClient, this); 0179 moveItemReq->setItemIds(EwsId::List() << id); 0180 moveItemReq->setDestinationFolderId(EwsId(mCollection.remoteId())); 0181 connect(moveItemReq, &EwsCreateItemRequest::finished, this, &EwsCreateMailJob::mailMoveWorkaroundFinished); 0182 addSubjob(moveItemReq); 0183 moveItemReq->start(); 0184 } else { 0185 setErrorMsg(i18n("Failed to create mail item")); 0186 emitResult(); 0187 } 0188 } 0189 0190 void EwsCreateMailJob::mailMoveWorkaroundFinished(KJob *job) 0191 { 0192 auto req = qobject_cast<EwsMoveItemRequest *>(job); 0193 if (job->error()) { 0194 setErrorMsg(job->errorString()); 0195 emitResult(); 0196 return; 0197 } 0198 0199 if (!req) { 0200 setErrorMsg(QStringLiteral("Invalid EwsMoveItemRequest job object")); 0201 emitResult(); 0202 return; 0203 } 0204 0205 if (req->responses().count() != 1) { 0206 setErrorMsg(QStringLiteral("Invalid number of responses received from server.")); 0207 emitResult(); 0208 return; 0209 } 0210 0211 EwsMoveItemRequest::Response resp = req->responses().first(); 0212 if (resp.isSuccess()) { 0213 EwsId id = resp.itemId(); 0214 mItem.setRemoteId(id.id()); 0215 mItem.setRemoteRevision(id.changeKey()); 0216 mItem.setParentCollection(mCollection); 0217 } else { 0218 setErrorMsg(i18n("Failed to create mail item")); 0219 } 0220 0221 emitResult(); 0222 } 0223 0224 bool EwsCreateMailJob::setSend(bool send) 0225 { 0226 mSend = send; 0227 return true; 0228 } 0229 0230 #include "moc_ewscreatemailjob.cpp"