KDAV

caldavprotocol.cpp
1/*
2 SPDX-FileCopyrightText: 2009 Grégory Oestreicher <greg@kamago.net>
3
4 SPDX-License-Identifier: LGPL-2.0-or-later
5*/
6
7#include "caldavprotocol_p.h"
8#include "common/utils_p.h"
9#include "libkdav_debug.h"
10
11#include <QDomDocument>
12#include <QStringList>
13#include <QUrl>
14
15using namespace KDAV;
16
17class CaldavCollectionQueryBuilder : public XMLQueryBuilder
18{
19public:
20 QDomDocument buildQuery() const override
21 {
22 QDomDocument document;
23
24 QDomElement propfindElement = document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("propfind"));
25 document.appendChild(propfindElement);
26
27 QDomElement propElement = document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("prop"));
28 propfindElement.appendChild(propElement);
29
30 propElement.appendChild(document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("displayname")));
31 propElement.appendChild(document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("resourcetype")));
32 propElement.appendChild(document.createElementNS(QStringLiteral("http://apple.com/ns/ical/"), QStringLiteral("calendar-color")));
33 propElement.appendChild(document.createElementNS(QStringLiteral("urn:ietf:params:xml:ns:caldav"), QStringLiteral("supported-calendar-component-set")));
34 propElement.appendChild(document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("current-user-privilege-set")));
35 propElement.appendChild(document.createElementNS(QStringLiteral("http://calendarserver.org/ns/"), QStringLiteral("getctag")));
36
37 return document;
38 }
39
40 QString mimeType() const override
41 {
42 return QString();
43 }
44};
45
46class CaldavListEventQueryBuilder : public XMLQueryBuilder
47{
48public:
49 QDomDocument buildQuery() const override
50 {
51 QString startTime = parameter(QStringLiteral("start")).toString();
52 QString endTime = parameter(QStringLiteral("end")).toString();
53 QDomDocument document;
54
55 QDomElement queryElement = document.createElementNS(QStringLiteral("urn:ietf:params:xml:ns:caldav"), QStringLiteral("calendar-query"));
56 document.appendChild(queryElement);
57
58 QDomElement propElement = document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("prop"));
59 queryElement.appendChild(propElement);
60
61 QDomElement getetagElement = document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("getetag"));
62 propElement.appendChild(getetagElement);
63
64 QDomElement getRTypeElement = document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("resourcetype"));
65 propElement.appendChild(getRTypeElement);
66
67 QDomElement filterElement = document.createElementNS(QStringLiteral("urn:ietf:params:xml:ns:caldav"), QStringLiteral("filter"));
68 queryElement.appendChild(filterElement);
69
70 QDomElement compfilterElement = document.createElementNS(QStringLiteral("urn:ietf:params:xml:ns:caldav"), QStringLiteral("comp-filter"));
71
72 QDomAttr nameAttribute = document.createAttribute(QStringLiteral("name"));
73 nameAttribute.setValue(QStringLiteral("VCALENDAR"));
74 compfilterElement.setAttributeNode(nameAttribute);
75 filterElement.appendChild(compfilterElement);
76
77 QDomElement subcompfilterElement = document.createElementNS(QStringLiteral("urn:ietf:params:xml:ns:caldav"), QStringLiteral("comp-filter"));
78 nameAttribute = document.createAttribute(QStringLiteral("name"));
79 nameAttribute.setValue(QStringLiteral("VEVENT"));
80 subcompfilterElement.setAttributeNode(nameAttribute);
81
82 if (!startTime.isEmpty() || !endTime.isEmpty()) {
83 QDomElement timeRangeElement = document.createElementNS(QStringLiteral("urn:ietf:params:xml:ns:caldav"), QStringLiteral("time-range"));
84
85 if (!startTime.isEmpty()) {
86 QDomAttr startAttribute = document.createAttribute(QStringLiteral("start"));
87 startAttribute.setValue(startTime);
88 timeRangeElement.setAttributeNode(startAttribute);
89 }
90
91 if (!endTime.isEmpty()) {
92 QDomAttr endAttribute = document.createAttribute(QStringLiteral("end"));
93 endAttribute.setValue(endTime);
94 timeRangeElement.setAttributeNode(endAttribute);
95 }
96
97 subcompfilterElement.appendChild(timeRangeElement);
98 }
99
100 compfilterElement.appendChild(subcompfilterElement);
101
102 return document;
103 }
104
105 QString mimeType() const override
106 {
107 return QStringLiteral("application/x-vnd.akonadi.calendar.event");
108 }
109};
110
111class CaldavListTodoQueryBuilder : public XMLQueryBuilder
112{
113public:
114 QDomDocument buildQuery() const override
115 {
116 QString startTime = parameter(QStringLiteral("start")).toString();
117 QString endTime = parameter(QStringLiteral("end")).toString();
118 QDomDocument document;
119
120 QDomElement queryElement = document.createElementNS(QStringLiteral("urn:ietf:params:xml:ns:caldav"), QStringLiteral("calendar-query"));
121 document.appendChild(queryElement);
122
123 QDomElement propElement = document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("prop"));
124 queryElement.appendChild(propElement);
125
126 QDomElement getetagElement = document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("getetag"));
127 propElement.appendChild(getetagElement);
128
129 QDomElement getRTypeElement = document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("resourcetype"));
130 propElement.appendChild(getRTypeElement);
131
132 QDomElement filterElement = document.createElementNS(QStringLiteral("urn:ietf:params:xml:ns:caldav"), QStringLiteral("filter"));
133 queryElement.appendChild(filterElement);
134
135 QDomElement compfilterElement = document.createElementNS(QStringLiteral("urn:ietf:params:xml:ns:caldav"), QStringLiteral("comp-filter"));
136
137 QDomAttr nameAttribute = document.createAttribute(QStringLiteral("name"));
138 nameAttribute.setValue(QStringLiteral("VCALENDAR"));
139 compfilterElement.setAttributeNode(nameAttribute);
140 filterElement.appendChild(compfilterElement);
141
142 QDomElement subcompfilterElement = document.createElementNS(QStringLiteral("urn:ietf:params:xml:ns:caldav"), QStringLiteral("comp-filter"));
143 nameAttribute = document.createAttribute(QStringLiteral("name"));
144 nameAttribute.setValue(QStringLiteral("VTODO"));
145 subcompfilterElement.setAttributeNode(nameAttribute);
146
147 if (!startTime.isEmpty() || !endTime.isEmpty()) {
148 QDomElement timeRangeElement = document.createElementNS(QStringLiteral("urn:ietf:params:xml:ns:caldav"), QStringLiteral("time-range"));
149
150 if (!startTime.isEmpty()) {
151 QDomAttr startAttribute = document.createAttribute(QStringLiteral("start"));
152 startAttribute.setValue(startTime);
153 timeRangeElement.setAttributeNode(startAttribute);
154 }
155
156 if (!endTime.isEmpty()) {
157 QDomAttr endAttribute = document.createAttribute(QStringLiteral("end"));
158 endAttribute.setValue(endTime);
159 timeRangeElement.setAttributeNode(endAttribute);
160 }
161
162 subcompfilterElement.appendChild(timeRangeElement);
163 }
164
165 compfilterElement.appendChild(subcompfilterElement);
166
167 return document;
168 }
169
170 QString mimeType() const override
171 {
172 return QStringLiteral("application/x-vnd.akonadi.calendar.todo");
173 }
174};
175
176class CaldavListJournalQueryBuilder : public XMLQueryBuilder
177{
178public:
179 QDomDocument buildQuery() const override
180 {
181 QString startTime = parameter(QStringLiteral("start")).toString();
182 QString endTime = parameter(QStringLiteral("end")).toString();
183 QDomDocument document;
184
185 QDomElement queryElement = document.createElementNS(QStringLiteral("urn:ietf:params:xml:ns:caldav"), QStringLiteral("calendar-query"));
186 document.appendChild(queryElement);
187
188 QDomElement propElement = document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("prop"));
189 queryElement.appendChild(propElement);
190
191 QDomElement getetagElement = document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("getetag"));
192 propElement.appendChild(getetagElement);
193
194 QDomElement getRTypeElement = document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("resourcetype"));
195 propElement.appendChild(getRTypeElement);
196
197 QDomElement filterElement = document.createElementNS(QStringLiteral("urn:ietf:params:xml:ns:caldav"), QStringLiteral("filter"));
198 queryElement.appendChild(filterElement);
199
200 QDomElement compfilterElement = document.createElementNS(QStringLiteral("urn:ietf:params:xml:ns:caldav"), QStringLiteral("comp-filter"));
201
202 QDomAttr nameAttribute = document.createAttribute(QStringLiteral("name"));
203 nameAttribute.setValue(QStringLiteral("VCALENDAR"));
204 compfilterElement.setAttributeNode(nameAttribute);
205 filterElement.appendChild(compfilterElement);
206
207 QDomElement subcompfilterElement = document.createElementNS(QStringLiteral("urn:ietf:params:xml:ns:caldav"), QStringLiteral("comp-filter"));
208 nameAttribute = document.createAttribute(QStringLiteral("name"));
209 nameAttribute.setValue(QStringLiteral("VJOURNAL"));
210 subcompfilterElement.setAttributeNode(nameAttribute);
211
212 if (!startTime.isEmpty() || !endTime.isEmpty()) {
213 QDomElement timeRangeElement = document.createElementNS(QStringLiteral("urn:ietf:params:xml:ns:caldav"), QStringLiteral("time-range"));
214
215 if (!startTime.isEmpty()) {
216 QDomAttr startAttribute = document.createAttribute(QStringLiteral("start"));
217 startAttribute.setValue(startTime);
218 timeRangeElement.setAttributeNode(startAttribute);
219 }
220
221 if (!endTime.isEmpty()) {
222 QDomAttr endAttribute = document.createAttribute(QStringLiteral("end"));
223 endAttribute.setValue(endTime);
224 timeRangeElement.setAttributeNode(endAttribute);
225 }
226
227 subcompfilterElement.appendChild(timeRangeElement);
228 }
229
230 compfilterElement.appendChild(subcompfilterElement);
231
232 return document;
233 }
234
235 QString mimeType() const override
236 {
237 return QStringLiteral("application/x-vnd.akonadi.calendar.journal");
238 }
239};
240
241class CaldavMultigetQueryBuilder : public XMLQueryBuilder
242{
243public:
244 QDomDocument buildQuery() const override
245 {
246 QDomDocument document;
247 const QStringList urls = parameter(QStringLiteral("urls")).toStringList();
248 if (urls.isEmpty()) {
249 return document;
250 }
251
252 QDomElement multigetElement = document.createElementNS(QStringLiteral("urn:ietf:params:xml:ns:caldav"), QStringLiteral("calendar-multiget"));
253 document.appendChild(multigetElement);
254
255 QDomElement propElement = document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("prop"));
256 multigetElement.appendChild(propElement);
257
258 propElement.appendChild(document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("getetag")));
259 propElement.appendChild(document.createElementNS(QStringLiteral("urn:ietf:params:xml:ns:caldav"), QStringLiteral("calendar-data")));
260
261 for (const QString &url : urls) {
262 QDomElement hrefElement = document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("href"));
263 const QUrl pathUrl = QUrl::fromUserInput(url);
264 qCDebug(KDAV_LOG) << pathUrl.toString() << "->" << pathUrl.path();
265 const QDomText textNode = document.createTextNode(pathUrl.path());
266 hrefElement.appendChild(textNode);
267
268 multigetElement.appendChild(hrefElement);
269 }
270
271 return document;
272 }
273
274 QString mimeType() const override
275 {
276 return QString();
277 }
278};
279
280CaldavProtocol::CaldavProtocol()
281{
282}
283
284bool CaldavProtocol::supportsPrincipals() const
285{
286 return true;
287}
288
289bool CaldavProtocol::useReport() const
290{
291 return true;
292}
293
294bool CaldavProtocol::useMultiget() const
295{
296 return true;
297}
298
299QString CaldavProtocol::principalHomeSet() const
300{
301 return QStringLiteral("calendar-home-set");
302}
303
304QString CaldavProtocol::principalHomeSetNS() const
305{
306 return QStringLiteral("urn:ietf:params:xml:ns:caldav");
307}
308
309XMLQueryBuilder::Ptr CaldavProtocol::collectionsQuery() const
310{
311 return XMLQueryBuilder::Ptr(new CaldavCollectionQueryBuilder());
312}
313
314bool CaldavProtocol::containsCollection(const QDomElement &propElem) const
315{
316 return !propElem.elementsByTagNameNS(QStringLiteral("urn:ietf:params:xml:ns:caldav"), QStringLiteral("calendar")).isEmpty();
317}
318
319QList<XMLQueryBuilder::Ptr> CaldavProtocol::itemsQueries() const
320{
322 ret << XMLQueryBuilder::Ptr(new CaldavListEventQueryBuilder());
323 ret << XMLQueryBuilder::Ptr(new CaldavListTodoQueryBuilder());
324 ret << XMLQueryBuilder::Ptr(new CaldavListJournalQueryBuilder());
325 return ret;
326}
327
328XMLQueryBuilder::Ptr CaldavProtocol::itemsReportQuery(const QStringList &urls) const
329{
330 XMLQueryBuilder::Ptr ret(new CaldavMultigetQueryBuilder());
331 ret->setParameter(QStringLiteral("urls"), urls);
332 return ret;
333}
334
335QString CaldavProtocol::responseNamespace() const
336{
337 return QStringLiteral("urn:ietf:params:xml:ns:caldav");
338}
339
340QString CaldavProtocol::dataTagName() const
341{
342 return QStringLiteral("calendar-data");
343}
344
345DavCollection::ContentTypes CaldavProtocol::collectionContentTypes(const QDomElement &propstatElement) const
346{
347 /*
348 * Extract the content type information from a propstat like the following
349 * <propstat xmlns="DAV:">
350 * <prop xmlns="DAV:">
351 * <C:supported-calendar-component-set xmlns:C="urn:ietf:params:xml:ns:caldav">
352 * <C:comp xmlns:C="urn:ietf:params:xml:ns:caldav" name="VEVENT"/>
353 * <C:comp xmlns:C="urn:ietf:params:xml:ns:caldav" name="VTODO"/>
354 * <C:comp xmlns:C="urn:ietf:params:xml:ns:caldav" name="VJOURNAL"/>
355 * <C:comp xmlns:C="urn:ietf:params:xml:ns:caldav" name="VTIMEZONE"/>
356 * <C:comp xmlns:C="urn:ietf:params:xml:ns:caldav" name="VFREEBUSY"/>
357 * </C:supported-calendar-component-set>
358 * <resourcetype xmlns="DAV:">
359 * <collection xmlns="DAV:"/>
360 * <C:calendar xmlns:C="urn:ietf:params:xml:ns:caldav"/>
361 * <C:schedule-calendar xmlns:C="urn:ietf:params:xml:ns:caldav"/>
362 * </resourcetype>
363 * <displayname xmlns="DAV:">Test1 User</displayname>
364 * </prop>
365 * <status xmlns="DAV:">HTTP/1.1 200 OK</status>
366 * </propstat>
367 */
368
369 const QDomElement propElement = Utils::firstChildElementNS(propstatElement, QStringLiteral("DAV:"), QStringLiteral("prop"));
370 const QDomElement supportedcomponentElement = Utils::firstChildElementNS(propElement, //
371 QStringLiteral("urn:ietf:params:xml:ns:caldav"),
372 QStringLiteral("supported-calendar-component-set"));
373
374 DavCollection::ContentTypes contentTypes;
375 QDomElement compElement = Utils::firstChildElementNS(supportedcomponentElement, QStringLiteral("urn:ietf:params:xml:ns:caldav"), QStringLiteral("comp"));
376
377 /*
378 * Assign the content-type if the server didn't return anything.
379 * According to RFC4791, §5.2.3:
380 * In the absence of this property, the server MUST accept all
381 * component types, and the client can assume that all component
382 * types are accepted.
383 */
384 if (compElement.isNull()) {
385 contentTypes |= DavCollection::Calendar;
386 contentTypes |= DavCollection::Events;
387 contentTypes |= DavCollection::Todos;
388 contentTypes |= DavCollection::FreeBusy;
389 contentTypes |= DavCollection::Journal;
390 }
391
392 while (!compElement.isNull()) {
393 const QString type = compElement.attribute(QStringLiteral("name")).toLower();
394 if (type == QLatin1String("vcalendar")) {
395 contentTypes |= DavCollection::Calendar;
396 } else if (type == QLatin1String("vevent")) {
397 contentTypes |= DavCollection::Events;
398 } else if (type == QLatin1String("vtodo")) {
399 contentTypes |= DavCollection::Todos;
400 } else if (type == QLatin1String("vfreebusy")) {
401 contentTypes |= DavCollection::FreeBusy;
402 } else if (type == QLatin1String("vjournal")) {
403 contentTypes |= DavCollection::Journal;
404 }
405
406 compElement = Utils::nextSiblingElementNS(compElement, QStringLiteral("urn:ietf:params:xml:ns:caldav"), QStringLiteral("comp"));
407 }
408
409 return contentTypes;
410}
@ FreeBusy
The collection can contain free/busy information.
@ Journal
The collection can contain journal DAV resources.
@ Todos
The collection can contain todo DAV resources.
@ Calendar
The collection can contain anything calendar-related.
@ Events
The collection can contain event DAV resources.
The KDAV namespace.
VehicleSection::Type type(QStringView coachNumber, QStringView coachClassification)
void setValue(const QString &v)
QDomAttr createAttribute(const QString &name)
QDomElement createElementNS(const QString &nsURI, const QString &qName)
QDomText createTextNode(const QString &value)
QString attribute(const QString &name, const QString &defValue) const const
QDomNodeList elementsByTagNameNS(const QString &nsURI, const QString &localName) const const
QDomAttr setAttributeNode(const QDomAttr &newAttr)
QDomNode appendChild(const QDomNode &newChild)
bool isNull() const const
bool isEmpty() const const
bool isEmpty() const const
bool isEmpty() const const
QString toLower() const const
QUrl fromUserInput(const QString &userInput, const QString &workingDirectory, UserInputResolutionOptions options)
QString path(ComponentFormattingOptions options) const const
QString toString(FormattingOptions options) const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2025 The KDE developers.
Generated on Fri Feb 28 2025 11:51:48 by doxygen 1.13.2 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.