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 | |
28 | class QUrl; |
29 | |
30 | namespace OCC { |
31 | |
32 | class AbstractSslErrorHandler; |
33 | |
34 | /** |
35 | * @brief The AbstractNetworkJob class |
36 | * @ingroup libsync |
37 | */ |
38 | class OWNCLOUDSYNC_EXPORT AbstractNetworkJob : public QObject |
39 | { |
40 | Q_OBJECT |
41 | public: |
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 | |
97 | public slots: |
98 | void setTimeout(qint64 msec); |
99 | void resetTimeout(); |
100 | signals: |
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 | |
116 | protected: |
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 | |
180 | private slots: |
181 | void slotFinished(); |
182 | void slotTimeout(); |
183 | |
184 | protected: |
185 | AccountPtr _account; |
186 | |
187 | private: |
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 | */ |
205 | class NetworkJobTimeoutPauser |
206 | { |
207 | public: |
208 | NetworkJobTimeoutPauser(QNetworkReply *reply); |
209 | ~NetworkJobTimeoutPauser(); |
210 | |
211 | private: |
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 | */ |
223 | QString OWNCLOUDSYNC_EXPORT (const QByteArray &errorResponse); |
224 | |
225 | /** Builds a error message based on the error and the reply body. */ |
226 | QString 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 | */ |
232 | QByteArray 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 | */ |
243 | QString OWNCLOUDSYNC_EXPORT networkReplyErrorString(const QNetworkReply &reply); |
244 | |
245 | } // namespace OCC |
246 | |