1 /*
2     SPDX-FileCopyrightText: 2015-2016 Krzysztof Nowicki <krissn@op.pl>
3 
4     SPDX-License-Identifier: LGPL-2.0-or-later
5 */
6 
7 #include "ewsmailhandler.h"
8 
9 #include <Akonadi/Item>
10 #include <Akonadi/KMime/MessageFlags>
11 #include <KMime/Message>
12 
13 #include "ewscreatemailjob.h"
14 #include "ewsfetchmaildetailjob.h"
15 #include "ewsmodifymailjob.h"
16 #include "ewsresource_debug.h"
17 
18 using namespace Akonadi;
19 
20 static const EwsPropertyField propPidFlagStatus(0x1090, EwsPropTypeInteger);
21 
EwsMailHandler()22 EwsMailHandler::EwsMailHandler()
23 {
24 }
25 
~EwsMailHandler()26 EwsMailHandler::~EwsMailHandler()
27 {
28 }
29 
factory()30 EwsItemHandler *EwsMailHandler::factory()
31 {
32     return new EwsMailHandler();
33 }
34 
fetchItemDetailJob(EwsClient & client,QObject * parent,const Akonadi::Collection & collection)35 EwsFetchItemDetailJob *EwsMailHandler::fetchItemDetailJob(EwsClient &client, QObject *parent, const Akonadi::Collection &collection)
36 {
37     return new EwsFetchMailDetailJob(client, parent, collection);
38 }
39 
setSeenFlag(Item & item,bool value)40 void EwsMailHandler::setSeenFlag(Item &item, bool value)
41 {
42     if (value) {
43         item.setFlag(MessageFlags::Seen);
44     } else {
45         item.clearFlag(MessageFlags::Seen);
46     }
47 }
48 
mimeType()49 QString EwsMailHandler::mimeType()
50 {
51     return KMime::Message::mimeType();
52 }
53 
setItemPayload(Akonadi::Item & item,const EwsItem & ewsItem)54 bool EwsMailHandler::setItemPayload(Akonadi::Item &item, const EwsItem &ewsItem)
55 {
56     QByteArray mimeContent = ewsItem[EwsItemFieldMimeContent].toByteArray();
57     if (mimeContent.isEmpty()) {
58         qCWarning(EWSRES_LOG) << QStringLiteral("MIME content is empty!");
59         return false;
60     }
61 
62     mimeContent.replace("\r\n", "\n");
63 
64     KMime::Message::Ptr msg(new KMime::Message);
65     msg->setContent(mimeContent);
66     msg->parse();
67     // Some messages might just be empty (just headers). This results in the body being empty.
68     // The problem is that when Akonadi sees an empty body it will interpret this as "body not
69     // yet loaded" and will retry which will cause an endless loop. To work around this put a
70     // single newline so that it is not empty.
71     if (msg->body().isEmpty()) {
72         msg->setBody("\n");
73     }
74     item.setPayload<KMime::Message::Ptr>(msg);
75     return true;
76 }
77 
modifyItemJob(EwsClient & client,const QVector<Akonadi::Item> & items,const QSet<QByteArray> & parts,QObject * parent)78 EwsModifyItemJob *EwsMailHandler::modifyItemJob(EwsClient &client, const QVector<Akonadi::Item> &items, const QSet<QByteArray> &parts, QObject *parent)
79 {
80     return new EwsModifyMailJob(client, items, parts, parent);
81 }
82 
83 EwsCreateItemJob *
createItemJob(EwsClient & client,const Akonadi::Item & item,const Akonadi::Collection & collection,EwsTagStore * tagStore,EwsResource * parent)84 EwsMailHandler::createItemJob(EwsClient &client, const Akonadi::Item &item, const Akonadi::Collection &collection, EwsTagStore *tagStore, EwsResource *parent)
85 {
86     return new EwsCreateMailJob(client, item, collection, tagStore, parent);
87 }
88 
writeFlags(const QSet<QByteArray> & flags)89 QHash<EwsPropertyField, QVariant> EwsMailHandler::writeFlags(const QSet<QByteArray> &flags)
90 {
91     // Strip all the message flags that can be stored in dedicated Exchange fields and leave
92     // any remaining ones to be stored in a private property.
93 
94     QSet<QByteArray> unknownFlags;
95     QHash<EwsPropertyField, QVariant> propertyHash;
96     bool isRead = false;
97     bool isFlagged = false;
98 
99     for (const QByteArray &flag : flags) {
100         if (flag == MessageFlags::Seen) {
101             isRead = true;
102         } else if (flag == MessageFlags::Flagged) {
103             isFlagged = true;
104         } else if (flag == MessageFlags::HasAttachment || flag == MessageFlags::HasInvitation || flag == MessageFlags::Signed
105                    || flag == MessageFlags::Encrypted) {
106             // These flags are read-only. Remove them from the unknown list but don't do anything with them.
107         } else {
108             unknownFlags.insert(flag);
109         }
110     }
111 
112     propertyHash.insert(EwsPropertyField(QStringLiteral("message:IsRead")), isRead ? QStringLiteral("true") : QStringLiteral("false"));
113     if (isFlagged) {
114         propertyHash.insert(propPidFlagStatus, QStringLiteral("2"));
115     } else {
116         propertyHash.insert(propPidFlagStatus, QVariant());
117     }
118 
119     propertyHash.insert(EwsItemHandler::writeFlags(unknownFlags));
120 
121     return propertyHash;
122 }
123 
readFlags(const EwsItem & item)124 QSet<QByteArray> EwsMailHandler::readFlags(const EwsItem &item)
125 {
126     QSet<QByteArray> flags = EwsItemHandler::readFlags(item);
127 
128     QVariant v = item[EwsItemFieldIsRead];
129     if (v.isValid() && v.toBool()) {
130         flags.insert(MessageFlags::Seen);
131     }
132 
133     v = item[EwsItemFieldHasAttachments];
134     if (v.isValid() && v.toBool()) {
135         flags.insert(MessageFlags::HasAttachment);
136     }
137 
138     QVariant flagProp = item[propPidFlagStatus];
139     if (!flagProp.isNull() && (flagProp.toUInt() == 2)) {
140         flags.insert(MessageFlags::Flagged);
141     }
142 
143     if (item.type() == EwsItemTypeMeetingRequest) {
144         flags.insert(MessageFlags::HasInvitation);
145     }
146 
147     return flags;
148 }
149 
flagsProperties()150 QList<EwsPropertyField> EwsMailHandler::flagsProperties()
151 {
152     QList<EwsPropertyField> props = EwsItemHandler::flagsProperties();
153 
154     props.append(propPidFlagStatus);
155     props.append(EwsPropertyField(QStringLiteral("message:IsRead")));
156     props.append(EwsPropertyField(QStringLiteral("item:HasAttachments")));
157 
158     return props;
159 }
160 
161 EWS_DECLARE_ITEM_HANDLER(EwsMailHandler, EwsItemTypeMessage)
162 EWS_DECLARE_ITEM_HANDLER(EwsMailHandler, EwsItemTypePostItem)
163