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
17 using namespace KDAV;
18
19 namespace KDAV
20 {
21 class DavCollectionModifyJobPrivate : public DavJobBasePrivate
22 {
23 public:
24 void davJobFinished(KJob *job);
25
26 DavUrl mUrl;
27 QDomDocument mQuery;
28
29 QVector<QDomElement> mSetProperties;
30 QVector<QDomElement> mRemoveProperties;
31 };
32 }
33
DavCollectionModifyJob(const DavUrl & url,QObject * parent)34 DavCollectionModifyJob::DavCollectionModifyJob(const DavUrl &url, QObject *parent)
35 : DavJobBase(new DavCollectionModifyJobPrivate, parent)
36 {
37 Q_D(DavCollectionModifyJob);
38 d->mUrl = url;
39 }
40
setProperty(const QString & prop,const QString & value,const QString & ns)41 void 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(prop);
48 } else {
49 propElement = d->mQuery.createElementNS(ns, prop);
50 }
51
52 const QDomText textElement = d->mQuery.createTextNode(value);
53 propElement.appendChild(textElement);
54
55 d->mSetProperties << propElement;
56 }
57
removeProperty(const QString & prop,const QString & ns)58 void 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(prop);
65 } else {
66 propElement = d->mQuery.createElementNS(ns, prop);
67 }
68
69 d->mRemoveProperties << propElement;
70 }
71
start()72 void 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(propertyUpdateElement);
85
86 if (!d->mSetProperties.isEmpty()) {
87 QDomElement setElement = mQuery.createElementNS(QStringLiteral("DAV:"), QStringLiteral("set"));
88 propertyUpdateElement.appendChild(setElement);
89
90 QDomElement propElement = mQuery.createElementNS(QStringLiteral("DAV:"), QStringLiteral("prop"));
91 setElement.appendChild(propElement);
92
93 for (const QDomElement &element : std::as_const(d->mSetProperties)) {
94 propElement.appendChild(element);
95 }
96 }
97
98 if (!d->mRemoveProperties.isEmpty()) {
99 QDomElement removeElement = mQuery.createElementNS(QStringLiteral("DAV:"), QStringLiteral("remove"));
100 propertyUpdateElement.appendChild(removeElement);
101
102 QDomElement propElement = mQuery.createElementNS(QStringLiteral("DAV:"), QStringLiteral("prop"));
103 removeElement.appendChild(propElement);
104
105 for (const QDomElement &element : std::as_const(d->mSetProperties)) {
106 propElement.appendChild(element);
107 }
108 }
109
110 KIO::DavJob *job = DavManager::self()->createPropPatchJob(d->mUrl.url(), mQuery.toString());
111 job->addMetaData(QStringLiteral("PropagateHttpHeader"), QStringLiteral("true"));
112 connect(job, &KIO::DavJob::result, this, [d](KJob *job) {
113 d->davJobFinished(job);
114 });
115 }
116
davJobFinished(KJob * job)117 void DavCollectionModifyJobPrivate::davJobFinished(KJob *job)
118 {
119 KIO::DavJob *davJob = qobject_cast<KIO::DavJob *>(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(davJob->responseData(), true);
136 QDomElement responseElement = Utils::firstChildElementNS(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(i).toElement();
144 const QDomElement statusElement = Utils::firstChildElementNS(propstatElement, QStringLiteral("DAV:"), QStringLiteral("status"));
145
146 const QString statusText = statusElement.text();
147 if (statusText.contains(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(responseElement, QStringLiteral("DAV:"), QStringLiteral("responsedescription"));
162 if (!responseDescriptionElement.isNull()) {
163 setJobErrorText(responseDescriptionElement.text());
164 }
165 setErrorTextFromDavError();
166 }
167
168 emitResult();
169 }
170