1 /*
2     SPDX-FileCopyrightText: 2015-2017 Krzysztof Nowicki <krissn@op.pl>
3 
4     SPDX-License-Identifier: LGPL-2.0-or-later
5 */
6 
7 #pragma once
8 
9 #include <functional>
10 
11 #include <QVector>
12 #include <QXmlStreamReader>
13 
14 #include "ewsclient_debug.h"
15 
16 template<typename T> class EwsXml
17 {
18 public:
19     typedef std::function<bool(QXmlStreamReader &, QVariant &)> ReadFunction;
20     typedef std::function<bool(QXmlStreamWriter &, const QVariant &)> WriteFunction;
21     typedef std::function<bool(QXmlStreamReader &, const QString &)> UnknownElementFunction;
22 
23     typedef QHash<T, QVariant> ValueHash;
24 
25     static constexpr T Ignore = static_cast<T>(-1);
26 
27     struct Item {
ItemItem28         Item()
29             : key(Ignore)
30         {
31         }
32 
33         Item(T k, const QString &n, const ReadFunction &rfn = ReadFunction(), const WriteFunction &wfn = WriteFunction())
keyItem34             : key(k)
35             , elmName(n)
36             , readFn(rfn)
37             , writeFn(wfn)
38         {
39         }
40 
41         T key;
42         QString elmName;
43         ReadFunction readFn;
44         WriteFunction writeFn;
45     };
46 
EwsXml()47     EwsXml()
48     {
49     }
50 
EwsXml(const QVector<Item> & items)51     EwsXml(const QVector<Item> &items)
52         : mItems(items)
53     {
54         rebuildItemHash();
55     }
56 
EwsXml(const EwsXml & other)57     EwsXml(const EwsXml &other)
58         : mItems(other.mItems)
59         , mValues(other.mValues)
60         , mItemHash(other.mItemHash)
61     {
62     }
63 
setItems(const QVector<Item> & items)64     void setItems(const QVector<Item> &items)
65     {
66         mItems = items;
67         rebuildItemHash();
68     }
69 
70     bool readItem(QXmlStreamReader &reader, const QString &parentElm, const QString &nsUri, UnknownElementFunction unknownElmFn = &defaultUnknownElmFunction)
71     {
72         typename QHash<QString, Item>::iterator it = mItemHash.find(reader.name().toString());
73         if (it != mItemHash.end() && nsUri == reader.namespaceUri()) {
74             if (it->key == Ignore) {
75                 qCInfoNC(EWSCLI_LOG) << QStringLiteral("Unsupported %1 child element %2 - ignoring.").arg(parentElm).arg(reader.name().toString());
76                 reader.skipCurrentElement();
77                 return true;
78             } else if (!it->readFn) {
79                 qCWarning(EWSCLI_LOG)
80                     << QStringLiteral("Failed to read %1 element - no read support for %2 element.").arg(parentElm).arg(reader.name().toString());
81                 return false;
82             } else {
83                 QVariant val = mValues[it->key];
84                 if (it->readFn(reader, val)) {
85                     mValues[it->key] = val;
86                     return true;
87                 }
88                 return false;
89             }
90         }
91         return unknownElmFn(reader, parentElm);
92     }
93 
94     bool readItems(QXmlStreamReader &reader, const QString &nsUri, const UnknownElementFunction &unknownElmFn = &defaultUnknownElmFunction)
95     {
96         QString elmName(reader.name().toString());
97         while (reader.readNextStartElement()) {
98             if (!readItem(reader, elmName, nsUri, unknownElmFn)) {
99                 return false;
100             }
101         }
102         return true;
103     }
104 
105     bool writeItems(QXmlStreamWriter &writer,
106                     const QString &parentElm,
107                     const QString &nsUri,
108                     const ValueHash &values,
109                     const QList<T> &keysToWrite = QList<T>()) const
110     {
111         bool hasKeysToWrite = !keysToWrite.isEmpty();
112         for (const Item &item : std::as_const(mItems)) {
113             if (!hasKeysToWrite || keysToWrite.contains(item.key)) {
114                 typename ValueHash::const_iterator it = values.find(item.key);
115                 if (it != values.end()) {
116                     if (!item.writeFn) {
117                         qCWarning(EWSCLI_LOG)
118                             << QStringLiteral("Failed to write %1 element - no write support for %2 element.").arg(parentElm).arg(item.elmName);
119                         return false;
120                     }
121                     writer.writeStartElement(nsUri, item.elmName);
122                     bool status = item.writeFn(writer, *it);
123                     writer.writeEndElement();
124                     if (!status) {
125                         return false;
126                     }
127                 }
128             }
129         }
130         return true;
131     }
132 
values()133     ValueHash values() const
134     {
135         return mValues;
136     }
137 
138 private:
defaultUnknownElmFunction(QXmlStreamReader & reader,const QString & parentElm)139     static bool defaultUnknownElmFunction(QXmlStreamReader &reader, const QString &parentElm)
140     {
141         qCWarning(EWSCLI_LOG) << QStringLiteral("Failed to read %1 element - invalid %2 element.").arg(parentElm).arg(reader.name().toString());
142         return false;
143     }
144 
145     const QVector<Item> mItems;
146     ValueHash mValues;
147     QHash<QString, Item> mItemHash;
148 
rebuildItemHash()149     void rebuildItemHash()
150     {
151         for (const Item &item : std::as_const(mItems)) {
152             mItemHash.insert(item.elmName, item);
153         }
154     }
155 };
156 
157 template<typename T> T readXmlElementValue(QXmlStreamReader &reader, bool &ok, const QString &parentElement);
158 
159 extern bool ewsXmlBoolReader(QXmlStreamReader &reader, QVariant &val);
160 extern bool ewsXmlBoolWriter(QXmlStreamWriter &writer, const QVariant &val);
161 extern bool ewsXmlBase64Reader(QXmlStreamReader &reader, QVariant &val);
162 extern bool ewsXmlBase64Writer(QXmlStreamWriter &writer, const QVariant &val);
163 extern bool ewsXmlIdReader(QXmlStreamReader &reader, QVariant &val);
164 extern bool ewsXmlIdWriter(QXmlStreamWriter &writer, const QVariant &val);
165 extern bool ewsXmlTextReader(QXmlStreamReader &reader, QVariant &val);
166 extern bool ewsXmlTextWriter(QXmlStreamWriter &writer, const QVariant &val);
167 extern bool ewsXmlUIntReader(QXmlStreamReader &reader, QVariant &val);
168 extern bool ewsXmlUIntWriter(QXmlStreamWriter &writer, const QVariant &val);
169 extern bool ewsXmlDateTimeReader(QXmlStreamReader &reader, QVariant &val);
170 extern bool ewsXmlItemReader(QXmlStreamReader &reader, QVariant &val);
171 extern bool ewsXmlFolderReader(QXmlStreamReader &reader, QVariant &val);
172 
173 extern bool ewsXmlEnumReader(QXmlStreamReader &reader, QVariant &val, const QVector<QString> &items);
174 extern bool ewsXmlSensitivityReader(QXmlStreamReader &reader, QVariant &val);
175 extern bool ewsXmlImportanceReader(QXmlStreamReader &reader, QVariant &val);
176 extern bool ewsXmlCalendarItemTypeReader(QXmlStreamReader &reader, QVariant &val);
177 extern bool ewsXmlLegacyFreeBusyStatusReader(QXmlStreamReader &reader, QVariant &val);
178 extern bool ewsXmlResponseTypeReader(QXmlStreamReader &reader, QVariant &val);
179 
180