1/*
2 SPDX-FileCopyrightText: 2010 Grégory Oestreicher <greg@kamago.net>
3
4 SPDX-License-Identifier: LGPL-2.0-or-later
5*/
6
7#include "davcollectionmodifyjob.h"
8#include "davjobbase_p.h"
9#include "davmanager_p.h"
10
11#include "daverror.h"
12#include "utils_p.h"
13
14#include <KIO/DavJob>
15#include <KIO/Job>
16
17using namespace KDAV;
18
19namespace KDAV
20{
21class DavCollectionModifyJobPrivate : public DavJobBasePrivate
22{
23public:
24 void davJobFinished(KJob *job);
25
26 DavUrl mUrl;
27 QDomDocument mQuery;
28
29 QList<QDomElement> mSetProperties;
30 QList<QDomElement> mRemoveProperties;
31};
32}
33
34DavCollectionModifyJob::DavCollectionModifyJob(const DavUrl &url, QObject *parent)
35 : DavJobBase(new DavCollectionModifyJobPrivate, parent)
36{
37 Q_D(DavCollectionModifyJob);
38 d->mUrl = url;
39}
40
41void DavCollectionModifyJob::setProperty(const QString &prop, const QString &value, const QString &ns)
42{
43 Q_D(DavCollectionModifyJob);
44 QDomElement propElement;
45
46 if (ns.isEmpty()) {
47 propElement = d->mQuery.createElement(tagName: prop);
48 } else {
49 propElement = d->mQuery.createElementNS(nsURI: ns, qName: prop);
50 }
51
52 const QDomText textElement = d->mQuery.createTextNode(data: value);
53 propElement.appendChild(newChild: textElement);
54
55 d->mSetProperties << propElement;
56}
57
58void DavCollectionModifyJob::removeProperty(const QString &prop, const QString &ns)
59{
60 Q_D(DavCollectionModifyJob);
61 QDomElement propElement;
62
63 if (ns.isEmpty()) {
64 propElement = d->mQuery.createElement(tagName: prop);
65 } else {
66 propElement = d->mQuery.createElementNS(nsURI: ns, qName: prop);
67 }
68
69 d->mRemoveProperties << propElement;
70}
71
72void DavCollectionModifyJob::start()
73{
74 Q_D(DavCollectionModifyJob);
75 if (d->mSetProperties.isEmpty() && d->mRemoveProperties.isEmpty()) {
76 setError(ERR_COLLECTIONMODIFY_NO_PROPERITES);
77 d->setErrorTextFromDavError();
78 emitResult();
79 return;
80 }
81
82 QDomDocument mQuery;
83 QDomElement propertyUpdateElement = mQuery.createElementNS(QStringLiteral("DAV:"), QStringLiteral("propertyupdate"));
84 mQuery.appendChild(newChild: propertyUpdateElement);
85
86 if (!d->mSetProperties.isEmpty()) {
87 QDomElement setElement = mQuery.createElementNS(QStringLiteral("DAV:"), QStringLiteral("set"));
88 propertyUpdateElement.appendChild(newChild: setElement);
89
90 QDomElement propElement = mQuery.createElementNS(QStringLiteral("DAV:"), QStringLiteral("prop"));
91 setElement.appendChild(newChild: propElement);
92
93 for (const QDomElement &element : std::as_const(t&: d->mSetProperties)) {
94 propElement.appendChild(newChild: element);
95 }
96 }
97
98 if (!d->mRemoveProperties.isEmpty()) {
99 QDomElement removeElement = mQuery.createElementNS(QStringLiteral("DAV:"), QStringLiteral("remove"));
100 propertyUpdateElement.appendChild(newChild: removeElement);
101
102 QDomElement propElement = mQuery.createElementNS(QStringLiteral("DAV:"), QStringLiteral("prop"));
103 removeElement.appendChild(newChild: propElement);
104
105 for (const QDomElement &element : std::as_const(t&: d->mSetProperties)) {
106 propElement.appendChild(newChild: element);
107 }
108 }
109
110 KIO::DavJob *job = DavManager::self()->createPropPatchJob(url: d->mUrl.url(), document: mQuery.toString());
111 job->addMetaData(QStringLiteral("PropagateHttpHeader"), QStringLiteral("true"));
112 connect(sender: job, signal: &KIO::DavJob::result, context: this, slot: [d](KJob *job) {
113 d->davJobFinished(job);
114 });
115}
116
117void DavCollectionModifyJobPrivate::davJobFinished(KJob *job)
118{
119 KIO::DavJob *davJob = qobject_cast<KIO::DavJob *>(object: job);
120 const QString responseCodeStr = davJob->queryMetaData(QStringLiteral("responsecode"));
121 const int responseCode = responseCodeStr.isEmpty() ? 0 : responseCodeStr.toInt();
122
123 // KIO::DavJob does not set error() even if the HTTP status code is a 4xx or a 5xx
124 if (davJob->error() || (responseCode >= 400 && responseCode < 600)) {
125 setLatestResponseCode(responseCode);
126 setError(ERR_COLLECTIONMODIFY);
127 setJobErrorText(davJob->errorText());
128 setJobError(davJob->error());
129 setErrorTextFromDavError();
130 emitResult();
131 return;
132 }
133
134 QDomDocument response;
135 response.setContent(data: davJob->responseData(), options: QDomDocument::ParseOption::UseNamespaceProcessing);
136 QDomElement responseElement = Utils::firstChildElementNS(parent: response.documentElement(), QStringLiteral("DAV:"), QStringLiteral("response"));
137
138 bool hasError = false;
139
140 // parse all propstats answers to get the eventual errors
141 const QDomNodeList propstats = responseElement.elementsByTagNameNS(QStringLiteral("DAV:"), QStringLiteral("propstat"));
142 for (int i = 0; i < propstats.length(); ++i) {
143 const QDomElement propstatElement = propstats.item(index: i).toElement();
144 const QDomElement statusElement = Utils::firstChildElementNS(parent: propstatElement, QStringLiteral("DAV:"), QStringLiteral("status"));
145
146 const QString statusText = statusElement.text();
147 if (statusText.contains(s: QLatin1String("200"))) {
148 continue;
149 } else {
150 // Generic error
151 hasError = true;
152 break;
153 }
154 }
155
156 if (hasError) {
157 setError(ERR_COLLECTIONMODIFY_RESPONSE);
158
159 // Trying to get more information about the error
160 const QDomElement responseDescriptionElement =
161 Utils::firstChildElementNS(parent: responseElement, QStringLiteral("DAV:"), QStringLiteral("responsedescription"));
162 if (!responseDescriptionElement.isNull()) {
163 setJobErrorText(responseDescriptionElement.text());
164 }
165 setErrorTextFromDavError();
166 }
167
168 emitResult();
169}
170
171#include "moc_davcollectionmodifyjob.cpp"
172

source code of kdav/src/common/davcollectionmodifyjob.cpp