1 /*
2     SPDX-FileCopyrightText: 2017 Krzysztof Nowicki <krissn@op.pl>
3 
4     SPDX-License-Identifier: LGPL-2.0-or-later
5 */
6 
7 #include "ewsattachment.h"
8 
9 #include <QBitArray>
10 
11 #include "ewsclient_debug.h"
12 #include "ewsxml.h"
13 
14 class EwsAttachmentPrivate : public QSharedData
15 {
16 public:
17     EwsAttachmentPrivate();
18     ~EwsAttachmentPrivate();
19 
20     enum Field {
21         Id = 0,
22         Name,
23         ContentType,
24         ContentId,
25         ContentLocation,
26         Size,
27         LastModifiedTime,
28         IsInline,
29         IsContactPhoto,
30         Content,
31         Item,
32         NumFields,
33     };
34 
35     EwsAttachment::Type mType;
36     QString mId;
37     QString mName;
38     QString mContentType;
39     QString mContentId;
40     QString mContentLocation;
41     long mSize = 0;
42     QDateTime mLastModifiedTime;
43     bool mIsInline = false;
44     bool mIsContactPhoto = false;
45     QByteArray mContent;
46     EwsItem mItem;
47     bool mValid = false;
48     QBitArray mValidFields;
49 };
50 
EwsAttachmentPrivate()51 EwsAttachmentPrivate::EwsAttachmentPrivate()
52     : mType(EwsAttachment::UnknownAttachment)
53     , mValidFields(NumFields)
54 {
55 }
56 
~EwsAttachmentPrivate()57 EwsAttachmentPrivate::~EwsAttachmentPrivate()
58 {
59 }
60 
EwsAttachment()61 EwsAttachment::EwsAttachment()
62     : d(new EwsAttachmentPrivate())
63 {
64 }
65 
~EwsAttachment()66 EwsAttachment::~EwsAttachment()
67 {
68 }
69 
EwsAttachment(QXmlStreamReader & reader)70 EwsAttachment::EwsAttachment(QXmlStreamReader &reader)
71     : d(new EwsAttachmentPrivate())
72 {
73     bool ok = true;
74 
75     if (reader.namespaceUri() != ewsTypeNsUri) {
76         qCWarningNC(EWSCLI_LOG) << QStringLiteral("Unexpected namespace in Attachment element:") << reader.namespaceUri();
77         reader.skipCurrentElement();
78         return;
79     }
80 
81     const QStringRef readerName = reader.name();
82     if (readerName == QLatin1String("ItemAttachment")) {
83         d->mType = ItemAttachment;
84     } else if (readerName == QLatin1String("FileAttachment")) {
85         d->mType = FileAttachment;
86     } else if (readerName == QLatin1String("ReferenceAttachment")) {
87         d->mType = ReferenceAttachment;
88     } else {
89         qCWarningNC(EWSCLI_LOG) << QStringLiteral("Unknown attachment type %1").arg(readerName.toString());
90         ok = false;
91     }
92 
93     // Skip this attachment type as it's not clearly documented.
94     if (d->mType == ReferenceAttachment) {
95         qCWarningNC(EWSCLI_LOG) << QStringLiteral("Attachment type ReferenceAttachment not fully supported");
96         reader.skipCurrentElement();
97         d->mValid = true;
98         return;
99     }
100 
101     while (ok && reader.readNextStartElement()) {
102         if (reader.namespaceUri() != ewsTypeNsUri) {
103             qCWarningNC(EWSCLI_LOG) << QStringLiteral("Unexpected namespace in Attachment element:") << reader.namespaceUri();
104             reader.skipCurrentElement();
105             ok = false;
106             break;
107         }
108 
109         const QString elmName = reader.name().toString();
110         if (elmName == QLatin1String("AttachmentId")) {
111             QXmlStreamAttributes attrs = reader.attributes();
112             if (!attrs.hasAttribute(QStringLiteral("Id"))) {
113                 qCWarningNC(EWSCLI_LOG) << QStringLiteral("Failed to read %1 element - missing Id in AttachmentId element.").arg(QStringLiteral("Attachment"));
114                 reader.skipCurrentElement();
115                 ok = false;
116             } else {
117                 d->mId = attrs.value(QStringLiteral("Id")).toString();
118                 d->mValidFields.setBit(EwsAttachmentPrivate::Id);
119             }
120             reader.skipCurrentElement();
121         } else if (elmName == QLatin1String("Name")) {
122             d->mName = readXmlElementValue<QString>(reader, ok, QStringLiteral("Attachment"));
123             d->mValidFields.setBit(EwsAttachmentPrivate::Name, ok);
124         } else if (elmName == QLatin1String("ContentType")) {
125             d->mContentType = readXmlElementValue<QString>(reader, ok, QStringLiteral("Attachment"));
126             d->mValidFields.setBit(EwsAttachmentPrivate::ContentType, ok);
127         } else if (elmName == QLatin1String("ContentId")) {
128             d->mContentId = readXmlElementValue<QString>(reader, ok, QStringLiteral("Attachment"));
129             d->mValidFields.setBit(EwsAttachmentPrivate::ContentId, ok);
130         } else if (elmName == QLatin1String("ContentLocation")) {
131             d->mContentLocation = readXmlElementValue<QString>(reader, ok, QStringLiteral("Attachment"));
132             d->mValidFields.setBit(EwsAttachmentPrivate::ContentLocation, ok);
133         } else if (elmName == QLatin1String("AttachmentOriginalUrl")) {
134             // Ignore
135             reader.skipCurrentElement();
136         } else if (elmName == QLatin1String("Size")) {
137             d->mSize = readXmlElementValue<long>(reader, ok, QStringLiteral("Attachment"));
138             d->mValidFields.setBit(EwsAttachmentPrivate::Size, ok);
139         } else if (elmName == QLatin1String("LastModifiedTime")) {
140             d->mLastModifiedTime = readXmlElementValue<QDateTime>(reader, ok, QStringLiteral("Attachment"));
141             d->mValidFields.setBit(EwsAttachmentPrivate::LastModifiedTime, ok);
142         } else if (elmName == QLatin1String("IsInline")) {
143             d->mIsInline = readXmlElementValue<bool>(reader, ok, QStringLiteral("Attachment"));
144             d->mValidFields.setBit(EwsAttachmentPrivate::IsInline, ok);
145         } else if (d->mType == FileAttachment && elmName == QLatin1String("IsContactPhoto")) {
146             d->mIsContactPhoto = readXmlElementValue<bool>(reader, ok, QStringLiteral("Attachment"));
147             d->mValidFields.setBit(EwsAttachmentPrivate::IsContactPhoto, ok);
148         } else if (d->mType == FileAttachment && elmName == QLatin1String("Content")) {
149             d->mContent = readXmlElementValue<QByteArray>(reader, ok, QStringLiteral("Attachment"));
150             d->mValidFields.setBit(EwsAttachmentPrivate::Content, ok);
151         } else if (d->mType == ItemAttachment
152                    && (elmName == QLatin1String("Item") || elmName == QLatin1String("Message") || elmName == QLatin1String("CalendarItem")
153                        || elmName == QLatin1String("Contact") || elmName == QLatin1String("MeetingMessage") || elmName == QLatin1String("MeetingRequest")
154                        || elmName == QLatin1String("MeetingResponse") || elmName == QLatin1String("MeetingCancellation") || elmName == QLatin1String("Task"))) {
155             d->mItem = EwsItem(reader);
156             if (!d->mItem.isValid()) {
157                 qCWarningNC(EWSCLI_LOG)
158                     << QStringLiteral("Failed to read %1 element - invalid %2 element.").arg(QStringLiteral("Attachment"), QStringLiteral("Item"));
159                 reader.skipCurrentElement();
160                 ok = false;
161             } else {
162                 d->mValidFields.setBit(EwsAttachmentPrivate::Item);
163             }
164         } else {
165             qCWarningNC(EWSCLI_LOG) << QStringLiteral("Failed to read %1 element - unknown %2 element.").arg(QStringLiteral("Attachment"), elmName);
166             reader.skipCurrentElement();
167             ok = false;
168         }
169     }
170 
171     if (!ok) {
172         reader.skipCurrentElement();
173     }
174     d->mValid = ok;
175 }
176 
EwsAttachment(const EwsAttachment & other)177 EwsAttachment::EwsAttachment(const EwsAttachment &other)
178 {
179     d = other.d;
180 }
181 
EwsAttachment(EwsAttachment && other)182 EwsAttachment::EwsAttachment(EwsAttachment &&other)
183 {
184     d = std::move(other.d);
185 }
186 
operator =(EwsAttachment && other)187 EwsAttachment &EwsAttachment::operator=(EwsAttachment &&other)
188 {
189     d = std::move(other.d);
190     return *this;
191 }
192 
operator =(const EwsAttachment & other)193 EwsAttachment &EwsAttachment::operator=(const EwsAttachment &other)
194 {
195     d = other.d;
196     return *this;
197 }
198 
write(QXmlStreamWriter & writer) const199 void EwsAttachment::write(QXmlStreamWriter &writer) const
200 {
201     QString elmName;
202     switch (d->mType) {
203     case ItemAttachment:
204         elmName = QStringLiteral("ItemAttachment");
205         break;
206     case FileAttachment:
207         elmName = QStringLiteral("FileAttachment");
208         break;
209     case ReferenceAttachment:
210         elmName = QStringLiteral("ReferenceAttachment");
211         break;
212     default:
213         qCWarning(EWSCLI_LOG) << QStringLiteral("Failed to write Attachment element - invalid attachment type.");
214         return;
215     }
216     writer.writeStartElement(ewsTypeNsUri, elmName);
217 
218     if (d->mType == ReferenceAttachment) {
219         qCWarningNC(EWSCLI_LOG) << QStringLiteral("Attachment type ReferenceAttachment not fully supported");
220         writer.writeEndElement();
221         return;
222     }
223 
224     if (d->mValidFields[EwsAttachmentPrivate::Id]) {
225         writer.writeStartElement(ewsTypeNsUri, QStringLiteral("AttachmentId"));
226         writer.writeAttribute(QStringLiteral("Id"), d->mId);
227         writer.writeEndElement();
228     }
229     if (d->mValidFields[EwsAttachmentPrivate::Name]) {
230         writer.writeTextElement(ewsTypeNsUri, QStringLiteral("Name"), d->mName);
231     }
232     if (d->mValidFields[EwsAttachmentPrivate::ContentType]) {
233         writer.writeTextElement(ewsTypeNsUri, QStringLiteral("ContentType"), d->mContentType);
234     }
235     if (d->mValidFields[EwsAttachmentPrivate::ContentId]) {
236         writer.writeTextElement(ewsTypeNsUri, QStringLiteral("ContentId"), d->mContentId);
237     }
238     if (d->mValidFields[EwsAttachmentPrivate::ContentLocation]) {
239         writer.writeTextElement(ewsTypeNsUri, QStringLiteral("ContentLocation"), d->mContentLocation);
240     }
241     if (d->mValidFields[EwsAttachmentPrivate::Size]) {
242         writer.writeTextElement(ewsTypeNsUri, QStringLiteral("Size"), QString::number(d->mSize));
243     }
244     if (d->mValidFields[EwsAttachmentPrivate::LastModifiedTime]) {
245         writer.writeTextElement(ewsTypeNsUri, QStringLiteral("LastModifiedTime"), d->mLastModifiedTime.toString(Qt::ISODate));
246     }
247     if (d->mValidFields[EwsAttachmentPrivate::IsInline]) {
248         writer.writeTextElement(ewsTypeNsUri, QStringLiteral("IsInline"), d->mIsInline ? QStringLiteral("true") : QStringLiteral("false"));
249     }
250     if (d->mType == FileAttachment) {
251         if (d->mValidFields[EwsAttachmentPrivate::IsContactPhoto]) {
252             writer.writeTextElement(ewsTypeNsUri, QStringLiteral("IsContactPhoto"), d->mIsContactPhoto ? QStringLiteral("true") : QStringLiteral("false"));
253         }
254         if (d->mValidFields[EwsAttachmentPrivate::Content]) {
255             writer.writeTextElement(ewsTypeNsUri, QStringLiteral("Content"), QString::fromLatin1(d->mContent.toBase64()));
256         }
257     } else if (d->mType == ItemAttachment) {
258         if (d->mValidFields[EwsAttachmentPrivate::Item]) {
259             d->mItem.write(writer);
260         }
261     }
262 
263     writer.writeEndElement();
264 }
265 
isValid() const266 bool EwsAttachment::isValid() const
267 {
268     return d->mValid;
269 }
270 
type() const271 EwsAttachment::Type EwsAttachment::type() const
272 {
273     return d->mType;
274 }
275 
setType(Type type)276 void EwsAttachment::setType(Type type)
277 {
278     d->mType = type;
279 }
280 
id() const281 QString EwsAttachment::id() const
282 {
283     return d->mId;
284 }
285 
setId(const QString & id)286 void EwsAttachment::setId(const QString &id)
287 {
288     d->mId = id;
289     d->mValidFields.setBit(EwsAttachmentPrivate::Id);
290 }
291 
resetId()292 void EwsAttachment::resetId()
293 {
294     d->mValidFields.clearBit(EwsAttachmentPrivate::Id);
295 }
296 
hasId() const297 bool EwsAttachment::hasId() const
298 {
299     return d->mValidFields[EwsAttachmentPrivate::Id];
300 }
301 
name() const302 QString EwsAttachment::name() const
303 {
304     return d->mName;
305 }
306 
setName(const QString & name)307 void EwsAttachment::setName(const QString &name)
308 {
309     d->mName = name;
310     d->mValidFields.setBit(EwsAttachmentPrivate::Name);
311 }
312 
resetName()313 void EwsAttachment::resetName()
314 {
315     d->mValidFields.clearBit(EwsAttachmentPrivate::Name);
316 }
317 
hasName() const318 bool EwsAttachment::hasName() const
319 {
320     return d->mValidFields[EwsAttachmentPrivate::Name];
321 }
322 
contentType() const323 QString EwsAttachment::contentType() const
324 {
325     return d->mContentType;
326 }
327 
setContentType(const QString & contentType)328 void EwsAttachment::setContentType(const QString &contentType)
329 {
330     d->mContentType = contentType;
331     d->mValidFields.setBit(EwsAttachmentPrivate::ContentType);
332 }
333 
resetContentType()334 void EwsAttachment::resetContentType()
335 {
336     d->mValidFields.clearBit(EwsAttachmentPrivate::ContentType);
337 }
338 
hasContentType() const339 bool EwsAttachment::hasContentType() const
340 {
341     return d->mValidFields[EwsAttachmentPrivate::ContentType];
342 }
343 
contentId() const344 QString EwsAttachment::contentId() const
345 {
346     return d->mContentId;
347 }
348 
setContentId(const QString & contentId)349 void EwsAttachment::setContentId(const QString &contentId)
350 {
351     d->mContentId = contentId;
352     d->mValidFields.setBit(EwsAttachmentPrivate::ContentId);
353 }
354 
resetContentId()355 void EwsAttachment::resetContentId()
356 {
357     d->mValidFields.clearBit(EwsAttachmentPrivate::ContentId);
358 }
359 
hasContentId() const360 bool EwsAttachment::hasContentId() const
361 {
362     return d->mValidFields[EwsAttachmentPrivate::ContentId];
363 }
364 
contentLocation() const365 QString EwsAttachment::contentLocation() const
366 {
367     return d->mContentLocation;
368 }
369 
setContentLocation(const QString & contentLocation)370 void EwsAttachment::setContentLocation(const QString &contentLocation)
371 {
372     d->mContentLocation = contentLocation;
373     d->mValidFields.setBit(EwsAttachmentPrivate::ContentLocation);
374 }
375 
resetContentLocation()376 void EwsAttachment::resetContentLocation()
377 {
378     d->mValidFields.clearBit(EwsAttachmentPrivate::ContentLocation);
379 }
380 
hasContentLocation() const381 bool EwsAttachment::hasContentLocation() const
382 {
383     return d->mValidFields[EwsAttachmentPrivate::ContentLocation];
384 }
385 
size() const386 long EwsAttachment::size() const
387 {
388     return d->mSize;
389 }
390 
setSize(long size)391 void EwsAttachment::setSize(long size)
392 {
393     d->mSize = size;
394     d->mValidFields.setBit(EwsAttachmentPrivate::Size);
395 }
396 
resetSize()397 void EwsAttachment::resetSize()
398 {
399     d->mValidFields.clearBit(EwsAttachmentPrivate::Size);
400 }
401 
hasSize() const402 bool EwsAttachment::hasSize() const
403 {
404     return d->mValidFields[EwsAttachmentPrivate::Size];
405 }
406 
lastModifiedTime() const407 QDateTime EwsAttachment::lastModifiedTime() const
408 {
409     return d->mLastModifiedTime;
410 }
411 
setLastModifiedTime(const QDateTime & time)412 void EwsAttachment::setLastModifiedTime(const QDateTime &time)
413 {
414     d->mLastModifiedTime = time;
415     d->mValidFields.setBit(EwsAttachmentPrivate::LastModifiedTime);
416 }
417 
resetLastModifiedTime()418 void EwsAttachment::resetLastModifiedTime()
419 {
420     d->mValidFields.clearBit(EwsAttachmentPrivate::LastModifiedTime);
421 }
422 
hasLastModifiedTime() const423 bool EwsAttachment::hasLastModifiedTime() const
424 {
425     return d->mValidFields[EwsAttachmentPrivate::LastModifiedTime];
426 }
427 
isInline() const428 bool EwsAttachment::isInline() const
429 {
430     return d->mIsInline;
431 }
432 
setIsInline(bool isInline)433 void EwsAttachment::setIsInline(bool isInline)
434 {
435     d->mIsInline = isInline;
436     d->mValidFields.setBit(EwsAttachmentPrivate::IsInline);
437 }
438 
resetIsInline()439 void EwsAttachment::resetIsInline()
440 {
441     d->mValidFields.clearBit(EwsAttachmentPrivate::IsInline);
442 }
443 
hasIsInline() const444 bool EwsAttachment::hasIsInline() const
445 {
446     return d->mValidFields[EwsAttachmentPrivate::IsInline];
447 }
448 
isContactPhoto() const449 bool EwsAttachment::isContactPhoto() const
450 {
451     return d->mIsContactPhoto;
452 }
453 
setIsContactPhoto(bool isContactPhoto)454 void EwsAttachment::setIsContactPhoto(bool isContactPhoto)
455 {
456     d->mIsContactPhoto = isContactPhoto;
457     d->mValidFields.setBit(EwsAttachmentPrivate::IsContactPhoto);
458 }
459 
resetIsContactPhoto()460 void EwsAttachment::resetIsContactPhoto()
461 {
462     d->mValidFields.clearBit(EwsAttachmentPrivate::IsContactPhoto);
463 }
464 
hasIsContactPhoto() const465 bool EwsAttachment::hasIsContactPhoto() const
466 {
467     return d->mValidFields[EwsAttachmentPrivate::IsContactPhoto];
468 }
469 
content() const470 QByteArray EwsAttachment::content() const
471 {
472     return d->mContent;
473 }
474 
setContent(const QByteArray & content)475 void EwsAttachment::setContent(const QByteArray &content)
476 {
477     d->mContent = content;
478     d->mValidFields.setBit(EwsAttachmentPrivate::Content);
479 }
480 
resetContent()481 void EwsAttachment::resetContent()
482 {
483     d->mValidFields.clearBit(EwsAttachmentPrivate::Content);
484 }
485 
hasContent() const486 bool EwsAttachment::hasContent() const
487 {
488     return d->mValidFields[EwsAttachmentPrivate::Content];
489 }
490 
item() const491 const EwsItem &EwsAttachment::item() const
492 {
493     return d->mItem;
494 }
495 
setItem(const EwsItem & item)496 void EwsAttachment::setItem(const EwsItem &item)
497 {
498     d->mItem = item;
499     d->mValidFields.setBit(EwsAttachmentPrivate::Item);
500 }
501 
resetItem()502 void EwsAttachment::resetItem()
503 {
504     d->mValidFields.clearBit(EwsAttachmentPrivate::Item);
505 }
506 
hasItem() const507 bool EwsAttachment::hasItem() const
508 {
509     return d->mValidFields[EwsAttachmentPrivate::Item];
510 }
511