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"