1/*
2 * Copyright (C) by Klaas Freitag <freitag@owncloud.com>
3 * Copyright (C) by Daniel Molkentin <danimo@owncloud.com>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 * for more details.
14 */
15
16#pragma once
17
18#include "owncloudlib.h"
19#include <QObject>
20#include <QNetworkRequest>
21#include <QNetworkReply>
22#include <QPointer>
23#include <QElapsedTimer>
24#include <QDateTime>
25#include <QTimer>
26#include "accountfwd.h"
27
28class QUrl;
29
30namespace OCC {
31
32class AbstractSslErrorHandler;
33
34/**
35 * @brief The AbstractNetworkJob class
36 * @ingroup libsync
37 */
38class OWNCLOUDSYNC_EXPORT AbstractNetworkJob : public QObject
39{
40 Q_OBJECT
41public:
42 explicit AbstractNetworkJob(AccountPtr account, const QString &path, QObject *parent = 0);
43 virtual ~AbstractNetworkJob();
44
45 virtual void start();
46
47 AccountPtr account() const { return _account; }
48
49 void setPath(const QString &path);
50 QString path() const { return _path; }
51
52 void setReply(QNetworkReply *reply);
53 QNetworkReply *reply() const { return _reply; }
54
55 void setIgnoreCredentialFailure(bool ignore);
56 bool ignoreCredentialFailure() const { return _ignoreCredentialFailure; }
57
58 /** Whether to handle redirects transparently.
59 *
60 * If true, a follow-up request is issued automatically when
61 * a redirect is encountered. The finished() function is only
62 * called if there are no more redirects (or there are problems
63 * with the redirect).
64 *
65 * The transparent redirect following may be disabled for some
66 * requests where custom handling is necessary.
67 */
68 void setFollowRedirects(bool follow);
69 bool followRedirects() const { return _followRedirects; }
70
71 QByteArray responseTimestamp();
72 /* Content of the X-Request-ID header. (Only set after the request is sent) */
73 QByteArray requestId();
74
75 qint64 timeoutMsec() const { return _timer.interval(); }
76 bool timedOut() const { return _timedout; }
77
78 /** Returns an error message, if any. */
79 QString errorString() const;
80
81 /** Like errorString, but also checking the reply body for information.
82 *
83 * Specifically, sometimes xml bodies have extra error information.
84 * This function reads the body of the reply and parses out the
85 * error information, if possible.
86 *
87 * \a body is optinally filled with the reply body.
88 *
89 * Warning: Needs to call reply()->readAll().
90 */
91 QString errorStringParsingBody(QByteArray *body = 0);
92
93 /** static variable the HTTP timeout (in seconds). If set to 0, the default will be used
94 */
95 static int httpTimeout;
96
97public slots:
98 void setTimeout(qint64 msec);
99 void resetTimeout();
100signals:
101 /** Emitted on network error.
102 *
103 * \a reply is never null
104 */
105 void networkError(QNetworkReply *reply);
106 void networkActivity();
107
108 /** Emitted when a redirect is followed.
109 *
110 * \a reply The "please redirect" reply
111 * \a targetUrl Where to redirect to
112 * \a redirectCount Counts redirect hops, first is 0.
113 */
114 void redirected(QNetworkReply *reply, const QUrl &targetUrl, int redirectCount);
115
116protected:
117 /** Initiate a network request, returning a QNetworkReply.
118 *
119 * Calls setReply() and setupConnections() on it.
120 *
121 * Takes ownership of the requestBody (to allow redirects).
122 */
123 QNetworkReply *sendRequest(const QByteArray &verb, const QUrl &url,
124 QNetworkRequest req = QNetworkRequest(),
125 QIODevice *requestBody = 0);
126
127 // sendRequest does not take a relative path instead of an url,
128 // but the old API allowed that. We have this undefined overload
129 // to help catch usage errors
130 QNetworkReply *sendRequest(const QByteArray &verb, const QString &relativePath,
131 QNetworkRequest req = QNetworkRequest(),
132 QIODevice *requestBody = 0);
133
134 /** Makes this job drive a pre-made QNetworkReply
135 *
136 * This reply cannot have a QIODevice request body because we can't get
137 * at it and thus not resend it in case of redirects.
138 */
139 void adoptRequest(QNetworkReply *reply);
140
141 void setupConnections(QNetworkReply *reply);
142
143 /** Can be used by derived classes to set up the network reply.
144 *
145 * Particularly useful when the request is redirected and reply()
146 * changes. For things like setting up additional signal connections
147 * on the new reply.
148 */
149 virtual void newReplyHook(QNetworkReply *) {}
150
151 /// Creates a url for the account from a relative path
152 QUrl makeAccountUrl(const QString &relativePath) const;
153
154 /// Like makeAccountUrl() but uses the account's dav base path
155 QUrl makeDavUrl(const QString &relativePath) const;
156
157 int maxRedirects() const { return 10; }
158
159 /** Called at the end of QNetworkReply::finished processing.
160 *
161 * Returning true triggers a deleteLater() of this job.
162 */
163 virtual bool finished() = 0;
164
165 /** Called on timeout.
166 *
167 * The default implementation aborts the reply.
168 */
169 virtual void onTimedOut();
170
171 QByteArray _responseTimestamp;
172 bool _timedout; // set to true when the timeout slot is received
173
174 // Automatically follows redirects. Note that this only works for
175 // GET requests that don't set up any HTTP body or other flags.
176 bool _followRedirects;
177
178 QString replyStatusString();
179
180private slots:
181 void slotFinished();
182 void slotTimeout();
183
184protected:
185 AccountPtr _account;
186
187private:
188 QNetworkReply *addTimer(QNetworkReply *reply);
189 bool _ignoreCredentialFailure;
190 QPointer<QNetworkReply> _reply; // (QPointer because the NetworkManager may be destroyed before the jobs at exit)
191 QString _path;
192 QTimer _timer;
193 int _redirectCount;
194
195 // Set by the xyzRequest() functions and needed to be able to redirect
196 // requests, should it be required.
197 //
198 // Reparented to the currently running QNetworkReply.
199 QPointer<QIODevice> _requestBody;
200};
201
202/**
203 * @brief Internal Helper class
204 */
205class NetworkJobTimeoutPauser
206{
207public:
208 NetworkJobTimeoutPauser(QNetworkReply *reply);
209 ~NetworkJobTimeoutPauser();
210
211private:
212 QPointer<QTimer> _timer;
213};
214
215
216/** Gets the SabreDAV-style error message from an error response.
217 *
218 * This assumes the response is XML with a 'error' tag that has a
219 * 'message' tag that contains the data to extract.
220 *
221 * Returns a null string if no message was found.
222 */
223QString OWNCLOUDSYNC_EXPORT extractErrorMessage(const QByteArray &errorResponse);
224
225/** Builds a error message based on the error and the reply body. */
226QString OWNCLOUDSYNC_EXPORT errorMessage(const QString &baseError, const QByteArray &body);
227
228/** Helper to construct the HTTP verb used in the request
229 *
230 * Returns an empty QByteArray for UnknownOperation.
231 */
232QByteArray OWNCLOUDSYNC_EXPORT requestVerb(const QNetworkReply &reply);
233
234/** Nicer errorString() for QNetworkReply
235 *
236 * By default QNetworkReply::errorString() often produces messages like
237 * "Error downloading <url> - server replied: <reason>"
238 * but the "downloading" part invariably confuses people since the
239 * error might very well have been produced by a PUT request.
240 *
241 * This function produces clearer error messages for HTTP errors.
242 */
243QString OWNCLOUDSYNC_EXPORT networkReplyErrorString(const QNetworkReply &reply);
244
245} // namespace OCC
246