File indexing completed on 2024-06-16 04:52:28

0001 /*
0002     Copyright (c) 2010 Tobias Koenig <tokoe@kde.org>
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 "utils.h"
0020 
0021 #include "enums.h"
0022 
0023 #include "davitem.h"
0024 #include "davmanager.h"
0025 #include "davprotocolbase.h"
0026 #include "davurl.h"
0027 
0028 #include <QColor>
0029 #include <QtCore/QByteArray>
0030 #include <QtCore/QDateTime>
0031 #include <QtCore/QString>
0032 
0033 #include "libkdav2_debug.h"
0034 
0035 using namespace KDAV2;
0036 
0037 QDomElement Utils::firstChildElementNS(const QDomElement &parent, const QString &namespaceUri, const QString &tagName)
0038 {
0039     for (QDomNode child = parent.firstChild(); !child.isNull(); child = child.nextSibling()) {
0040         if (child.isElement()) {
0041             const QDomElement elt = child.toElement();
0042             if (tagName.isEmpty() || (elt.tagName() == tagName && elt.namespaceURI() == namespaceUri)) {
0043                 return elt;
0044             }
0045         }
0046     }
0047 
0048     return QDomElement();
0049 }
0050 
0051 QDomElement Utils::nextSiblingElementNS(const QDomElement &element, const QString &namespaceUri, const QString &tagName)
0052 {
0053     for (QDomNode sib = element.nextSibling(); !sib.isNull(); sib = sib.nextSibling()) {
0054         if (sib.isElement()) {
0055             const QDomElement elt = sib.toElement();
0056             if (tagName.isEmpty() || (elt.tagName() == tagName && elt.namespaceURI() == namespaceUri)) {
0057                 return elt;
0058             }
0059         }
0060     }
0061 
0062     return QDomElement();
0063 }
0064 
0065 Privileges Utils::extractPrivileges(const QDomElement &element)
0066 {
0067     Privileges final = None;
0068     QDomElement privElement = firstChildElementNS(element, QStringLiteral("DAV:"), QStringLiteral("privilege"));
0069 
0070     while (!privElement.isNull()) {
0071         QDomElement child = privElement.firstChildElement();
0072 
0073         while (!child.isNull()) {
0074             final |= parsePrivilege(child);
0075             child = child.nextSiblingElement();
0076         }
0077 
0078         privElement = Utils::nextSiblingElementNS(privElement, QStringLiteral("DAV:"), QStringLiteral("privilege"));
0079     }
0080 
0081     return final;
0082 }
0083 
0084 Privileges Utils::parsePrivilege(const QDomElement &element)
0085 {
0086     Privileges final = None;
0087 
0088     if (!element.childNodes().isEmpty()) {
0089         // This is an aggregate privilege, parse each of its children
0090         QDomElement child = element.firstChildElement();
0091         while (!child.isNull()) {
0092             final |= parsePrivilege(child);
0093             child = child.nextSiblingElement();
0094         }
0095     } else {
0096         // This is a normal privilege
0097         const QString privname = element.localName();
0098 
0099         if (privname == QLatin1String("read")) {
0100             final |= KDAV2::Read;
0101         } else if (privname == QLatin1String("write")) {
0102             final |= KDAV2::Write;
0103         } else if (privname == QLatin1String("write-properties")) {
0104             final |= KDAV2::WriteProperties;
0105         } else if (privname == QLatin1String("write-content")) {
0106             final |= KDAV2::WriteContent;
0107         } else if (privname == QLatin1String("unlock")) {
0108             final |= KDAV2::Unlock;
0109         } else if (privname == QLatin1String("read-acl")) {
0110             final |= KDAV2::ReadAcl;
0111         } else if (privname == QLatin1String("read-current-user-privilege-set")) {
0112             final |= KDAV2::ReadCurrentUserPrivilegeSet;
0113         } else if (privname == QLatin1String("write-acl")) {
0114             final |= KDAV2::WriteAcl;
0115         } else if (privname == QLatin1String("bind")) {
0116             final |= KDAV2::Bind;
0117         } else if (privname == QLatin1String("unbind")) {
0118             final |= KDAV2::Unbind;
0119         } else if (privname == QLatin1String("all")) {
0120             final |= KDAV2::All;
0121         }
0122     }
0123 
0124     return final;
0125 }
0126 
0127 QLatin1String Utils::protocolName(Protocol protocol)
0128 {
0129     QLatin1String protocolName("");
0130 
0131     switch (protocol) {
0132     case KDAV2::CalDav:
0133         protocolName = QLatin1String("CalDav");
0134         break;
0135     case KDAV2::CardDav:
0136         protocolName = QLatin1String("CardDav");
0137         break;
0138     case KDAV2::GroupDav:
0139         protocolName = QLatin1String("GroupDav");
0140         break;
0141     }
0142 
0143     return protocolName;
0144 }
0145 
0146 Protocol Utils::protocolByName(const QString &name)
0147 {
0148     Protocol protocol = KDAV2::CalDav;
0149 
0150     if (name == QLatin1String("CalDav")) {
0151         protocol = KDAV2::CalDav;
0152     } else if (name == QLatin1String("CardDav")) {
0153         protocol = KDAV2::CardDav;
0154     } else if (name == QLatin1String("GroupDav")) {
0155         protocol = KDAV2::GroupDav;
0156     } else {
0157         qCCritical(KDAV2_LOG) << "Unexpected protocol name : " << name;
0158     }
0159 
0160     return protocol;
0161 }
0162 
0163 QString Utils::createUniqueId()
0164 {
0165     qint64 time = QDateTime::currentMSecsSinceEpoch() / 1000;
0166     int r = qrand() % 1000;
0167     QString id = QLatin1String("R") + QString::number(r);
0168     QString uid = QString::number(time) + QLatin1String(".") + id;
0169     return uid;
0170 }
0171 
0172 QString Utils::contactsMimeType(Protocol protocol)
0173 {
0174     QString ret;
0175 
0176     if (protocol == KDAV2::CardDav) {
0177         ret = QStringLiteral("text/vcard");
0178     } else if (protocol == KDAV2::GroupDav) {
0179         ret = QStringLiteral("text/x-vcard");
0180     }
0181 
0182     return ret;
0183 }
0184 
0185 bool Utils::extractCollection(const QDomElement &response, DavUrl davUrl, DavCollection &collection)
0186 {
0187     QDomElement propstatElement;
0188 
0189     // check for the valid propstat, without giving up on first error
0190     {
0191         const QDomNodeList propstats =
0192             response.elementsByTagNameNS(QStringLiteral("DAV:"), QStringLiteral("propstat"));
0193         for (int i = 0; i < propstats.length(); ++i) {
0194             const QDomElement propstatCandidate = propstats.item(i).toElement();
0195             const QDomElement statusElement     = Utils::firstChildElementNS(
0196                 propstatCandidate, QStringLiteral("DAV:"), QStringLiteral("status"));
0197             if (statusElement.text().contains(QStringLiteral("200"))) {
0198                 propstatElement = propstatCandidate;
0199             }
0200         }
0201     }
0202 
0203     if (propstatElement.isNull()) {
0204         return false;
0205     }
0206 
0207     // extract url
0208     const QDomElement hrefElement =
0209         Utils::firstChildElementNS(response, QStringLiteral("DAV:"), QStringLiteral("href"));
0210 
0211     if (hrefElement.isNull()) {
0212         return false;
0213     }
0214 
0215 
0216     QString href = hrefElement.text();
0217     if (!href.endsWith(QLatin1Char('/'))) {
0218         href.append(QLatin1Char('/'));
0219     }
0220 
0221     QUrl url = davUrl.url();
0222     url.setUserInfo(QString());
0223     if (href.startsWith(QLatin1Char('/'))) {
0224         // href is only a path, use request url to complete
0225         url.setPath(href, QUrl::TolerantMode);
0226     } else {
0227         // href is a complete url
0228         url = QUrl::fromUserInput(href);
0229     }
0230 
0231     // extract display name
0232     const QDomElement propElement =
0233         Utils::firstChildElementNS(propstatElement, QStringLiteral("DAV:"), QStringLiteral("prop"));
0234     const QDomElement displaynameElement =
0235         Utils::firstChildElementNS(propElement, QStringLiteral("DAV:"), QStringLiteral("displayname"));
0236     const QString displayName = displaynameElement.text();
0237 
0238     // Extract CTag
0239     const QDomElement CTagElement = Utils::firstChildElementNS(
0240         propElement, QStringLiteral("http://calendarserver.org/ns/"), QStringLiteral("getctag"));
0241     QString CTag;
0242     if (!CTagElement.isNull()) {
0243         CTag = CTagElement.text();
0244     }
0245 
0246     // extract calendar color if provided
0247     const QDomElement colorElement = Utils::firstChildElementNS(
0248         propElement, QStringLiteral("http://apple.com/ns/ical/"), QStringLiteral("calendar-color"));
0249     QColor color;
0250     if (!colorElement.isNull()) {
0251         QString colorValue = colorElement.text();
0252         if(colorValue[0] == '#' && colorValue.size() == 9) {
0253             // Put the alpha part at the beginning for Qt:
0254             // Qt wants #AARRGGBB instead of #RRGGBBAA
0255             colorValue = QStringLiteral("#") + colorValue.right(2) + colorValue.mid(1, 6);
0256         }
0257 
0258         if (QColor::isValidColor(colorValue)) {
0259             color.setNamedColor(colorValue);
0260         }
0261     }
0262 
0263     // extract allowed content types
0264     const DavCollection::ContentTypes contentTypes =
0265         DavManager::self()->davProtocol(davUrl.protocol())->collectionContentTypes(propstatElement);
0266 
0267     auto _url = url;
0268     _url.setUserInfo(davUrl.url().userInfo());
0269     collection = DavCollection(DavUrl(_url, davUrl.protocol()), displayName, contentTypes);
0270 
0271     collection.setCTag(CTag);
0272     if (color.isValid()) {
0273         collection.setColor(color);
0274     }
0275 
0276     // extract privileges
0277     const QDomElement currentPrivsElement = Utils::firstChildElementNS(
0278         propElement, QStringLiteral("DAV:"), QStringLiteral("current-user-privilege-set"));
0279     if (currentPrivsElement.isNull()) {
0280         // Assume that we have all privileges
0281         collection.setPrivileges(KDAV2::All);
0282     } else {
0283         Privileges privileges = Utils::extractPrivileges(currentPrivsElement);
0284         collection.setPrivileges(privileges);
0285     }
0286 
0287     qCDebug(KDAV2_LOG) << url.toDisplayString() << "PRIVS: " << collection.privileges();
0288 
0289     return true;
0290 }