1/*
2 SPDX-FileCopyrightText: 2010 Tobias Koenig <tokoe@kde.org>
3
4 SPDX-License-Identifier: LGPL-2.0-or-later
5*/
6
7#include "davitemmodifyjob.h"
8#include "davjobbase_p.h"
9
10#include "daverror.h"
11#include "davitemfetchjob.h"
12
13#include <KIO/StoredTransferJob>
14
15using namespace KDAV;
16namespace KDAV
17{
18class DavItemModifyJobPrivate : public DavJobBasePrivate
19{
20public:
21 void davJobFinished(KJob *job);
22 void itemRefreshed(KJob *job);
23 void conflictingItemFetched(KJob *job);
24
25 DavItem mItem;
26 DavItem mFreshItem;
27 int mFreshResponseCode = 0;
28
29 Q_DECLARE_PUBLIC(DavItemModifyJob)
30};
31}
32
33DavItemModifyJob::DavItemModifyJob(const DavItem &item, QObject *parent)
34 : DavJobBase(new DavItemModifyJobPrivate, parent)
35{
36 Q_D(DavItemModifyJob);
37 d->mItem = item;
38}
39
40void DavItemModifyJob::start()
41{
42 Q_D(DavItemModifyJob);
43 QString headers = QStringLiteral("Content-Type: ");
44 headers += d->mItem.contentType();
45 headers += QLatin1String("\r\n");
46 headers += QLatin1String("If-Match: ") + d->mItem.etag();
47
48 KIO::StoredTransferJob *job = KIO::storedPut(arr: d->mItem.data(), url: itemUrl(), permissions: -1, flags: KIO::HideProgressInfo | KIO::DefaultFlags);
49 job->addMetaData(QStringLiteral("PropagateHttpHeader"), QStringLiteral("true"));
50 job->addMetaData(QStringLiteral("customHTTPHeader"), value: headers);
51 job->addMetaData(QStringLiteral("cookies"), QStringLiteral("none"));
52 job->addMetaData(QStringLiteral("no-auth-prompt"), QStringLiteral("true"));
53
54 connect(sender: job, signal: &KIO::StoredTransferJob::result, context: this, slot: [d](KJob *job) {
55 d->davJobFinished(job);
56 });
57}
58
59DavItem DavItemModifyJob::item() const
60{
61 Q_D(const DavItemModifyJob);
62 return d->mItem;
63}
64
65DavItem DavItemModifyJob::freshItem() const
66{
67 Q_D(const DavItemModifyJob);
68 return d->mFreshItem;
69}
70
71int DavItemModifyJob::freshResponseCode() const
72{
73 Q_D(const DavItemModifyJob);
74 return d->mFreshResponseCode;
75}
76
77QUrl DavItemModifyJob::itemUrl() const
78{
79 Q_D(const DavItemModifyJob);
80 return d->mItem.url().url();
81}
82
83void DavItemModifyJobPrivate::davJobFinished(KJob *job)
84{
85 Q_Q(DavItemModifyJob);
86 KIO::StoredTransferJob *storedJob = qobject_cast<KIO::StoredTransferJob *>(object: job);
87
88 if (storedJob->error()) {
89 const int responseCode = storedJob->queryMetaData(QStringLiteral("responsecode")).isEmpty() //
90 ? 0
91 : storedJob->queryMetaData(QStringLiteral("responsecode")).toInt();
92
93 setLatestResponseCode(responseCode);
94 setError(ERR_ITEMMODIFY);
95 setJobErrorText(storedJob->errorText());
96 setJobError(storedJob->error());
97 setErrorTextFromDavError();
98
99 if (q->hasConflict()) {
100 DavItemFetchJob *fetchJob = new DavItemFetchJob(mItem);
101 QObject::connect(sender: fetchJob, signal: &DavItemFetchJob::result, context: q, slot: [this](KJob *job) {
102 conflictingItemFetched(job);
103 });
104 fetchJob->start();
105 } else {
106 emitResult();
107 }
108
109 return;
110 }
111
112 // The 'Location:' HTTP header is used to indicate the new URL
113 const QStringList allHeaders = storedJob->queryMetaData(QStringLiteral("HTTP-Headers")).split(sep: QLatin1Char('\n'));
114 QString location;
115 for (const QString &header : allHeaders) {
116 if (header.startsWith(s: QLatin1String("location:"), cs: Qt::CaseInsensitive)) {
117 location = header.section(asep: QLatin1Char(' '), astart: 1);
118 }
119 }
120
121 QUrl url;
122 if (location.isEmpty()) {
123 url = storedJob->url();
124 } else if (location.startsWith(c: QLatin1Char('/'))) {
125 url = storedJob->url();
126 url.setPath(path: location, mode: QUrl::TolerantMode);
127 } else {
128 url = QUrl::fromUserInput(userInput: location);
129 }
130
131 url.setUserInfo(userInfo: q->itemUrl().userInfo());
132 mItem.setUrl(DavUrl(url, mItem.url().protocol()));
133
134 DavItemFetchJob *fetchJob = new DavItemFetchJob(mItem);
135 QObject::connect(sender: fetchJob, signal: &DavItemFetchJob::result, context: q, slot: [this](KJob *job) {
136 itemRefreshed(job);
137 });
138 fetchJob->start();
139}
140
141void DavItemModifyJobPrivate::itemRefreshed(KJob *job)
142{
143 if (!job->error()) {
144 DavItemFetchJob *fetchJob = qobject_cast<DavItemFetchJob *>(object: job);
145 mItem.setEtag(fetchJob->item().etag());
146 } else {
147 mItem.setEtag(QString());
148 }
149 emitResult();
150}
151
152void DavItemModifyJobPrivate::conflictingItemFetched(KJob *job)
153{
154 DavItemFetchJob *fetchJob = qobject_cast<DavItemFetchJob *>(object: job);
155 mFreshResponseCode = fetchJob->latestResponseCode();
156
157 if (!job->error()) {
158 mFreshItem = fetchJob->item();
159 }
160
161 emitResult();
162}
163
164#include "moc_davitemmodifyjob.cpp"
165

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