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 "davitemslistjob.h"
0020 
0021 #include "daverror.h"
0022 #include "davmanager.h"
0023 #include "davprotocolbase.h"
0024 #include "davurl.h"
0025 #include "utils.h"
0026 #include "davjob.h"
0027 
0028 #include <QtCore/QBuffer>
0029 #include <QtCore/QDebug>
0030 
0031 using namespace KDAV2;
0032 
0033 class DavItemsListJobPrivate {
0034 public:
0035     DavItemsListJobPrivate(const DavUrl &url);
0036 
0037     DavUrl mUrl;
0038     QStringList mMimeTypes;
0039     QString mRangeStart;
0040     QString mRangeEnd;
0041     DavItem::List mItems;
0042     QSet<QString> mSeenUrls; // to prevent events duplication with some servers
0043     uint mSubJobCount;
0044 };
0045 
0046 DavItemsListJobPrivate::DavItemsListJobPrivate(const DavUrl &url)
0047     : mUrl(url)
0048     , mSubJobCount(0)
0049 {
0050 }
0051 
0052 
0053 DavItemsListJob::DavItemsListJob(const DavUrl &url, QObject *parent)
0054     : DavJobBase(parent)
0055     , d(std::unique_ptr<DavItemsListJobPrivate>(new DavItemsListJobPrivate(url)))
0056 {
0057 }
0058 
0059 DavItemsListJob::~DavItemsListJob()
0060 {
0061 }
0062 
0063 void DavItemsListJob::setContentMimeTypes(const QStringList &types)
0064 {
0065     d->mMimeTypes = types;
0066 }
0067 
0068 void DavItemsListJob::setTimeRange(const QString &start, const QString &end)
0069 {
0070     d->mRangeStart = start;
0071     d->mRangeEnd = end;
0072 }
0073 
0074 void DavItemsListJob::start()
0075 {
0076     const DavProtocolBase *protocol = DavManager::self()->davProtocol(d->mUrl.protocol());
0077     Q_ASSERT(protocol);
0078     QVectorIterator<XMLQueryBuilder::Ptr> it(protocol->itemsQueries());
0079 
0080     while (it.hasNext()) {
0081         XMLQueryBuilder::Ptr builder = it.next();
0082         if (!d->mRangeStart.isEmpty()) {
0083             builder->setParameter(QStringLiteral("start"), d->mRangeStart);
0084         }
0085         if (!d->mRangeEnd.isEmpty()) {
0086             builder->setParameter(QStringLiteral("end"), d->mRangeEnd);
0087         }
0088 
0089         const QDomDocument props = builder->buildQuery();
0090         const QString mimeType = builder->mimeType();
0091 
0092         if (d->mMimeTypes.isEmpty() || d->mMimeTypes.contains(mimeType)) {
0093             ++d->mSubJobCount;
0094             const auto url = d->mUrl.url();
0095             auto job = protocol->useReport() ?
0096                 DavManager::self()->createReportJob(url, props) :
0097                 DavManager::self()->createPropFindJob(url, props);
0098             job->setProperty("itemsMimeType", mimeType);
0099             connect(job, &DavJob::result, this, &DavItemsListJob::davJobFinished);
0100         }
0101     }
0102 
0103     if (d->mSubJobCount == 0) {
0104         setError(ERR_ITEMLIST_NOMIMETYPE);
0105         setErrorTextFromDavError();
0106         emitResult();
0107     }
0108 }
0109 
0110 DavItem::List DavItemsListJob::items() const
0111 {
0112     return d->mItems;
0113 }
0114 
0115 void DavItemsListJob::davJobFinished(KJob *job)
0116 {
0117     auto davJob = static_cast<DavJob*>(job);
0118     if (davJob->error()) {
0119         setErrorFromJob(davJob);
0120     } else {
0121         /*
0122          * Extract data from a document like the following:
0123          *
0124          * <multistatus xmlns="DAV:">
0125          *   <response xmlns="DAV:">
0126          *     <href xmlns="DAV:">/caldav.php/test1.user/home/KOrganizer-166749289.780.ics</href>
0127          *     <propstat xmlns="DAV:">
0128          *       <prop xmlns="DAV:">
0129          *         <getetag xmlns="DAV:">"b4bbea0278f4f63854c4167a7656024a"</getetag>
0130          *       </prop>
0131          *       <status xmlns="DAV:">HTTP/1.1 200 OK</status>
0132          *     </propstat>
0133          *   </response>
0134          *   <response xmlns="DAV:">
0135          *     <href xmlns="DAV:">/caldav.php/test1.user/home/KOrganizer-399416366.464.ics</href>
0136          *     <propstat xmlns="DAV:">
0137          *       <prop xmlns="DAV:">
0138          *         <getetag xmlns="DAV:">"52eb129018398a7da4f435b2bc4c6cd5"</getetag>
0139          *       </prop>
0140          *       <status xmlns="DAV:">HTTP/1.1 200 OK</status>
0141          *     </propstat>
0142          *   </response>
0143          * </multistatus>
0144          */
0145 
0146         const QString itemsMimeType = job->property("itemsMimeType").toString();
0147         const QDomDocument document = davJob->response();
0148         const QDomElement documentElement = document.documentElement();
0149 
0150         QDomElement responseElement = Utils::firstChildElementNS(documentElement, QStringLiteral("DAV:"), QStringLiteral("response"));
0151         while (!responseElement.isNull()) {
0152 
0153             QDomElement propstatElement;
0154 
0155             // check for the valid propstat, without giving up on first error
0156             {
0157                 const QDomNodeList propstats = responseElement.elementsByTagNameNS(QStringLiteral("DAV:"), QStringLiteral("propstat"));
0158                 for (int i = 0; i < propstats.length(); ++i) {
0159                     const QDomElement propstatCandidate = propstats.item(i).toElement();
0160                     const QDomElement statusElement = Utils::firstChildElementNS(propstatCandidate, QStringLiteral("DAV:"), QStringLiteral("status"));
0161                     if (statusElement.text().contains(QStringLiteral("200"))) {
0162                         propstatElement = propstatCandidate;
0163                     }
0164                 }
0165             }
0166 
0167             if (propstatElement.isNull()) {
0168                 responseElement = Utils::nextSiblingElementNS(responseElement, QStringLiteral("DAV:"), QStringLiteral("response"));
0169                 continue;
0170             }
0171 
0172             const QDomElement propElement = Utils::firstChildElementNS(propstatElement, QStringLiteral("DAV:"), QStringLiteral("prop"));
0173 
0174             // check whether it is a dav collection ...
0175             const QDomElement resourcetypeElement = Utils::firstChildElementNS(propElement, QStringLiteral("DAV:"), QStringLiteral("resourcetype"));
0176             if (!responseElement.isNull()) {
0177                 const QDomElement collectionElement = Utils::firstChildElementNS(resourcetypeElement, QStringLiteral("DAV:"), QStringLiteral("collection"));
0178                 if (!collectionElement.isNull()) {
0179                     responseElement = Utils::nextSiblingElementNS(responseElement, QStringLiteral("DAV:"), QStringLiteral("response"));
0180                     continue;
0181                 }
0182             }
0183 
0184             // ... if not it is an item
0185             DavItem item;
0186             item.setContentType(itemsMimeType);
0187 
0188             // extract path
0189             const QDomElement hrefElement = Utils::firstChildElementNS(responseElement, QStringLiteral("DAV:"), QStringLiteral("href"));
0190             const QString href = hrefElement.text();
0191 
0192             QUrl url = davJob->url();
0193             url.setUserInfo(QString());
0194             if (href.startsWith(QLatin1Char('/'))) {
0195                 // href is only a path, use request url to complete
0196                 url.setPath(href, QUrl::TolerantMode);
0197             } else {
0198                 // href is a complete url
0199                 url = QUrl::fromUserInput(href);
0200             }
0201 
0202             QString itemUrl = url.toDisplayString();
0203             if (d->mSeenUrls.contains(itemUrl)) {
0204                 responseElement = Utils::nextSiblingElementNS(responseElement, QStringLiteral("DAV:"), QStringLiteral("response"));
0205                 continue;
0206             }
0207 
0208             d->mSeenUrls << itemUrl;
0209             auto _url = url;
0210             _url.setUserInfo(d->mUrl.url().userInfo());
0211             item.setUrl(DavUrl(_url, d->mUrl.protocol()));
0212 
0213             // extract etag
0214             const QDomElement getetagElement = Utils::firstChildElementNS(propElement, QStringLiteral("DAV:"), QStringLiteral("getetag"));
0215 
0216             item.setEtag(getetagElement.text());
0217 
0218             d->mItems << item;
0219 
0220             responseElement = Utils::nextSiblingElementNS(responseElement, QStringLiteral("DAV:"), QStringLiteral("response"));
0221         }
0222     }
0223 
0224     if (--d->mSubJobCount == 0) {
0225         emitResult();
0226     }
0227 }
0228