File indexing completed on 2024-05-05 03:54:25
0001 /* 0002 SPDX-FileCopyrightText: 2009 Grégory Oestreicher <greg@kamago.net> 0003 0004 SPDX-License-Identifier: LGPL-2.0-or-later 0005 */ 0006 0007 #include "caldavprotocol_p.h" 0008 #include "common/utils_p.h" 0009 0010 #include <QDomDocument> 0011 #include <QStringList> 0012 #include <QUrl> 0013 0014 using namespace KDAV; 0015 0016 class CaldavCollectionQueryBuilder : public XMLQueryBuilder 0017 { 0018 public: 0019 QDomDocument buildQuery() const override 0020 { 0021 QDomDocument document; 0022 0023 QDomElement propfindElement = document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("propfind")); 0024 document.appendChild(propfindElement); 0025 0026 QDomElement propElement = document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("prop")); 0027 propfindElement.appendChild(propElement); 0028 0029 propElement.appendChild(document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("displayname"))); 0030 propElement.appendChild(document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("resourcetype"))); 0031 propElement.appendChild(document.createElementNS(QStringLiteral("http://apple.com/ns/ical/"), QStringLiteral("calendar-color"))); 0032 propElement.appendChild(document.createElementNS(QStringLiteral("urn:ietf:params:xml:ns:caldav"), QStringLiteral("supported-calendar-component-set"))); 0033 propElement.appendChild(document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("current-user-privilege-set"))); 0034 propElement.appendChild(document.createElementNS(QStringLiteral("http://calendarserver.org/ns/"), QStringLiteral("getctag"))); 0035 0036 return document; 0037 } 0038 0039 QString mimeType() const override 0040 { 0041 return QString(); 0042 } 0043 }; 0044 0045 class CaldavListEventQueryBuilder : public XMLQueryBuilder 0046 { 0047 public: 0048 QDomDocument buildQuery() const override 0049 { 0050 QString startTime = parameter(QStringLiteral("start")).toString(); 0051 QString endTime = parameter(QStringLiteral("end")).toString(); 0052 QDomDocument document; 0053 0054 QDomElement queryElement = document.createElementNS(QStringLiteral("urn:ietf:params:xml:ns:caldav"), QStringLiteral("calendar-query")); 0055 document.appendChild(queryElement); 0056 0057 QDomElement propElement = document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("prop")); 0058 queryElement.appendChild(propElement); 0059 0060 QDomElement getetagElement = document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("getetag")); 0061 propElement.appendChild(getetagElement); 0062 0063 QDomElement getRTypeElement = document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("resourcetype")); 0064 propElement.appendChild(getRTypeElement); 0065 0066 QDomElement filterElement = document.createElementNS(QStringLiteral("urn:ietf:params:xml:ns:caldav"), QStringLiteral("filter")); 0067 queryElement.appendChild(filterElement); 0068 0069 QDomElement compfilterElement = document.createElementNS(QStringLiteral("urn:ietf:params:xml:ns:caldav"), QStringLiteral("comp-filter")); 0070 0071 QDomAttr nameAttribute = document.createAttribute(QStringLiteral("name")); 0072 nameAttribute.setValue(QStringLiteral("VCALENDAR")); 0073 compfilterElement.setAttributeNode(nameAttribute); 0074 filterElement.appendChild(compfilterElement); 0075 0076 QDomElement subcompfilterElement = document.createElementNS(QStringLiteral("urn:ietf:params:xml:ns:caldav"), QStringLiteral("comp-filter")); 0077 nameAttribute = document.createAttribute(QStringLiteral("name")); 0078 nameAttribute.setValue(QStringLiteral("VEVENT")); 0079 subcompfilterElement.setAttributeNode(nameAttribute); 0080 0081 if (!startTime.isEmpty() || !endTime.isEmpty()) { 0082 QDomElement timeRangeElement = document.createElementNS(QStringLiteral("urn:ietf:params:xml:ns:caldav"), QStringLiteral("time-range")); 0083 0084 if (!startTime.isEmpty()) { 0085 QDomAttr startAttribute = document.createAttribute(QStringLiteral("start")); 0086 startAttribute.setValue(startTime); 0087 timeRangeElement.setAttributeNode(startAttribute); 0088 } 0089 0090 if (!endTime.isEmpty()) { 0091 QDomAttr endAttribute = document.createAttribute(QStringLiteral("end")); 0092 endAttribute.setValue(endTime); 0093 timeRangeElement.setAttributeNode(endAttribute); 0094 } 0095 0096 subcompfilterElement.appendChild(timeRangeElement); 0097 } 0098 0099 compfilterElement.appendChild(subcompfilterElement); 0100 0101 return document; 0102 } 0103 0104 QString mimeType() const override 0105 { 0106 return QStringLiteral("application/x-vnd.akonadi.calendar.event"); 0107 } 0108 }; 0109 0110 class CaldavListTodoQueryBuilder : public XMLQueryBuilder 0111 { 0112 public: 0113 QDomDocument buildQuery() const override 0114 { 0115 QString startTime = parameter(QStringLiteral("start")).toString(); 0116 QString endTime = parameter(QStringLiteral("end")).toString(); 0117 QDomDocument document; 0118 0119 QDomElement queryElement = document.createElementNS(QStringLiteral("urn:ietf:params:xml:ns:caldav"), QStringLiteral("calendar-query")); 0120 document.appendChild(queryElement); 0121 0122 QDomElement propElement = document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("prop")); 0123 queryElement.appendChild(propElement); 0124 0125 QDomElement getetagElement = document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("getetag")); 0126 propElement.appendChild(getetagElement); 0127 0128 QDomElement getRTypeElement = document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("resourcetype")); 0129 propElement.appendChild(getRTypeElement); 0130 0131 QDomElement filterElement = document.createElementNS(QStringLiteral("urn:ietf:params:xml:ns:caldav"), QStringLiteral("filter")); 0132 queryElement.appendChild(filterElement); 0133 0134 QDomElement compfilterElement = document.createElementNS(QStringLiteral("urn:ietf:params:xml:ns:caldav"), QStringLiteral("comp-filter")); 0135 0136 QDomAttr nameAttribute = document.createAttribute(QStringLiteral("name")); 0137 nameAttribute.setValue(QStringLiteral("VCALENDAR")); 0138 compfilterElement.setAttributeNode(nameAttribute); 0139 filterElement.appendChild(compfilterElement); 0140 0141 QDomElement subcompfilterElement = document.createElementNS(QStringLiteral("urn:ietf:params:xml:ns:caldav"), QStringLiteral("comp-filter")); 0142 nameAttribute = document.createAttribute(QStringLiteral("name")); 0143 nameAttribute.setValue(QStringLiteral("VTODO")); 0144 subcompfilterElement.setAttributeNode(nameAttribute); 0145 0146 if (!startTime.isEmpty() || !endTime.isEmpty()) { 0147 QDomElement timeRangeElement = document.createElementNS(QStringLiteral("urn:ietf:params:xml:ns:caldav"), QStringLiteral("time-range")); 0148 0149 if (!startTime.isEmpty()) { 0150 QDomAttr startAttribute = document.createAttribute(QStringLiteral("start")); 0151 startAttribute.setValue(startTime); 0152 timeRangeElement.setAttributeNode(startAttribute); 0153 } 0154 0155 if (!endTime.isEmpty()) { 0156 QDomAttr endAttribute = document.createAttribute(QStringLiteral("end")); 0157 endAttribute.setValue(endTime); 0158 timeRangeElement.setAttributeNode(endAttribute); 0159 } 0160 0161 subcompfilterElement.appendChild(timeRangeElement); 0162 } 0163 0164 compfilterElement.appendChild(subcompfilterElement); 0165 0166 return document; 0167 } 0168 0169 QString mimeType() const override 0170 { 0171 return QStringLiteral("application/x-vnd.akonadi.calendar.todo"); 0172 } 0173 }; 0174 0175 class CaldavListJournalQueryBuilder : public XMLQueryBuilder 0176 { 0177 public: 0178 QDomDocument buildQuery() const override 0179 { 0180 QString startTime = parameter(QStringLiteral("start")).toString(); 0181 QString endTime = parameter(QStringLiteral("end")).toString(); 0182 QDomDocument document; 0183 0184 QDomElement queryElement = document.createElementNS(QStringLiteral("urn:ietf:params:xml:ns:caldav"), QStringLiteral("calendar-query")); 0185 document.appendChild(queryElement); 0186 0187 QDomElement propElement = document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("prop")); 0188 queryElement.appendChild(propElement); 0189 0190 QDomElement getetagElement = document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("getetag")); 0191 propElement.appendChild(getetagElement); 0192 0193 QDomElement getRTypeElement = document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("resourcetype")); 0194 propElement.appendChild(getRTypeElement); 0195 0196 QDomElement filterElement = document.createElementNS(QStringLiteral("urn:ietf:params:xml:ns:caldav"), QStringLiteral("filter")); 0197 queryElement.appendChild(filterElement); 0198 0199 QDomElement compfilterElement = document.createElementNS(QStringLiteral("urn:ietf:params:xml:ns:caldav"), QStringLiteral("comp-filter")); 0200 0201 QDomAttr nameAttribute = document.createAttribute(QStringLiteral("name")); 0202 nameAttribute.setValue(QStringLiteral("VCALENDAR")); 0203 compfilterElement.setAttributeNode(nameAttribute); 0204 filterElement.appendChild(compfilterElement); 0205 0206 QDomElement subcompfilterElement = document.createElementNS(QStringLiteral("urn:ietf:params:xml:ns:caldav"), QStringLiteral("comp-filter")); 0207 nameAttribute = document.createAttribute(QStringLiteral("name")); 0208 nameAttribute.setValue(QStringLiteral("VJOURNAL")); 0209 subcompfilterElement.setAttributeNode(nameAttribute); 0210 0211 if (!startTime.isEmpty() || !endTime.isEmpty()) { 0212 QDomElement timeRangeElement = document.createElementNS(QStringLiteral("urn:ietf:params:xml:ns:caldav"), QStringLiteral("time-range")); 0213 0214 if (!startTime.isEmpty()) { 0215 QDomAttr startAttribute = document.createAttribute(QStringLiteral("start")); 0216 startAttribute.setValue(startTime); 0217 timeRangeElement.setAttributeNode(startAttribute); 0218 } 0219 0220 if (!endTime.isEmpty()) { 0221 QDomAttr endAttribute = document.createAttribute(QStringLiteral("end")); 0222 endAttribute.setValue(endTime); 0223 timeRangeElement.setAttributeNode(endAttribute); 0224 } 0225 0226 subcompfilterElement.appendChild(timeRangeElement); 0227 } 0228 0229 compfilterElement.appendChild(subcompfilterElement); 0230 0231 return document; 0232 } 0233 0234 QString mimeType() const override 0235 { 0236 return QStringLiteral("application/x-vnd.akonadi.calendar.journal"); 0237 } 0238 }; 0239 0240 class CaldavMultigetQueryBuilder : public XMLQueryBuilder 0241 { 0242 public: 0243 QDomDocument buildQuery() const override 0244 { 0245 QDomDocument document; 0246 const QStringList urls = parameter(QStringLiteral("urls")).toStringList(); 0247 if (urls.isEmpty()) { 0248 return document; 0249 } 0250 0251 QDomElement multigetElement = document.createElementNS(QStringLiteral("urn:ietf:params:xml:ns:caldav"), QStringLiteral("calendar-multiget")); 0252 document.appendChild(multigetElement); 0253 0254 QDomElement propElement = document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("prop")); 0255 multigetElement.appendChild(propElement); 0256 0257 propElement.appendChild(document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("getetag"))); 0258 propElement.appendChild(document.createElementNS(QStringLiteral("urn:ietf:params:xml:ns:caldav"), QStringLiteral("calendar-data"))); 0259 0260 for (const QString &url : urls) { 0261 QDomElement hrefElement = document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("href")); 0262 const QUrl pathUrl = QUrl::fromUserInput(url); 0263 const QDomText textNode = document.createTextNode(pathUrl.path()); 0264 hrefElement.appendChild(textNode); 0265 0266 multigetElement.appendChild(hrefElement); 0267 } 0268 0269 return document; 0270 } 0271 0272 QString mimeType() const override 0273 { 0274 return QString(); 0275 } 0276 }; 0277 0278 CaldavProtocol::CaldavProtocol() 0279 { 0280 } 0281 0282 bool CaldavProtocol::supportsPrincipals() const 0283 { 0284 return true; 0285 } 0286 0287 bool CaldavProtocol::useReport() const 0288 { 0289 return true; 0290 } 0291 0292 bool CaldavProtocol::useMultiget() const 0293 { 0294 return true; 0295 } 0296 0297 QString CaldavProtocol::principalHomeSet() const 0298 { 0299 return QStringLiteral("calendar-home-set"); 0300 } 0301 0302 QString CaldavProtocol::principalHomeSetNS() const 0303 { 0304 return QStringLiteral("urn:ietf:params:xml:ns:caldav"); 0305 } 0306 0307 XMLQueryBuilder::Ptr CaldavProtocol::collectionsQuery() const 0308 { 0309 return XMLQueryBuilder::Ptr(new CaldavCollectionQueryBuilder()); 0310 } 0311 0312 bool CaldavProtocol::containsCollection(const QDomElement &propElem) const 0313 { 0314 return !propElem.elementsByTagNameNS(QStringLiteral("urn:ietf:params:xml:ns:caldav"), QStringLiteral("calendar")).isEmpty(); 0315 } 0316 0317 QList<XMLQueryBuilder::Ptr> CaldavProtocol::itemsQueries() const 0318 { 0319 QList<XMLQueryBuilder::Ptr> ret; 0320 ret << XMLQueryBuilder::Ptr(new CaldavListEventQueryBuilder()); 0321 ret << XMLQueryBuilder::Ptr(new CaldavListTodoQueryBuilder()); 0322 ret << XMLQueryBuilder::Ptr(new CaldavListJournalQueryBuilder()); 0323 return ret; 0324 } 0325 0326 XMLQueryBuilder::Ptr CaldavProtocol::itemsReportQuery(const QStringList &urls) const 0327 { 0328 XMLQueryBuilder::Ptr ret(new CaldavMultigetQueryBuilder()); 0329 ret->setParameter(QStringLiteral("urls"), urls); 0330 return ret; 0331 } 0332 0333 QString CaldavProtocol::responseNamespace() const 0334 { 0335 return QStringLiteral("urn:ietf:params:xml:ns:caldav"); 0336 } 0337 0338 QString CaldavProtocol::dataTagName() const 0339 { 0340 return QStringLiteral("calendar-data"); 0341 } 0342 0343 DavCollection::ContentTypes CaldavProtocol::collectionContentTypes(const QDomElement &propstatElement) const 0344 { 0345 /* 0346 * Extract the content type information from a propstat like the following 0347 * <propstat xmlns="DAV:"> 0348 * <prop xmlns="DAV:"> 0349 * <C:supported-calendar-component-set xmlns:C="urn:ietf:params:xml:ns:caldav"> 0350 * <C:comp xmlns:C="urn:ietf:params:xml:ns:caldav" name="VEVENT"/> 0351 * <C:comp xmlns:C="urn:ietf:params:xml:ns:caldav" name="VTODO"/> 0352 * <C:comp xmlns:C="urn:ietf:params:xml:ns:caldav" name="VJOURNAL"/> 0353 * <C:comp xmlns:C="urn:ietf:params:xml:ns:caldav" name="VTIMEZONE"/> 0354 * <C:comp xmlns:C="urn:ietf:params:xml:ns:caldav" name="VFREEBUSY"/> 0355 * </C:supported-calendar-component-set> 0356 * <resourcetype xmlns="DAV:"> 0357 * <collection xmlns="DAV:"/> 0358 * <C:calendar xmlns:C="urn:ietf:params:xml:ns:caldav"/> 0359 * <C:schedule-calendar xmlns:C="urn:ietf:params:xml:ns:caldav"/> 0360 * </resourcetype> 0361 * <displayname xmlns="DAV:">Test1 User</displayname> 0362 * </prop> 0363 * <status xmlns="DAV:">HTTP/1.1 200 OK</status> 0364 * </propstat> 0365 */ 0366 0367 const QDomElement propElement = Utils::firstChildElementNS(propstatElement, QStringLiteral("DAV:"), QStringLiteral("prop")); 0368 const QDomElement supportedcomponentElement = Utils::firstChildElementNS(propElement, // 0369 QStringLiteral("urn:ietf:params:xml:ns:caldav"), 0370 QStringLiteral("supported-calendar-component-set")); 0371 0372 DavCollection::ContentTypes contentTypes; 0373 QDomElement compElement = Utils::firstChildElementNS(supportedcomponentElement, QStringLiteral("urn:ietf:params:xml:ns:caldav"), QStringLiteral("comp")); 0374 0375 /* 0376 * Assign the content-type if the server didn't return anything. 0377 * According to RFC4791, §5.2.3: 0378 * In the absence of this property, the server MUST accept all 0379 * component types, and the client can assume that all component 0380 * types are accepted. 0381 */ 0382 if (compElement.isNull()) { 0383 contentTypes |= DavCollection::Calendar; 0384 contentTypes |= DavCollection::Events; 0385 contentTypes |= DavCollection::Todos; 0386 contentTypes |= DavCollection::FreeBusy; 0387 contentTypes |= DavCollection::Journal; 0388 } 0389 0390 while (!compElement.isNull()) { 0391 const QString type = compElement.attribute(QStringLiteral("name")).toLower(); 0392 if (type == QLatin1String("vcalendar")) { 0393 contentTypes |= DavCollection::Calendar; 0394 } else if (type == QLatin1String("vevent")) { 0395 contentTypes |= DavCollection::Events; 0396 } else if (type == QLatin1String("vtodo")) { 0397 contentTypes |= DavCollection::Todos; 0398 } else if (type == QLatin1String("vfreebusy")) { 0399 contentTypes |= DavCollection::FreeBusy; 0400 } else if (type == QLatin1String("vjournal")) { 0401 contentTypes |= DavCollection::Journal; 0402 } 0403 0404 compElement = Utils::nextSiblingElementNS(compElement, QStringLiteral("urn:ietf:params:xml:ns:caldav"), QStringLiteral("comp")); 0405 } 0406 0407 return contentTypes; 0408 }