File indexing completed on 2024-11-17 04:44:02
0001 /* 0002 Copyright (c) 2009 Grégory Oestreicher <greg@kamago.net> 0003 0004 This program is free software; you can redistribute it and/or modify 0005 it under the terms of the GNU General Public License as published by 0006 the Free Software Foundation; either version 2 of the License, or 0007 (at your option) any later version. 0008 0009 This program is distributed in the hope that it will be useful, 0010 but WITHOUT ANY WARRANTY; without even the implied warranty of 0011 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 0012 GNU General Public License for more details. 0013 0014 You should have received a copy of the GNU General Public License 0015 along with this program; if not, write to the Free Software 0016 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 0017 */ 0018 0019 #include "caldavprotocol.h" 0020 #include "common/utils.h" 0021 0022 #include <QtCore/QDateTime> 0023 #include <QtCore/QStringList> 0024 #include <QtCore/QUrl> 0025 #include <QtCore/QVariant> 0026 #include <QtXml/QDomDocument> 0027 0028 using namespace KDAV2; 0029 0030 class CaldavCollectionQueryBuilder : public XMLQueryBuilder 0031 { 0032 public: 0033 QDomDocument buildQuery() const Q_DECL_OVERRIDE 0034 { 0035 QDomDocument document; 0036 0037 QDomElement propfindElement = document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("propfind")); 0038 document.appendChild(propfindElement); 0039 0040 QDomElement propElement = document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("prop")); 0041 propfindElement.appendChild(propElement); 0042 0043 propElement.appendChild(document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("displayname"))); 0044 propElement.appendChild(document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("resourcetype"))); 0045 propElement.appendChild(document.createElementNS(QStringLiteral("http://apple.com/ns/ical/"), QStringLiteral("calendar-color"))); 0046 propElement.appendChild(document.createElementNS(QStringLiteral("urn:ietf:params:xml:ns:caldav"), QStringLiteral("supported-calendar-component-set"))); 0047 propElement.appendChild(document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("current-user-privilege-set"))); 0048 propElement.appendChild(document.createElementNS(QStringLiteral("http://calendarserver.org/ns/"), QStringLiteral("getctag"))); 0049 0050 return document; 0051 } 0052 0053 QString mimeType() const Q_DECL_OVERRIDE 0054 { 0055 return QString(); 0056 } 0057 }; 0058 0059 static QDomDocument listQuery(const QString &typeFilter, const QString startTime, const QString &endTime) 0060 { 0061 QDomDocument document; 0062 0063 QDomElement queryElement = document.createElementNS(QStringLiteral("urn:ietf:params:xml:ns:caldav"), QStringLiteral("calendar-query")); 0064 document.appendChild(queryElement); 0065 0066 QDomElement propElement = document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("prop")); 0067 queryElement.appendChild(propElement); 0068 0069 QDomElement getetagElement = document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("getetag")); 0070 propElement.appendChild(getetagElement); 0071 0072 QDomElement getRTypeElement = document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("resourcetype")); 0073 propElement.appendChild(getRTypeElement); 0074 0075 QDomElement filterElement = document.createElementNS(QStringLiteral("urn:ietf:params:xml:ns:caldav"), QStringLiteral("filter")); 0076 queryElement.appendChild(filterElement); 0077 0078 QDomElement compfilterElement = document.createElementNS(QStringLiteral("urn:ietf:params:xml:ns:caldav"), QStringLiteral("comp-filter")); 0079 0080 QDomAttr nameAttribute = document.createAttribute(QStringLiteral("name")); 0081 nameAttribute.setValue(QStringLiteral("VCALENDAR")); 0082 compfilterElement.setAttributeNode(nameAttribute); 0083 filterElement.appendChild(compfilterElement); 0084 0085 QDomElement subcompfilterElement = document.createElementNS(QStringLiteral("urn:ietf:params:xml:ns:caldav"), QStringLiteral("comp-filter")); 0086 nameAttribute = document.createAttribute(QStringLiteral("name")); 0087 nameAttribute.setValue(typeFilter); 0088 subcompfilterElement.setAttributeNode(nameAttribute); 0089 0090 if (!startTime.isEmpty() || !endTime.isEmpty()) { 0091 QDomElement timeRangeElement = document.createElementNS(QStringLiteral("urn:ietf:params:xml:ns:caldav"), QStringLiteral("time-range")); 0092 0093 if (!startTime.isEmpty()) { 0094 QDomAttr startAttribute = document.createAttribute(QStringLiteral("start")); 0095 startAttribute.setValue(startTime); 0096 timeRangeElement.setAttributeNode(startAttribute); 0097 } 0098 0099 if (!endTime.isEmpty()) { 0100 QDomAttr endAttribute = document.createAttribute(QStringLiteral("end")); 0101 endAttribute.setValue(endTime); 0102 timeRangeElement.setAttributeNode(endAttribute); 0103 } 0104 0105 subcompfilterElement.appendChild(timeRangeElement); 0106 } 0107 compfilterElement.appendChild(subcompfilterElement); 0108 0109 return document; 0110 } 0111 0112 class CaldavListEventQueryBuilder : public XMLQueryBuilder 0113 { 0114 public: 0115 QDomDocument buildQuery() const Q_DECL_OVERRIDE 0116 { 0117 return listQuery(QLatin1String{"VEVENT"}, parameter(QStringLiteral("start")).toString(), parameter(QStringLiteral("end")).toString()); 0118 } 0119 0120 QString mimeType() const Q_DECL_OVERRIDE 0121 { 0122 return QStringLiteral("VEVENT"); 0123 } 0124 }; 0125 0126 class CaldavListTodoQueryBuilder : public XMLQueryBuilder 0127 { 0128 public: 0129 QDomDocument buildQuery() const Q_DECL_OVERRIDE 0130 { 0131 return listQuery(QLatin1String{"VTODO"}, parameter(QStringLiteral("start")).toString(), parameter(QStringLiteral("end")).toString()); 0132 } 0133 0134 QString mimeType() const Q_DECL_OVERRIDE 0135 { 0136 return QStringLiteral("VTODO"); 0137 } 0138 }; 0139 0140 class CaldavListJournalQueryBuilder : public XMLQueryBuilder 0141 { 0142 public: 0143 QDomDocument buildQuery() const Q_DECL_OVERRIDE 0144 { 0145 return listQuery(QLatin1String{"VJOURNAL"}, parameter(QStringLiteral("start")).toString(), parameter(QStringLiteral("end")).toString()); 0146 } 0147 0148 QString mimeType() const Q_DECL_OVERRIDE 0149 { 0150 return QStringLiteral("VJOURNAL"); 0151 } 0152 }; 0153 0154 class CaldavMultigetQueryBuilder : public XMLQueryBuilder 0155 { 0156 public: 0157 QDomDocument buildQuery() const Q_DECL_OVERRIDE 0158 { 0159 QDomDocument document; 0160 const QStringList urls = parameter(QStringLiteral("urls")).toStringList(); 0161 if (urls.isEmpty()) { 0162 return document; 0163 } 0164 0165 QDomElement multigetElement = document.createElementNS(QStringLiteral("urn:ietf:params:xml:ns:caldav"), QStringLiteral("calendar-multiget")); 0166 document.appendChild(multigetElement); 0167 0168 QDomElement propElement = document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("prop")); 0169 multigetElement.appendChild(propElement); 0170 0171 propElement.appendChild(document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("getetag"))); 0172 propElement.appendChild(document.createElementNS(QStringLiteral("urn:ietf:params:xml:ns:caldav"), QStringLiteral("calendar-data"))); 0173 0174 for (const QString &url : urls) { 0175 QDomElement hrefElement = document.createElementNS(QStringLiteral("DAV:"), QStringLiteral("href")); 0176 const QUrl pathUrl = QUrl::fromUserInput(url); 0177 const QDomText textNode = document.createTextNode(pathUrl.toString()); 0178 hrefElement.appendChild(textNode); 0179 0180 multigetElement.appendChild(hrefElement); 0181 } 0182 0183 return document; 0184 } 0185 0186 QString mimeType() const Q_DECL_OVERRIDE 0187 { 0188 return QString(); 0189 } 0190 }; 0191 0192 CaldavProtocol::CaldavProtocol() 0193 { 0194 } 0195 0196 bool CaldavProtocol::supportsPrincipals() const 0197 { 0198 return true; 0199 } 0200 0201 bool CaldavProtocol::supportsCTags() const 0202 { 0203 return true; 0204 } 0205 0206 bool CaldavProtocol::useReport() const 0207 { 0208 return true; 0209 } 0210 0211 bool CaldavProtocol::useMultiget() const 0212 { 0213 return true; 0214 } 0215 0216 QString CaldavProtocol::principalHomeSet() const 0217 { 0218 return QStringLiteral("calendar-home-set"); 0219 } 0220 0221 QString CaldavProtocol::principalHomeSetNS() const 0222 { 0223 return QStringLiteral("urn:ietf:params:xml:ns:caldav"); 0224 } 0225 0226 XMLQueryBuilder::Ptr CaldavProtocol::collectionsQuery() const 0227 { 0228 return XMLQueryBuilder::Ptr(new CaldavCollectionQueryBuilder()); 0229 } 0230 0231 QString CaldavProtocol::collectionsXQuery() const 0232 { 0233 //const QString query( "//*[local-name()='calendar' and namespace-uri()='urn:ietf:params:xml:ns:caldav']/ancestor::*[local-name()='prop' and namespace-uri()='DAV:']/*[local-name()='supported-calendar-component-set' and namespace-uri()='urn:ietf:params:xml:ns:caldav']/*[local-name()='comp' and namespace-uri()='urn:ietf:params:xml:ns:caldav' and (@name='VTODO' or @name='VEVENT')]/ancestor::*[local-name()='response' and namespace-uri()='DAV:']" ); 0234 const QString query(QStringLiteral("//*[local-name()='calendar' and namespace-uri()='urn:ietf:params:xml:ns:caldav']/ancestor::*[local-name()='prop' and namespace-uri()='DAV:']/ancestor::*[local-name()='response' and namespace-uri()='DAV:']")); 0235 0236 return query; 0237 } 0238 0239 QVector<XMLQueryBuilder::Ptr> CaldavProtocol::itemsQueries() const 0240 { 0241 QVector<XMLQueryBuilder::Ptr> ret; 0242 ret << XMLQueryBuilder::Ptr(new CaldavListEventQueryBuilder()); 0243 ret << XMLQueryBuilder::Ptr(new CaldavListTodoQueryBuilder()); 0244 ret << XMLQueryBuilder::Ptr(new CaldavListJournalQueryBuilder()); 0245 return ret; 0246 } 0247 0248 XMLQueryBuilder::Ptr CaldavProtocol::itemsReportQuery(const QStringList &urls) const 0249 { 0250 XMLQueryBuilder::Ptr ret(new CaldavMultigetQueryBuilder()); 0251 ret->setParameter(QStringLiteral("urls"), urls); 0252 return ret; 0253 } 0254 0255 QString CaldavProtocol::responseNamespace() const 0256 { 0257 return QStringLiteral("urn:ietf:params:xml:ns:caldav"); 0258 } 0259 0260 QString CaldavProtocol::dataTagName() const 0261 { 0262 return QStringLiteral("calendar-data"); 0263 } 0264 0265 DavCollection::ContentTypes CaldavProtocol::collectionContentTypes(const QDomElement &propstatElement) const 0266 { 0267 /* 0268 * Extract the content type information from a propstat like the following 0269 * <propstat xmlns="DAV:"> 0270 * <prop xmlns="DAV:"> 0271 * <C:supported-calendar-component-set xmlns:C="urn:ietf:params:xml:ns:caldav"> 0272 * <C:comp xmlns:C="urn:ietf:params:xml:ns:caldav" name="VEVENT"/> 0273 * <C:comp xmlns:C="urn:ietf:params:xml:ns:caldav" name="VTODO"/> 0274 * <C:comp xmlns:C="urn:ietf:params:xml:ns:caldav" name="VJOURNAL"/> 0275 * <C:comp xmlns:C="urn:ietf:params:xml:ns:caldav" name="VTIMEZONE"/> 0276 * <C:comp xmlns:C="urn:ietf:params:xml:ns:caldav" name="VFREEBUSY"/> 0277 * </C:supported-calendar-component-set> 0278 * <resourcetype xmlns="DAV:"> 0279 * <collection xmlns="DAV:"/> 0280 * <C:calendar xmlns:C="urn:ietf:params:xml:ns:caldav"/> 0281 * <C:schedule-calendar xmlns:C="urn:ietf:params:xml:ns:caldav"/> 0282 * </resourcetype> 0283 * <displayname xmlns="DAV:">Test1 User</displayname> 0284 * </prop> 0285 * <status xmlns="DAV:">HTTP/1.1 200 OK</status> 0286 * </propstat> 0287 */ 0288 0289 const QDomElement propElement = Utils::firstChildElementNS(propstatElement, QStringLiteral("DAV:"), QStringLiteral("prop")); 0290 const QDomElement supportedcomponentElement = Utils::firstChildElementNS(propElement, QStringLiteral("urn:ietf:params:xml:ns:caldav"), QStringLiteral("supported-calendar-component-set")); 0291 0292 DavCollection::ContentTypes contentTypes; 0293 QDomElement compElement = Utils::firstChildElementNS(supportedcomponentElement, QStringLiteral("urn:ietf:params:xml:ns:caldav"), QStringLiteral("comp")); 0294 0295 /* 0296 * Assign the content-type if the server didn't return anything. 0297 * According to RFC4791, §5.2.3: 0298 * In the absence of this property, the server MUST accept all 0299 * component types, and the client can assume that all component 0300 * types are accepted. 0301 */ 0302 if (compElement.isNull()) { 0303 contentTypes |= DavCollection::Calendar; 0304 contentTypes |= DavCollection::Events; 0305 contentTypes |= DavCollection::Todos; 0306 contentTypes |= DavCollection::FreeBusy; 0307 contentTypes |= DavCollection::Journal; 0308 } 0309 0310 while (!compElement.isNull()) { 0311 const QString type = compElement.attribute(QStringLiteral("name")).toLower(); 0312 if (type == QLatin1String("vcalendar")) { 0313 contentTypes |= DavCollection::Calendar; 0314 } else if (type == QLatin1String("vevent")) { 0315 contentTypes |= DavCollection::Events; 0316 } else if (type == QLatin1String("vtodo")) { 0317 contentTypes |= DavCollection::Todos; 0318 } else if (type == QLatin1String("vfreebusy")) { 0319 contentTypes |= DavCollection::FreeBusy; 0320 } else if (type == QLatin1String("vjournal")) { 0321 contentTypes |= DavCollection::Journal; 0322 } 0323 0324 compElement = Utils::nextSiblingElementNS(compElement, QStringLiteral("urn:ietf:params:xml:ns:caldav"), QStringLiteral("comp")); 0325 } 0326 0327 return contentTypes; 0328 }