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