1 /*
2     This file is part of the KContacts framework.
3     SPDX-FileCopyrightText: 2008 Tobias Koenig <tokoe@kde.org>
4     SPDX-FileCopyrightText: 2008 Kevin Krammer <kevin.krammer@gmx.at>
5 
6     SPDX-License-Identifier: LGPL-2.0-or-later
7 */
8 
9 #include "contactgrouptool.h"
10 #include "contactgroup.h"
11 
12 #include <QIODevice>
13 
14 #include <QXmlStreamReader>
15 #include <QXmlStreamWriter>
16 
17 using namespace KContacts;
18 
19 class XmlContactGroupWriter : public QXmlStreamWriter
20 {
21 public:
22     XmlContactGroupWriter();
23 
24     void write(const ContactGroup &group, QIODevice *device);
25     void write(const QVector<ContactGroup> &groupLis, QIODevice *device);
26 
27 private:
28     void writeGroup(const ContactGroup &group);
29     void writeContactReference(const ContactGroup::ContactReference &reference);
30     void writeContactGroupReference(const ContactGroup::ContactGroupReference &reference);
31     void writeData(const ContactGroup::Data &data);
32 };
33 
XmlContactGroupWriter()34 XmlContactGroupWriter::XmlContactGroupWriter()
35 {
36     setAutoFormatting(true);
37 }
38 
write(const ContactGroup & group,QIODevice * device)39 void XmlContactGroupWriter::write(const ContactGroup &group, QIODevice *device)
40 {
41     setDevice(device);
42 
43     writeStartDocument();
44 
45     writeGroup(group);
46 
47     writeEndDocument();
48 }
49 
write(const QVector<ContactGroup> & groupList,QIODevice * device)50 void XmlContactGroupWriter::write(const QVector<ContactGroup> &groupList, QIODevice *device)
51 {
52     setDevice(device);
53 
54     writeStartDocument();
55 
56     writeStartElement(QStringLiteral("contactGroupList"));
57 
58     for (const ContactGroup &group : groupList) {
59         writeGroup(group);
60     }
61 
62     writeEndElement();
63 
64     writeEndDocument();
65 }
66 
writeGroup(const ContactGroup & group)67 void XmlContactGroupWriter::writeGroup(const ContactGroup &group)
68 {
69     writeStartElement(QStringLiteral("contactGroup"));
70     writeAttribute(QStringLiteral("uid"), group.id());
71     writeAttribute(QStringLiteral("name"), group.name());
72 
73     const uint contactCount(group.contactReferenceCount());
74     for (uint i = 0; i < contactCount; ++i) {
75         writeContactReference(group.contactReference(i));
76     }
77 
78     const uint contactGroupReference(group.contactGroupReferenceCount());
79     for (uint i = 0; i < contactGroupReference; ++i) {
80         writeContactGroupReference(group.contactGroupReference(i));
81     }
82 
83     const uint dataCount(group.dataCount());
84     for (uint i = 0; i < dataCount; ++i) {
85         writeData(group.data(i));
86     }
87 
88     writeEndElement();
89 }
90 
writeContactReference(const ContactGroup::ContactReference & reference)91 void XmlContactGroupWriter::writeContactReference(const ContactGroup::ContactReference &reference)
92 {
93     writeStartElement(QStringLiteral("contactReference"));
94     writeAttribute(QStringLiteral("uid"), reference.uid());
95     writeAttribute(QStringLiteral("gid"), reference.gid());
96     if (!reference.preferredEmail().isEmpty()) {
97         writeAttribute(QStringLiteral("preferredEmail"), reference.preferredEmail());
98     }
99 
100     // TODO: customs
101 
102     writeEndElement();
103 }
104 
writeContactGroupReference(const ContactGroup::ContactGroupReference & reference)105 void XmlContactGroupWriter::writeContactGroupReference(const ContactGroup::ContactGroupReference &reference)
106 {
107     writeStartElement(QStringLiteral("contactGroupReference"));
108     writeAttribute(QStringLiteral("uid"), reference.uid());
109 
110     // TODO: customs
111 
112     writeEndElement();
113 }
114 
writeData(const ContactGroup::Data & data)115 void XmlContactGroupWriter::writeData(const ContactGroup::Data &data)
116 {
117     writeStartElement(QStringLiteral("contactData"));
118     writeAttribute(QStringLiteral("name"), data.name());
119     writeAttribute(QStringLiteral("email"), data.email());
120 
121     // TODO: customs
122 
123     writeEndElement();
124 }
125 
126 class XmlContactGroupReader : public QXmlStreamReader
127 {
128 public:
129     XmlContactGroupReader();
130 
131     bool read(QIODevice *device, ContactGroup &group);
132     bool read(QIODevice *device, QVector<ContactGroup> &groupList);
133 
134 private:
135     bool readGroup(ContactGroup &group);
136     bool readContactReference(ContactGroup::ContactReference &reference);
137     bool readContactGroupReference(ContactGroup::ContactGroupReference &reference);
138     bool readData(ContactGroup::Data &data);
139 };
140 
XmlContactGroupReader()141 XmlContactGroupReader::XmlContactGroupReader()
142 {
143 }
144 
read(QIODevice * device,ContactGroup & group)145 bool XmlContactGroupReader::read(QIODevice *device, ContactGroup &group)
146 {
147     setDevice(device);
148 
149     while (!atEnd()) {
150         readNext();
151         if (isStartElement()) {
152             if (name() == QLatin1String("contactGroup")) {
153                 return readGroup(group);
154             } else {
155                 raiseError(QStringLiteral("The document does not describe a ContactGroup"));
156             }
157         }
158     }
159 
160     return error() == NoError;
161 }
162 
read(QIODevice * device,QVector<ContactGroup> & groupList)163 bool XmlContactGroupReader::read(QIODevice *device, QVector<ContactGroup> &groupList)
164 {
165     setDevice(device);
166 
167     int depth = 0;
168 
169     while (!atEnd()) {
170         readNext();
171         if (isStartElement()) {
172             ++depth;
173             if (depth == 1) {
174                 if (name() == QLatin1String("contactGroupList")) {
175                     continue;
176                 } else {
177                     raiseError(QStringLiteral("The document does not describe a list of ContactGroup"));
178                 }
179             } else if (depth == 2) {
180                 if (name() == QLatin1String("contactGroup")) {
181                     ContactGroup group;
182                     if (!readGroup(group)) {
183                         return false;
184                     }
185 
186                     groupList.append(group);
187                 } else {
188                     raiseError(QStringLiteral("The document does not describe a list of ContactGroup"));
189                 }
190             }
191         }
192 
193         if (isEndElement()) {
194             --depth;
195         }
196     }
197 
198     return error() == NoError;
199 }
200 
readGroup(ContactGroup & group)201 bool XmlContactGroupReader::readGroup(ContactGroup &group)
202 {
203     const QXmlStreamAttributes elementAttributes = attributes();
204     const auto uid = elementAttributes.value(QLatin1String("uid"));
205     if (uid.isEmpty()) {
206         raiseError(QStringLiteral("ContactGroup is missing a uid"));
207         return false;
208     }
209 
210     const auto groupName = elementAttributes.value(QLatin1String("name"));
211     if (groupName.isEmpty()) {
212         raiseError(QStringLiteral("ContactGroup is missing a name"));
213         return false;
214     }
215 
216     group.setId(uid.toString());
217     group.setName(groupName.toString());
218 
219     while (!atEnd()) {
220         readNext();
221         if (isStartElement()) {
222             if (name() == QLatin1String("contactData")) {
223                 ContactGroup::Data data;
224                 if (!readData(data)) {
225                     return false;
226                 }
227                 group.append(data);
228             } else if (name() == QLatin1String("contactReference")) {
229                 ContactGroup::ContactReference reference;
230                 if (!readContactReference(reference)) {
231                     return false;
232                 }
233                 group.append(reference);
234             } else if (name() == QLatin1String("contactGroupReference")) {
235                 ContactGroup::ContactGroupReference reference;
236                 if (!readContactGroupReference(reference)) {
237                     return false;
238                 }
239                 group.append(reference);
240             } else {
241                 raiseError(QStringLiteral("The document does not describe a ContactGroup"));
242             }
243         }
244 
245         if (isEndElement()) {
246             if (name() == QLatin1String("contactGroup")) {
247                 return true;
248             }
249         }
250     }
251 
252     return false;
253 }
254 
readData(ContactGroup::Data & data)255 bool XmlContactGroupReader::readData(ContactGroup::Data &data)
256 {
257     const QXmlStreamAttributes elementAttributes = attributes();
258     const auto email = elementAttributes.value(QLatin1String("email"));
259     if (email.isEmpty()) {
260         raiseError(QStringLiteral("ContactData is missing an email address"));
261         return false;
262     }
263 
264     const auto name = elementAttributes.value(QLatin1String("name"));
265 
266     data.setName(name.toString());
267     data.setEmail(email.toString());
268 
269     return true;
270 }
271 
readContactReference(ContactGroup::ContactReference & reference)272 bool XmlContactGroupReader::readContactReference(ContactGroup::ContactReference &reference)
273 {
274     const QXmlStreamAttributes elementAttributes = attributes();
275     const auto uid = elementAttributes.value(QLatin1String("uid"));
276     const auto gid = elementAttributes.value(QLatin1String("gid"));
277     if (uid.isEmpty() && gid.isEmpty()) {
278         raiseError(QStringLiteral("ContactReference is missing both uid and gid"));
279         return false;
280     }
281     const auto preferredEmail = elementAttributes.value(QLatin1String("preferredEmail"));
282 
283     reference.setUid(uid.toString());
284     reference.setGid(gid.toString());
285     reference.setPreferredEmail(preferredEmail.toString());
286 
287     return true;
288 }
289 
readContactGroupReference(ContactGroup::ContactGroupReference & reference)290 bool XmlContactGroupReader::readContactGroupReference(ContactGroup::ContactGroupReference &reference)
291 {
292     const QXmlStreamAttributes elementAttributes = attributes();
293     const auto uid = elementAttributes.value(QLatin1String("uid"));
294     if (uid.isEmpty()) {
295         raiseError(QStringLiteral("ContactGroupReference is missing a uid"));
296         return false;
297     }
298 
299     reference.setUid(uid.toString());
300 
301     return true;
302 }
303 
convertFromXml(QIODevice * device,ContactGroup & group,QString * errorMessage)304 bool ContactGroupTool::convertFromXml(QIODevice *device, ContactGroup &group, QString *errorMessage)
305 {
306     Q_UNUSED(errorMessage);
307 
308     XmlContactGroupReader reader;
309 
310     bool ok = reader.read(device, group);
311 
312     if (!ok && errorMessage != nullptr) {
313         *errorMessage = reader.errorString();
314     }
315 
316     return ok;
317 }
318 
convertToXml(const ContactGroup & group,QIODevice * device,QString * errorMessage)319 bool ContactGroupTool::convertToXml(const ContactGroup &group, QIODevice *device, QString *errorMessage)
320 {
321     Q_UNUSED(errorMessage);
322 
323     XmlContactGroupWriter writer;
324     writer.write(group, device);
325 
326     return true;
327 }
328 
convertFromXml(QIODevice * device,QVector<ContactGroup> & groupList,QString * errorMessage)329 bool ContactGroupTool::convertFromXml(QIODevice *device, QVector<ContactGroup> &groupList, QString *errorMessage)
330 {
331     Q_UNUSED(errorMessage);
332 
333     XmlContactGroupReader reader;
334 
335     bool ok = reader.read(device, groupList);
336 
337     if (!ok && errorMessage != nullptr) {
338         *errorMessage = reader.errorString();
339     }
340 
341     return ok;
342 }
343 
convertToXml(const QVector<ContactGroup> & groupList,QIODevice * device,QString * errorMessage)344 bool ContactGroupTool::convertToXml(const QVector<ContactGroup> &groupList, QIODevice *device, QString *errorMessage)
345 {
346     Q_UNUSED(errorMessage);
347 
348     XmlContactGroupWriter writer;
349     writer.write(groupList, device);
350 
351     return true;
352 }
353