1 | /****************************************************************************
|
---|
2 | **
|
---|
3 | ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
|
---|
4 | ** All rights reserved.
|
---|
5 | ** Contact: Nokia Corporation (qt-info@nokia.com)
|
---|
6 | **
|
---|
7 | ** This file is part of the QtNetwork module of the Qt Toolkit.
|
---|
8 | **
|
---|
9 | ** $QT_BEGIN_LICENSE:LGPL$
|
---|
10 | ** Commercial Usage
|
---|
11 | ** Licensees holding valid Qt Commercial licenses may use this file in
|
---|
12 | ** accordance with the Qt Commercial License Agreement provided with the
|
---|
13 | ** Software or, alternatively, in accordance with the terms contained in
|
---|
14 | ** a written agreement between you and Nokia.
|
---|
15 | **
|
---|
16 | ** GNU Lesser General Public License Usage
|
---|
17 | ** Alternatively, this file may be used under the terms of the GNU Lesser
|
---|
18 | ** General Public License version 2.1 as published by the Free Software
|
---|
19 | ** Foundation and appearing in the file LICENSE.LGPL included in the
|
---|
20 | ** packaging of this file. Please review the following information to
|
---|
21 | ** ensure the GNU Lesser General Public License version 2.1 requirements
|
---|
22 | ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
---|
23 | **
|
---|
24 | ** In addition, as a special exception, Nokia gives you certain additional
|
---|
25 | ** rights. These rights are described in the Nokia Qt LGPL Exception
|
---|
26 | ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
---|
27 | **
|
---|
28 | ** GNU General Public License Usage
|
---|
29 | ** Alternatively, this file may be used under the terms of the GNU
|
---|
30 | ** General Public License version 3.0 as published by the Free Software
|
---|
31 | ** Foundation and appearing in the file LICENSE.GPL included in the
|
---|
32 | ** packaging of this file. Please review the following information to
|
---|
33 | ** ensure the GNU General Public License version 3.0 requirements will be
|
---|
34 | ** met: http://www.gnu.org/copyleft/gpl.html.
|
---|
35 | **
|
---|
36 | ** If you have questions regarding the use of this file, please contact
|
---|
37 | ** Nokia at qt-info@nokia.com.
|
---|
38 | ** $QT_END_LICENSE$
|
---|
39 | **
|
---|
40 | ****************************************************************************/
|
---|
41 |
|
---|
42 | #include "qnetworkaccessmanager.h"
|
---|
43 | #include "qnetworkaccessmanager_p.h"
|
---|
44 | #include "qnetworkrequest.h"
|
---|
45 | #include "qnetworkreply.h"
|
---|
46 | #include "qnetworkreply_p.h"
|
---|
47 | #include "qnetworkcookie.h"
|
---|
48 | #include "qabstractnetworkcache.h"
|
---|
49 |
|
---|
50 | #include "QtNetwork/qnetworksession.h"
|
---|
51 | #include "QtNetwork/private/qsharednetworksession_p.h"
|
---|
52 |
|
---|
53 | #include "qnetworkaccesshttpbackend_p.h"
|
---|
54 | #include "qnetworkaccessftpbackend_p.h"
|
---|
55 | #include "qnetworkaccessfilebackend_p.h"
|
---|
56 | #include "qnetworkaccessdatabackend_p.h"
|
---|
57 | #include "qnetworkaccessdebugpipebackend_p.h"
|
---|
58 | #include "qnetworkaccesscachebackend_p.h"
|
---|
59 | #include "qfilenetworkreply_p.h"
|
---|
60 |
|
---|
61 | #include "QtCore/qbuffer.h"
|
---|
62 | #include "QtCore/qurl.h"
|
---|
63 | #include "QtCore/qvector.h"
|
---|
64 | #include "QtNetwork/qauthenticator.h"
|
---|
65 | #include "QtNetwork/qsslconfiguration.h"
|
---|
66 | #include "QtNetwork/qnetworkconfigmanager.h"
|
---|
67 |
|
---|
68 | QT_BEGIN_NAMESPACE
|
---|
69 |
|
---|
70 | #ifndef QT_NO_HTTP
|
---|
71 | Q_GLOBAL_STATIC(QNetworkAccessHttpBackendFactory, httpBackend)
|
---|
72 | #endif // QT_NO_HTTP
|
---|
73 | Q_GLOBAL_STATIC(QNetworkAccessFileBackendFactory, fileBackend)
|
---|
74 | Q_GLOBAL_STATIC(QNetworkAccessDataBackendFactory, dataBackend)
|
---|
75 | #ifndef QT_NO_FTP
|
---|
76 | Q_GLOBAL_STATIC(QNetworkAccessFtpBackendFactory, ftpBackend)
|
---|
77 | #endif // QT_NO_FTP
|
---|
78 |
|
---|
79 | #ifdef QT_BUILD_INTERNAL
|
---|
80 | Q_GLOBAL_STATIC(QNetworkAccessDebugPipeBackendFactory, debugpipeBackend)
|
---|
81 | #endif
|
---|
82 |
|
---|
83 | static void ensureInitialized()
|
---|
84 | {
|
---|
85 | #ifndef QT_NO_HTTP
|
---|
86 | (void) httpBackend();
|
---|
87 | #endif // QT_NO_HTTP
|
---|
88 | (void) dataBackend();
|
---|
89 | #ifndef QT_NO_FTP
|
---|
90 | (void) ftpBackend();
|
---|
91 | #endif
|
---|
92 |
|
---|
93 | #ifdef QT_BUILD_INTERNAL
|
---|
94 | (void) debugpipeBackend();
|
---|
95 | #endif
|
---|
96 |
|
---|
97 | // leave this one last since it will query the special QAbstractFileEngines
|
---|
98 | (void) fileBackend();
|
---|
99 | }
|
---|
100 |
|
---|
101 | /*!
|
---|
102 | \class QNetworkAccessManager
|
---|
103 | \brief The QNetworkAccessManager class allows the application to
|
---|
104 | send network requests and receive replies
|
---|
105 | \since 4.4
|
---|
106 |
|
---|
107 | \ingroup network
|
---|
108 | \inmodule QtNetwork
|
---|
109 | \reentrant
|
---|
110 |
|
---|
111 | The Network Access API is constructed around one QNetworkAccessManager
|
---|
112 | object, which holds the common configuration and settings for the requests
|
---|
113 | it sends. It contains the proxy and cache configuration, as well as the
|
---|
114 | signals related to such issues, and reply signals that can be used to
|
---|
115 | monitor the progress of a network operation. One QNetworkAccessManager
|
---|
116 | should be enough for the whole Qt application.
|
---|
117 |
|
---|
118 | Once a QNetworkAccessManager object has been created, the application can
|
---|
119 | use it to send requests over the network. A group of standard functions
|
---|
120 | are supplied that take a request and optional data, and each return a
|
---|
121 | QNetworkReply object. The returned object is used to obtain any data
|
---|
122 | returned in response to the corresponding request.
|
---|
123 |
|
---|
124 | A simple download off the network could be accomplished with:
|
---|
125 | \snippet doc/src/snippets/code/src_network_access_qnetworkaccessmanager.cpp 0
|
---|
126 |
|
---|
127 | QNetworkAccessManager has an asynchronous API.
|
---|
128 | When the \tt replyFinished slot above is called, the parameter it
|
---|
129 | takes is the QNetworkReply object containing the downloaded data
|
---|
130 | as well as meta-data (headers, etc.).
|
---|
131 |
|
---|
132 | \note After the request has finished, it is the responsibility of the user
|
---|
133 | to delete the QNetworkReply object at an appropriate time. Do not directly
|
---|
134 | delete it inside the slot connected to finished(). You can use the
|
---|
135 | deleteLater() function.
|
---|
136 |
|
---|
137 | \note QNetworkAccessManager queues the requests it receives. The number
|
---|
138 | of requests executed in parallel is dependent on the protocol.
|
---|
139 | Currently, for the HTTP protocol on desktop platforms, 6 requests are
|
---|
140 | executed in parallel for one host/port combination.
|
---|
141 |
|
---|
142 | A more involved example, assuming the manager is already existent,
|
---|
143 | can be:
|
---|
144 | \snippet doc/src/snippets/code/src_network_access_qnetworkaccessmanager.cpp 1
|
---|
145 |
|
---|
146 | \section1 Network and Roaming support
|
---|
147 |
|
---|
148 | With the addition of the \l {Bearer Management} API to Qt 4.7
|
---|
149 | QNetworkAccessManager gained the ability to manage network connections.
|
---|
150 | QNetworkAccessManager can start the network interface if the device is
|
---|
151 | offline and terminates the interface if the current process is the last
|
---|
152 | one to use the uplink. Note that some platform utilize grace periods from
|
---|
153 | when the last application stops using a uplink until the system actually
|
---|
154 | terminates the connectivity link. Roaming is equally transparent. Any
|
---|
155 | queued/pending network requests are automatically transferred to new
|
---|
156 | access point.
|
---|
157 |
|
---|
158 | Clients wanting to utilize this feature should not require any changes. In fact
|
---|
159 | it is likely that existing platform specific connection code can simply be
|
---|
160 | removed from the application.
|
---|
161 |
|
---|
162 | \note The network and roaming support in QNetworkAccessManager is conditional
|
---|
163 | upon the platform supporting connection management. The
|
---|
164 | \l QNetworkConfigurationManager::NetworkSessionRequired can be used to
|
---|
165 | detect whether QNetworkAccessManager utilizes this feature. Currently only
|
---|
166 | Meego/Harmattan and Symbian platforms provide connection management support.
|
---|
167 |
|
---|
168 | \note This feature cannot be used in combination with the Bearer Management
|
---|
169 | API as provided by QtMobility. Applications have to migrate to the Qt version
|
---|
170 | of Bearer Management.
|
---|
171 |
|
---|
172 | \section1 Symbian Platform Security Requirements
|
---|
173 |
|
---|
174 | On Symbian, processes which use this class must have the
|
---|
175 | \c NetworkServices platform security capability. If the client
|
---|
176 | process lacks this capability, operations will result in a panic.
|
---|
177 |
|
---|
178 | Platform security capabilities are added via the
|
---|
179 | \l{qmake-variable-reference.html#target-capability}{TARGET.CAPABILITY}
|
---|
180 | qmake variable.
|
---|
181 |
|
---|
182 | \sa QNetworkRequest, QNetworkReply, QNetworkProxy
|
---|
183 | */
|
---|
184 |
|
---|
185 | /*!
|
---|
186 | \enum QNetworkAccessManager::Operation
|
---|
187 |
|
---|
188 | Indicates the operation this reply is processing.
|
---|
189 |
|
---|
190 | \value HeadOperation retrieve headers operation (created
|
---|
191 | with head())
|
---|
192 |
|
---|
193 | \value GetOperation retrieve headers and download contents
|
---|
194 | (created with get())
|
---|
195 |
|
---|
196 | \value PutOperation upload contents operation (created
|
---|
197 | with put())
|
---|
198 |
|
---|
199 | \value PostOperation send the contents of an HTML form for
|
---|
200 | processing via HTTP POST (created with post())
|
---|
201 |
|
---|
202 | \value DeleteOperation delete contents operation (created with
|
---|
203 | deleteResource())
|
---|
204 |
|
---|
205 | \value CustomOperation custom operation (created with
|
---|
206 | sendCustomRequest()) \since 4.7
|
---|
207 |
|
---|
208 | \omitvalue UnknownOperation
|
---|
209 |
|
---|
210 | \sa QNetworkReply::operation()
|
---|
211 | */
|
---|
212 |
|
---|
213 | /*!
|
---|
214 | \enum QNetworkAccessManager::NetworkAccessibility
|
---|
215 |
|
---|
216 | Indicates whether the network is accessible via this network access manager.
|
---|
217 |
|
---|
218 | \value UnknownAccessibility The network accessibility cannot be determined.
|
---|
219 | \value NotAccessible The network is not currently accessible, either because there
|
---|
220 | is currently no network coverage or network access has been
|
---|
221 | explicitly disabled by a call to setNetworkAccessible().
|
---|
222 | \value Accessible The network is accessible.
|
---|
223 |
|
---|
224 | \sa networkAccessible
|
---|
225 | */
|
---|
226 |
|
---|
227 | /*!
|
---|
228 | \property QNetworkAccessManager::networkAccessible
|
---|
229 | \brief whether the network is currently accessible via this network access manager.
|
---|
230 |
|
---|
231 | \since 4.7
|
---|
232 |
|
---|
233 | If the network is \l {NotAccessible}{not accessible} the network access manager will not
|
---|
234 | process any new network requests, all such requests will fail with an error. Requests with
|
---|
235 | URLs with the file:// scheme will still be processed.
|
---|
236 |
|
---|
237 | By default the value of this property reflects the physical state of the device. Applications
|
---|
238 | may override it to disable all network requests via this network access manager by calling
|
---|
239 |
|
---|
240 | \snippet doc/src/snippets/code/src_network_access_qnetworkaccessmanager.cpp 4
|
---|
241 |
|
---|
242 | Network requests can be reenabled again by calling
|
---|
243 |
|
---|
244 | \snippet doc/src/snippets/code/src_network_access_qnetworkaccessmanager.cpp 5
|
---|
245 |
|
---|
246 | \note Calling setNetworkAccessible() does not change the network state.
|
---|
247 | */
|
---|
248 |
|
---|
249 | /*!
|
---|
250 | \fn void QNetworkAccessManager::networkAccessibleChanged(QNetworkAccessManager::NetworkAccessibility accessible)
|
---|
251 |
|
---|
252 | This signal is emitted when the value of the \l networkAccessible property changes.
|
---|
253 | \a accessible is the new network accessibility.
|
---|
254 | */
|
---|
255 |
|
---|
256 | /*!
|
---|
257 | \fn void QNetworkAccessManager::networkSessionConnected()
|
---|
258 |
|
---|
259 | \since 4.7
|
---|
260 |
|
---|
261 | \internal
|
---|
262 |
|
---|
263 | This signal is emitted when the status of the network session changes into a usable (Connected)
|
---|
264 | state. It is used to signal to QNetworkReplys to start or migrate their network operation once
|
---|
265 | the network session has been opened or finished roaming.
|
---|
266 | */
|
---|
267 |
|
---|
268 | /*!
|
---|
269 | \fn void QNetworkAccessManager::proxyAuthenticationRequired(const QNetworkProxy &proxy, QAuthenticator *authenticator)
|
---|
270 |
|
---|
271 | This signal is emitted whenever a proxy requests authentication
|
---|
272 | and QNetworkAccessManager cannot find a valid, cached
|
---|
273 | credential. The slot connected to this signal should fill in the
|
---|
274 | credentials for the proxy \a proxy in the \a authenticator object.
|
---|
275 |
|
---|
276 | QNetworkAccessManager will cache the credentials internally. The
|
---|
277 | next time the proxy requests authentication, QNetworkAccessManager
|
---|
278 | will automatically send the same credential without emitting the
|
---|
279 | proxyAuthenticationRequired signal again.
|
---|
280 |
|
---|
281 | If the proxy rejects the credentials, QNetworkAccessManager will
|
---|
282 | emit the signal again.
|
---|
283 |
|
---|
284 | \sa proxy(), setProxy(), authenticationRequired()
|
---|
285 | */
|
---|
286 |
|
---|
287 | /*!
|
---|
288 | \fn void QNetworkAccessManager::authenticationRequired(QNetworkReply *reply, QAuthenticator *authenticator)
|
---|
289 |
|
---|
290 | This signal is emitted whenever a final server requests
|
---|
291 | authentication before it delivers the requested contents. The slot
|
---|
292 | connected to this signal should fill the credentials for the
|
---|
293 | contents (which can be determined by inspecting the \a reply
|
---|
294 | object) in the \a authenticator object.
|
---|
295 |
|
---|
296 | QNetworkAccessManager will cache the credentials internally and
|
---|
297 | will send the same values if the server requires authentication
|
---|
298 | again, without emitting the authenticationRequired() signal. If it
|
---|
299 | rejects the credentials, this signal will be emitted again.
|
---|
300 |
|
---|
301 | \sa proxyAuthenticationRequired()
|
---|
302 | */
|
---|
303 |
|
---|
304 | /*!
|
---|
305 | \fn void QNetworkAccessManager::finished(QNetworkReply *reply)
|
---|
306 |
|
---|
307 | This signal is emitted whenever a pending network reply is
|
---|
308 | finished. The \a reply parameter will contain a pointer to the
|
---|
309 | reply that has just finished. This signal is emitted in tandem
|
---|
310 | with the QNetworkReply::finished() signal.
|
---|
311 |
|
---|
312 | See QNetworkReply::finished() for information on the status that
|
---|
313 | the object will be in.
|
---|
314 |
|
---|
315 | \note Do not delete the \a reply object in the slot connected to this
|
---|
316 | signal. Use deleteLater().
|
---|
317 |
|
---|
318 | \sa QNetworkReply::finished(), QNetworkReply::error()
|
---|
319 | */
|
---|
320 |
|
---|
321 | /*!
|
---|
322 | \fn void QNetworkAccessManager::sslErrors(QNetworkReply *reply, const QList<QSslError> &errors)
|
---|
323 |
|
---|
324 | This signal is emitted if the SSL/TLS session encountered errors
|
---|
325 | during the set up, including certificate verification errors. The
|
---|
326 | \a errors parameter contains the list of errors and \a reply is
|
---|
327 | the QNetworkReply that is encountering these errors.
|
---|
328 |
|
---|
329 | To indicate that the errors are not fatal and that the connection
|
---|
330 | should proceed, the QNetworkReply::ignoreSslErrors() function should be called
|
---|
331 | from the slot connected to this signal. If it is not called, the
|
---|
332 | SSL session will be torn down before any data is exchanged
|
---|
333 | (including the URL).
|
---|
334 |
|
---|
335 | This signal can be used to display an error message to the user
|
---|
336 | indicating that security may be compromised and display the
|
---|
337 | SSL settings (see sslConfiguration() to obtain it). If the user
|
---|
338 | decides to proceed after analyzing the remote certificate, the
|
---|
339 | slot should call ignoreSslErrors().
|
---|
340 |
|
---|
341 | \sa QSslSocket::sslErrors(), QNetworkReply::sslErrors(),
|
---|
342 | QNetworkReply::sslConfiguration(), QNetworkReply::ignoreSslErrors()
|
---|
343 | */
|
---|
344 |
|
---|
345 | class QNetworkAuthenticationCredential
|
---|
346 | {
|
---|
347 | public:
|
---|
348 | QString domain;
|
---|
349 | QString user;
|
---|
350 | QString password;
|
---|
351 | };
|
---|
352 | Q_DECLARE_TYPEINFO(QNetworkAuthenticationCredential, Q_MOVABLE_TYPE);
|
---|
353 | inline bool operator<(const QNetworkAuthenticationCredential &t1, const QString &t2)
|
---|
354 | { return t1.domain < t2; }
|
---|
355 |
|
---|
356 | class QNetworkAuthenticationCache: private QVector<QNetworkAuthenticationCredential>,
|
---|
357 | public QNetworkAccessCache::CacheableObject
|
---|
358 | {
|
---|
359 | public:
|
---|
360 | QNetworkAuthenticationCache()
|
---|
361 | {
|
---|
362 | setExpires(false);
|
---|
363 | setShareable(true);
|
---|
364 | reserve(1);
|
---|
365 | }
|
---|
366 |
|
---|
367 | QNetworkAuthenticationCredential *findClosestMatch(const QString &domain)
|
---|
368 | {
|
---|
369 | iterator it = qLowerBound(begin(), end(), domain);
|
---|
370 | if (it == end() && !isEmpty())
|
---|
371 | --it;
|
---|
372 | if (it == end() || !domain.startsWith(it->domain))
|
---|
373 | return 0;
|
---|
374 | return &*it;
|
---|
375 | }
|
---|
376 |
|
---|
377 | void insert(const QString &domain, const QString &user, const QString &password)
|
---|
378 | {
|
---|
379 | QNetworkAuthenticationCredential *closestMatch = findClosestMatch(domain);
|
---|
380 | if (closestMatch && closestMatch->domain == domain) {
|
---|
381 | // we're overriding the current credentials
|
---|
382 | closestMatch->user = user;
|
---|
383 | closestMatch->password = password;
|
---|
384 | } else {
|
---|
385 | QNetworkAuthenticationCredential newCredential;
|
---|
386 | newCredential.domain = domain;
|
---|
387 | newCredential.user = user;
|
---|
388 | newCredential.password = password;
|
---|
389 |
|
---|
390 | if (closestMatch)
|
---|
391 | QVector<QNetworkAuthenticationCredential>::insert(++closestMatch, newCredential);
|
---|
392 | else
|
---|
393 | QVector<QNetworkAuthenticationCredential>::insert(end(), newCredential);
|
---|
394 | }
|
---|
395 | }
|
---|
396 |
|
---|
397 | virtual void dispose() { delete this; }
|
---|
398 | };
|
---|
399 |
|
---|
400 | #ifndef QT_NO_NETWORKPROXY
|
---|
401 | static QByteArray proxyAuthenticationKey(const QNetworkProxy &proxy, const QString &realm)
|
---|
402 | {
|
---|
403 | QUrl key;
|
---|
404 |
|
---|
405 | switch (proxy.type()) {
|
---|
406 | case QNetworkProxy::Socks5Proxy:
|
---|
407 | key.setScheme(QLatin1String("proxy-socks5"));
|
---|
408 | break;
|
---|
409 |
|
---|
410 | case QNetworkProxy::HttpProxy:
|
---|
411 | case QNetworkProxy::HttpCachingProxy:
|
---|
412 | key.setScheme(QLatin1String("proxy-http"));
|
---|
413 | break;
|
---|
414 |
|
---|
415 | case QNetworkProxy::FtpCachingProxy:
|
---|
416 | key.setScheme(QLatin1String("proxy-ftp"));
|
---|
417 | break;
|
---|
418 |
|
---|
419 | case QNetworkProxy::DefaultProxy:
|
---|
420 | case QNetworkProxy::NoProxy:
|
---|
421 | // shouldn't happen
|
---|
422 | return QByteArray();
|
---|
423 |
|
---|
424 | // no default:
|
---|
425 | // let there be errors if a new proxy type is added in the future
|
---|
426 | }
|
---|
427 |
|
---|
428 | if (key.scheme().isEmpty())
|
---|
429 | // proxy type not handled
|
---|
430 | return QByteArray();
|
---|
431 |
|
---|
432 | key.setUserName(proxy.user());
|
---|
433 | key.setHost(proxy.hostName());
|
---|
434 | key.setPort(proxy.port());
|
---|
435 | key.setFragment(realm);
|
---|
436 | return "auth:" + key.toEncoded();
|
---|
437 | }
|
---|
438 | #endif
|
---|
439 |
|
---|
440 | static inline QByteArray authenticationKey(const QUrl &url, const QString &realm)
|
---|
441 | {
|
---|
442 | QUrl copy = url;
|
---|
443 | copy.setFragment(realm);
|
---|
444 | return "auth:" + copy.toEncoded(QUrl::RemovePassword | QUrl::RemovePath | QUrl::RemoveQuery);
|
---|
445 | }
|
---|
446 |
|
---|
447 | /*!
|
---|
448 | Constructs a QNetworkAccessManager object that is the center of
|
---|
449 | the Network Access API and sets \a parent as the parent object.
|
---|
450 | */
|
---|
451 | QNetworkAccessManager::QNetworkAccessManager(QObject *parent)
|
---|
452 | : QObject(*new QNetworkAccessManagerPrivate, parent)
|
---|
453 | {
|
---|
454 | ensureInitialized();
|
---|
455 | }
|
---|
456 |
|
---|
457 | /*!
|
---|
458 | Destroys the QNetworkAccessManager object and frees up any
|
---|
459 | resources. Note that QNetworkReply objects that are returned from
|
---|
460 | this class have this object set as their parents, which means that
|
---|
461 | they will be deleted along with it if you don't call
|
---|
462 | QObject::setParent() on them.
|
---|
463 | */
|
---|
464 | QNetworkAccessManager::~QNetworkAccessManager()
|
---|
465 | {
|
---|
466 | #ifndef QT_NO_NETWORKPROXY
|
---|
467 | delete d_func()->proxyFactory;
|
---|
468 | #endif
|
---|
469 |
|
---|
470 | // Delete the QNetworkReply children first.
|
---|
471 | // Else a QAbstractNetworkCache might get deleted in ~QObject
|
---|
472 | // before a QNetworkReply that accesses the QAbstractNetworkCache
|
---|
473 | // object in its destructor.
|
---|
474 | qDeleteAll(findChildren<QNetworkReply *>());
|
---|
475 | // The other children will be deleted in this ~QObject
|
---|
476 | // FIXME instead of this "hack" make the QNetworkReplyImpl
|
---|
477 | // properly watch the cache deletion, e.g. via a QWeakPointer.
|
---|
478 | }
|
---|
479 |
|
---|
480 | #ifndef QT_NO_NETWORKPROXY
|
---|
481 | /*!
|
---|
482 | Returns the QNetworkProxy that the requests sent using this
|
---|
483 | QNetworkAccessManager object will use. The default value for the
|
---|
484 | proxy is QNetworkProxy::DefaultProxy.
|
---|
485 |
|
---|
486 | \sa setProxy(), setProxyFactory(), proxyAuthenticationRequired()
|
---|
487 | */
|
---|
488 | QNetworkProxy QNetworkAccessManager::proxy() const
|
---|
489 | {
|
---|
490 | return d_func()->proxy;
|
---|
491 | }
|
---|
492 |
|
---|
493 | /*!
|
---|
494 | Sets the proxy to be used in future requests to be \a proxy. This
|
---|
495 | does not affect requests that have already been sent. The
|
---|
496 | proxyAuthenticationRequired() signal will be emitted if the proxy
|
---|
497 | requests authentication.
|
---|
498 |
|
---|
499 | A proxy set with this function will be used for all requests
|
---|
500 | issued by QNetworkAccessManager. In some cases, it might be
|
---|
501 | necessary to select different proxies depending on the type of
|
---|
502 | request being sent or the destination host. If that's the case,
|
---|
503 | you should consider using setProxyFactory().
|
---|
504 |
|
---|
505 | \sa proxy(), proxyAuthenticationRequired()
|
---|
506 | */
|
---|
507 | void QNetworkAccessManager::setProxy(const QNetworkProxy &proxy)
|
---|
508 | {
|
---|
509 | Q_D(QNetworkAccessManager);
|
---|
510 | delete d->proxyFactory;
|
---|
511 | d->proxy = proxy;
|
---|
512 | d->proxyFactory = 0;
|
---|
513 | }
|
---|
514 |
|
---|
515 | /*!
|
---|
516 | \fn QNetworkProxyFactory *QNetworkAccessManager::proxyFactory() const
|
---|
517 | \since 4.5
|
---|
518 |
|
---|
519 | Returns the proxy factory that this QNetworkAccessManager object
|
---|
520 | is using to determine the proxies to be used for requests.
|
---|
521 |
|
---|
522 | Note that the pointer returned by this function is managed by
|
---|
523 | QNetworkAccessManager and could be deleted at any time.
|
---|
524 |
|
---|
525 | \sa setProxyFactory(), proxy()
|
---|
526 | */
|
---|
527 | QNetworkProxyFactory *QNetworkAccessManager::proxyFactory() const
|
---|
528 | {
|
---|
529 | return d_func()->proxyFactory;
|
---|
530 | }
|
---|
531 |
|
---|
532 | /*!
|
---|
533 | \since 4.5
|
---|
534 |
|
---|
535 | Sets the proxy factory for this class to be \a factory. A proxy
|
---|
536 | factory is used to determine a more specific list of proxies to be
|
---|
537 | used for a given request, instead of trying to use the same proxy
|
---|
538 | value for all requests.
|
---|
539 |
|
---|
540 | All queries sent by QNetworkAccessManager will have type
|
---|
541 | QNetworkProxyQuery::UrlRequest.
|
---|
542 |
|
---|
543 | For example, a proxy factory could apply the following rules:
|
---|
544 | \list
|
---|
545 | \o if the target address is in the local network (for example,
|
---|
546 | if the hostname contains no dots or if it's an IP address in
|
---|
547 | the organization's range), return QNetworkProxy::NoProxy
|
---|
548 | \o if the request is FTP, return an FTP proxy
|
---|
549 | \o if the request is HTTP or HTTPS, then return an HTTP proxy
|
---|
550 | \o otherwise, return a SOCKSv5 proxy server
|
---|
551 | \endlist
|
---|
552 |
|
---|
553 | The lifetime of the object \a factory will be managed by
|
---|
554 | QNetworkAccessManager. It will delete the object when necessary.
|
---|
555 |
|
---|
556 | \note If a specific proxy is set with setProxy(), the factory will not
|
---|
557 | be used.
|
---|
558 |
|
---|
559 | \sa proxyFactory(), setProxy(), QNetworkProxyQuery
|
---|
560 | */
|
---|
561 | void QNetworkAccessManager::setProxyFactory(QNetworkProxyFactory *factory)
|
---|
562 | {
|
---|
563 | Q_D(QNetworkAccessManager);
|
---|
564 | delete d->proxyFactory;
|
---|
565 | d->proxyFactory = factory;
|
---|
566 | d->proxy = QNetworkProxy();
|
---|
567 | }
|
---|
568 | #endif
|
---|
569 |
|
---|
570 | /*!
|
---|
571 | \since 4.5
|
---|
572 |
|
---|
573 | Returns the cache that is used to store data obtained from the network.
|
---|
574 |
|
---|
575 | \sa setCache()
|
---|
576 | */
|
---|
577 | QAbstractNetworkCache *QNetworkAccessManager::cache() const
|
---|
578 | {
|
---|
579 | Q_D(const QNetworkAccessManager);
|
---|
580 | return d->networkCache;
|
---|
581 | }
|
---|
582 |
|
---|
583 | /*!
|
---|
584 | \since 4.5
|
---|
585 |
|
---|
586 | Sets the manager's network cache to be the \a cache specified. The cache
|
---|
587 | is used for all requests dispatched by the manager.
|
---|
588 |
|
---|
589 | Use this function to set the network cache object to a class that implements
|
---|
590 | additional features, like saving the cookies to permanent storage.
|
---|
591 |
|
---|
592 | \note QNetworkAccessManager takes ownership of the \a cache object.
|
---|
593 |
|
---|
594 | QNetworkAccessManager by default does not have a set cache.
|
---|
595 | Qt provides a simple disk cache, QNetworkDiskCache, which can be used.
|
---|
596 |
|
---|
597 | \sa cache(), QNetworkRequest::CacheLoadControl
|
---|
598 | */
|
---|
599 | void QNetworkAccessManager::setCache(QAbstractNetworkCache *cache)
|
---|
600 | {
|
---|
601 | Q_D(QNetworkAccessManager);
|
---|
602 | if (d->networkCache != cache) {
|
---|
603 | delete d->networkCache;
|
---|
604 | d->networkCache = cache;
|
---|
605 | if (d->networkCache)
|
---|
606 | d->networkCache->setParent(this);
|
---|
607 | }
|
---|
608 | }
|
---|
609 |
|
---|
610 | /*!
|
---|
611 | Returns the QNetworkCookieJar that is used to store cookies
|
---|
612 | obtained from the network as well as cookies that are about to be
|
---|
613 | sent.
|
---|
614 |
|
---|
615 | \sa setCookieJar()
|
---|
616 | */
|
---|
617 | QNetworkCookieJar *QNetworkAccessManager::cookieJar() const
|
---|
618 | {
|
---|
619 | Q_D(const QNetworkAccessManager);
|
---|
620 | if (!d->cookieJar)
|
---|
621 | d->createCookieJar();
|
---|
622 | return d->cookieJar;
|
---|
623 | }
|
---|
624 |
|
---|
625 | /*!
|
---|
626 | Sets the manager's cookie jar to be the \a cookieJar specified.
|
---|
627 | The cookie jar is used by all requests dispatched by the manager.
|
---|
628 |
|
---|
629 | Use this function to set the cookie jar object to a class that
|
---|
630 | implements additional features, like saving the cookies to permanent
|
---|
631 | storage.
|
---|
632 |
|
---|
633 | \note QNetworkAccessManager takes ownership of the \a cookieJar object.
|
---|
634 |
|
---|
635 | If \a cookieJar is in the same thread as this QNetworkAccessManager,
|
---|
636 | it will set the parent of the \a cookieJar
|
---|
637 | so that the cookie jar is deleted when this
|
---|
638 | object is deleted as well. If you want to share cookie jars
|
---|
639 | between different QNetworkAccessManager objects, you may want to
|
---|
640 | set the cookie jar's parent to 0 after calling this function.
|
---|
641 |
|
---|
642 | QNetworkAccessManager by default does not implement any cookie
|
---|
643 | policy of its own: it accepts all cookies sent by the server, as
|
---|
644 | long as they are well formed and meet the minimum security
|
---|
645 | requirements (cookie domain matches the request's and cookie path
|
---|
646 | matches the request's). In order to implement your own security
|
---|
647 | policy, override the QNetworkCookieJar::cookiesForUrl() and
|
---|
648 | QNetworkCookieJar::setCookiesFromUrl() virtual functions. Those
|
---|
649 | functions are called by QNetworkAccessManager when it detects a
|
---|
650 | new cookie.
|
---|
651 |
|
---|
652 | \sa cookieJar(), QNetworkCookieJar::cookiesForUrl(), QNetworkCookieJar::setCookiesFromUrl()
|
---|
653 | */
|
---|
654 | void QNetworkAccessManager::setCookieJar(QNetworkCookieJar *cookieJar)
|
---|
655 | {
|
---|
656 | Q_D(QNetworkAccessManager);
|
---|
657 | d->cookieJarCreated = true;
|
---|
658 | if (d->cookieJar != cookieJar) {
|
---|
659 | if (d->cookieJar && d->cookieJar->parent() == this)
|
---|
660 | delete d->cookieJar;
|
---|
661 | d->cookieJar = cookieJar;
|
---|
662 | if (thread() == cookieJar->thread())
|
---|
663 | d->cookieJar->setParent(this);
|
---|
664 | }
|
---|
665 | }
|
---|
666 |
|
---|
667 | /*!
|
---|
668 | Posts a request to obtain the network headers for \a request
|
---|
669 | and returns a new QNetworkReply object which will contain such headers.
|
---|
670 |
|
---|
671 | The function is named after the HTTP request associated (HEAD).
|
---|
672 | */
|
---|
673 | QNetworkReply *QNetworkAccessManager::head(const QNetworkRequest &request)
|
---|
674 | {
|
---|
675 | return d_func()->postProcess(createRequest(QNetworkAccessManager::HeadOperation, request));
|
---|
676 | }
|
---|
677 |
|
---|
678 | /*!
|
---|
679 | Posts a request to obtain the contents of the target \a request
|
---|
680 | and returns a new QNetworkReply object opened for reading which emits the
|
---|
681 | \l{QIODevice::readyRead()}{readyRead()} signal whenever new data
|
---|
682 | arrives.
|
---|
683 |
|
---|
684 | The contents as well as associated headers will be downloaded.
|
---|
685 |
|
---|
686 | \sa post(), put(), deleteResource(), sendCustomRequest()
|
---|
687 | */
|
---|
688 | QNetworkReply *QNetworkAccessManager::get(const QNetworkRequest &request)
|
---|
689 | {
|
---|
690 | return d_func()->postProcess(createRequest(QNetworkAccessManager::GetOperation, request));
|
---|
691 | }
|
---|
692 |
|
---|
693 | /*!
|
---|
694 | Sends an HTTP POST request to the destination specified by \a request
|
---|
695 | and returns a new QNetworkReply object opened for reading that will
|
---|
696 | contain the reply sent by the server. The contents of the \a data
|
---|
697 | device will be uploaded to the server.
|
---|
698 |
|
---|
699 | \a data must be open for reading and must remain valid until the
|
---|
700 | finished() signal is emitted for this reply.
|
---|
701 |
|
---|
702 | \note Sending a POST request on protocols other than HTTP and
|
---|
703 | HTTPS is undefined and will probably fail.
|
---|
704 |
|
---|
705 | \sa get(), put(), deleteResource(), sendCustomRequest()
|
---|
706 | */
|
---|
707 | QNetworkReply *QNetworkAccessManager::post(const QNetworkRequest &request, QIODevice *data)
|
---|
708 | {
|
---|
709 | return d_func()->postProcess(createRequest(QNetworkAccessManager::PostOperation, request, data));
|
---|
710 | }
|
---|
711 |
|
---|
712 | /*!
|
---|
713 | \overload
|
---|
714 |
|
---|
715 | Sends the contents of the \a data byte array to the destination
|
---|
716 | specified by \a request.
|
---|
717 | */
|
---|
718 | QNetworkReply *QNetworkAccessManager::post(const QNetworkRequest &request, const QByteArray &data)
|
---|
719 | {
|
---|
720 | QBuffer *buffer = new QBuffer;
|
---|
721 | buffer->setData(data);
|
---|
722 | buffer->open(QIODevice::ReadOnly);
|
---|
723 |
|
---|
724 | QNetworkReply *reply = post(request, buffer);
|
---|
725 | buffer->setParent(reply);
|
---|
726 | return reply;
|
---|
727 | }
|
---|
728 |
|
---|
729 | /*!
|
---|
730 | Uploads the contents of \a data to the destination \a request and
|
---|
731 | returnes a new QNetworkReply object that will be open for reply.
|
---|
732 |
|
---|
733 | \a data must be opened for reading when this function is called
|
---|
734 | and must remain valid until the finished() signal is emitted for
|
---|
735 | this reply.
|
---|
736 |
|
---|
737 | Whether anything will be available for reading from the returned
|
---|
738 | object is protocol dependent. For HTTP, the server may send a
|
---|
739 | small HTML page indicating the upload was successful (or not).
|
---|
740 | Other protocols will probably have content in their replies.
|
---|
741 |
|
---|
742 | \note For HTTP, this request will send a PUT request, which most servers
|
---|
743 | do not allow. Form upload mechanisms, including that of uploading
|
---|
744 | files through HTML forms, use the POST mechanism.
|
---|
745 |
|
---|
746 | \sa get(), post(), deleteResource(), sendCustomRequest()
|
---|
747 | */
|
---|
748 | QNetworkReply *QNetworkAccessManager::put(const QNetworkRequest &request, QIODevice *data)
|
---|
749 | {
|
---|
750 | return d_func()->postProcess(createRequest(QNetworkAccessManager::PutOperation, request, data));
|
---|
751 | }
|
---|
752 |
|
---|
753 | /*!
|
---|
754 | \overload
|
---|
755 | Sends the contents of the \a data byte array to the destination
|
---|
756 | specified by \a request.
|
---|
757 | */
|
---|
758 | QNetworkReply *QNetworkAccessManager::put(const QNetworkRequest &request, const QByteArray &data)
|
---|
759 | {
|
---|
760 | QBuffer *buffer = new QBuffer;
|
---|
761 | buffer->setData(data);
|
---|
762 | buffer->open(QIODevice::ReadOnly);
|
---|
763 |
|
---|
764 | QNetworkReply *reply = put(request, buffer);
|
---|
765 | buffer->setParent(reply);
|
---|
766 | return reply;
|
---|
767 | }
|
---|
768 |
|
---|
769 | /*!
|
---|
770 | \since 4.6
|
---|
771 |
|
---|
772 | Sends a request to delete the resource identified by the URL of \a request.
|
---|
773 |
|
---|
774 | \note This feature is currently available for HTTP only, performing an
|
---|
775 | HTTP DELETE request.
|
---|
776 |
|
---|
777 | \sa get(), post(), put(), sendCustomRequest()
|
---|
778 | */
|
---|
779 | QNetworkReply *QNetworkAccessManager::deleteResource(const QNetworkRequest &request)
|
---|
780 | {
|
---|
781 | return d_func()->postProcess(createRequest(QNetworkAccessManager::DeleteOperation, request));
|
---|
782 | }
|
---|
783 |
|
---|
784 | #ifndef QT_NO_BEARERMANAGEMENT
|
---|
785 |
|
---|
786 | /*!
|
---|
787 | \since 4.7
|
---|
788 |
|
---|
789 | Sets the network configuration that will be used when creating the
|
---|
790 | \l {QNetworkSession}{network session} to \a config.
|
---|
791 |
|
---|
792 | The network configuration is used to create and open a network session before any request that
|
---|
793 | requires network access is process. If no network configuration is explicitly set via this
|
---|
794 | function the network configuration returned by
|
---|
795 | QNetworkConfigurationManager::defaultConfiguration() will be used.
|
---|
796 |
|
---|
797 | To restore the default network configuration set the network configuration to the value
|
---|
798 | returned from QNetworkConfigurationManager::defaultConfiguration().
|
---|
799 |
|
---|
800 | \snippet doc/src/snippets/code/src_network_access_qnetworkaccessmanager.cpp 2
|
---|
801 |
|
---|
802 | If an invalid network configuration is set, a network session will not be created. In this
|
---|
803 | case network requests will be processed regardless, but may fail. For example:
|
---|
804 |
|
---|
805 | \snippet doc/src/snippets/code/src_network_access_qnetworkaccessmanager.cpp 3
|
---|
806 |
|
---|
807 | \sa configuration(), QNetworkSession
|
---|
808 | */
|
---|
809 | void QNetworkAccessManager::setConfiguration(const QNetworkConfiguration &config)
|
---|
810 | {
|
---|
811 | d_func()->createSession(config);
|
---|
812 | }
|
---|
813 |
|
---|
814 | /*!
|
---|
815 | \since 4.7
|
---|
816 |
|
---|
817 | Returns the network configuration that will be used to create the
|
---|
818 | \l {QNetworkSession}{network session} which will be used when processing network requests.
|
---|
819 |
|
---|
820 | \sa setConfiguration(), activeConfiguration()
|
---|
821 | */
|
---|
822 | QNetworkConfiguration QNetworkAccessManager::configuration() const
|
---|
823 | {
|
---|
824 | Q_D(const QNetworkAccessManager);
|
---|
825 |
|
---|
826 | if (d->networkSession)
|
---|
827 | return d->networkSession->configuration();
|
---|
828 | else
|
---|
829 | return QNetworkConfiguration();
|
---|
830 | }
|
---|
831 |
|
---|
832 | /*!
|
---|
833 | \since 4.7
|
---|
834 |
|
---|
835 | Returns the current active network configuration.
|
---|
836 |
|
---|
837 | If the network configuration returned by configuration() is of type
|
---|
838 | QNetworkConfiguration::ServiceNetwork this function will return the current active child
|
---|
839 | network configuration of that configuration. Otherwise returns the same network configuration
|
---|
840 | as configuration().
|
---|
841 |
|
---|
842 | Use this function to return the actual network configuration currently in use by the network
|
---|
843 | session.
|
---|
844 |
|
---|
845 | \sa configuration()
|
---|
846 | */
|
---|
847 | QNetworkConfiguration QNetworkAccessManager::activeConfiguration() const
|
---|
848 | {
|
---|
849 | Q_D(const QNetworkAccessManager);
|
---|
850 |
|
---|
851 | if (d->networkSession) {
|
---|
852 | QNetworkConfigurationManager manager;
|
---|
853 |
|
---|
854 | return manager.configurationFromIdentifier(
|
---|
855 | d->networkSession->sessionProperty(QLatin1String("ActiveConfiguration")).toString());
|
---|
856 | } else {
|
---|
857 | return QNetworkConfiguration();
|
---|
858 | }
|
---|
859 | }
|
---|
860 |
|
---|
861 | /*!
|
---|
862 | \since 4.7
|
---|
863 |
|
---|
864 | Overrides the reported network accessibility. If \a accessible is NotAccessible the reported
|
---|
865 | network accessiblity will always be NotAccessible. Otherwise the reported network
|
---|
866 | accessibility will reflect the actual device state.
|
---|
867 | */
|
---|
868 | void QNetworkAccessManager::setNetworkAccessible(QNetworkAccessManager::NetworkAccessibility accessible)
|
---|
869 | {
|
---|
870 | Q_D(QNetworkAccessManager);
|
---|
871 |
|
---|
872 | if (d->networkAccessible != accessible) {
|
---|
873 | NetworkAccessibility previous = networkAccessible();
|
---|
874 | d->networkAccessible = accessible;
|
---|
875 | NetworkAccessibility current = networkAccessible();
|
---|
876 | if (previous != current)
|
---|
877 | emit networkAccessibleChanged(current);
|
---|
878 | }
|
---|
879 | }
|
---|
880 |
|
---|
881 | /*!
|
---|
882 | \since 4.7
|
---|
883 |
|
---|
884 | Returns the current network accessibility.
|
---|
885 | */
|
---|
886 | QNetworkAccessManager::NetworkAccessibility QNetworkAccessManager::networkAccessible() const
|
---|
887 | {
|
---|
888 | Q_D(const QNetworkAccessManager);
|
---|
889 |
|
---|
890 | if (d->networkSession) {
|
---|
891 | // d->online holds online/offline state of this network session.
|
---|
892 | if (d->online)
|
---|
893 | return d->networkAccessible;
|
---|
894 | else
|
---|
895 | return NotAccessible;
|
---|
896 | } else {
|
---|
897 | // Network accessibility is either disabled or unknown.
|
---|
898 | return (d->networkAccessible == NotAccessible) ? NotAccessible : UnknownAccessibility;
|
---|
899 | }
|
---|
900 | }
|
---|
901 |
|
---|
902 | #endif // QT_NO_BEARERMANAGEMENT
|
---|
903 |
|
---|
904 | /*!
|
---|
905 | \since 4.7
|
---|
906 |
|
---|
907 | Sends a custom request to the server identified by the URL of \a request.
|
---|
908 |
|
---|
909 | It is the user's responsibility to send a \a verb to the server that is valid
|
---|
910 | according to the HTTP specification.
|
---|
911 |
|
---|
912 | This method provides means to send verbs other than the common ones provided
|
---|
913 | via get() or post() etc., for instance sending an HTTP OPTIONS command.
|
---|
914 |
|
---|
915 | If \a data is not empty, the contents of the \a data
|
---|
916 | device will be uploaded to the server; in that case, data must be open for
|
---|
917 | reading and must remain valid until the finished() signal is emitted for this reply.
|
---|
918 |
|
---|
919 | \note This feature is currently available for HTTP only.
|
---|
920 |
|
---|
921 | \sa get(), post(), put(), deleteResource()
|
---|
922 | */
|
---|
923 | QNetworkReply *QNetworkAccessManager::sendCustomRequest(const QNetworkRequest &request, const QByteArray &verb, QIODevice *data)
|
---|
924 | {
|
---|
925 | QNetworkRequest newRequest(request);
|
---|
926 | newRequest.setAttribute(QNetworkRequest::CustomVerbAttribute, verb);
|
---|
927 | return d_func()->postProcess(createRequest(QNetworkAccessManager::CustomOperation, newRequest, data));
|
---|
928 | }
|
---|
929 |
|
---|
930 | /*!
|
---|
931 | Returns a new QNetworkReply object to handle the operation \a op
|
---|
932 | and request \a req. The device \a outgoingData is always 0 for Get and
|
---|
933 | Head requests, but is the value passed to post() and put() in
|
---|
934 | those operations (the QByteArray variants will pass a QBuffer
|
---|
935 | object).
|
---|
936 |
|
---|
937 | The default implementation calls QNetworkCookieJar::cookiesForUrl()
|
---|
938 | on the cookie jar set with setCookieJar() to obtain the cookies to
|
---|
939 | be sent to the remote server.
|
---|
940 |
|
---|
941 | The returned object must be in an open state.
|
---|
942 | */
|
---|
943 | QNetworkReply *QNetworkAccessManager::createRequest(QNetworkAccessManager::Operation op,
|
---|
944 | const QNetworkRequest &req,
|
---|
945 | QIODevice *outgoingData)
|
---|
946 | {
|
---|
947 | Q_D(QNetworkAccessManager);
|
---|
948 |
|
---|
949 | // 4.7 only hotfix fast path for data:// URLs
|
---|
950 | // In 4.8 this is solved with QNetworkReplyDataImpl and will work there
|
---|
951 | // This hotfix is done for not needing a QNetworkSession for data://
|
---|
952 | if ((op == QNetworkAccessManager::GetOperation || op == QNetworkAccessManager::HeadOperation)
|
---|
953 | && (req.url().scheme() == QLatin1String("data"))) {
|
---|
954 | QNetworkReplyImpl *reply = new QNetworkReplyImpl(this);
|
---|
955 | QNetworkReplyImplPrivate *priv = reply->d_func();
|
---|
956 | priv->manager = this;
|
---|
957 | priv->backend = new QNetworkAccessDataBackend();
|
---|
958 | priv->backend->manager = this->d_func();
|
---|
959 | priv->backend->setParent(reply);
|
---|
960 | priv->backend->reply = priv;
|
---|
961 | priv->setup(op, req, outgoingData);
|
---|
962 | return reply;
|
---|
963 | }
|
---|
964 |
|
---|
965 | // fast path for GET on file:// URLs
|
---|
966 | // Also if the scheme is empty we consider it a file.
|
---|
967 | // The QNetworkAccessFileBackend will right now only be used for PUT
|
---|
968 | if ((op == QNetworkAccessManager::GetOperation || op == QNetworkAccessManager::HeadOperation)
|
---|
969 | && (req.url().scheme() == QLatin1String("file")
|
---|
970 | || req.url().scheme() == QLatin1String("qrc")
|
---|
971 | || req.url().scheme().isEmpty())) {
|
---|
972 | return new QFileNetworkReply(this, req, op);
|
---|
973 | }
|
---|
974 |
|
---|
975 | // A request with QNetworkRequest::AlwaysCache does not need any bearer management
|
---|
976 | QNetworkRequest::CacheLoadControl mode =
|
---|
977 | static_cast<QNetworkRequest::CacheLoadControl>(
|
---|
978 | req.attribute(QNetworkRequest::CacheLoadControlAttribute,
|
---|
979 | QNetworkRequest::PreferNetwork).toInt());
|
---|
980 | if (mode == QNetworkRequest::AlwaysCache
|
---|
981 | && (op == QNetworkAccessManager::GetOperation
|
---|
982 | || op == QNetworkAccessManager::HeadOperation)) {
|
---|
983 | // FIXME Implement a QNetworkReplyCacheImpl instead, see QTBUG-15106
|
---|
984 | QNetworkReplyImpl *reply = new QNetworkReplyImpl(this);
|
---|
985 | QNetworkReplyImplPrivate *priv = reply->d_func();
|
---|
986 | priv->manager = this;
|
---|
987 | priv->backend = new QNetworkAccessCacheBackend();
|
---|
988 | priv->backend->manager = this->d_func();
|
---|
989 | priv->backend->setParent(reply);
|
---|
990 | priv->backend->reply = priv;
|
---|
991 | priv->setup(op, req, outgoingData);
|
---|
992 | return reply;
|
---|
993 | }
|
---|
994 |
|
---|
995 | #ifndef QT_NO_BEARERMANAGEMENT
|
---|
996 | // Return a disabled network reply if network access is disabled.
|
---|
997 | // Except if the scheme is empty or file://.
|
---|
998 | if (!d->networkAccessible && !(req.url().scheme() == QLatin1String("file") ||
|
---|
999 | req.url().scheme().isEmpty())) {
|
---|
1000 | return new QDisabledNetworkReply(this, req, op);
|
---|
1001 | }
|
---|
1002 |
|
---|
1003 | if (!d->networkSession && (d->initializeSession || !d->networkConfiguration.isEmpty())) {
|
---|
1004 | QNetworkConfigurationManager manager;
|
---|
1005 | if (!d->networkConfiguration.isEmpty()) {
|
---|
1006 | d->createSession(manager.configurationFromIdentifier(d->networkConfiguration));
|
---|
1007 | } else {
|
---|
1008 | if (manager.capabilities() & QNetworkConfigurationManager::NetworkSessionRequired)
|
---|
1009 | d->createSession(manager.defaultConfiguration());
|
---|
1010 | else
|
---|
1011 | d->initializeSession = false;
|
---|
1012 | }
|
---|
1013 | }
|
---|
1014 |
|
---|
1015 | if (d->networkSession)
|
---|
1016 | d->networkSession->setSessionProperty(QLatin1String("AutoCloseSessionTimeout"), -1);
|
---|
1017 | #endif
|
---|
1018 |
|
---|
1019 | QNetworkRequest request = req;
|
---|
1020 | if (!request.header(QNetworkRequest::ContentLengthHeader).isValid() &&
|
---|
1021 | outgoingData && !outgoingData->isSequential()) {
|
---|
1022 | // request has no Content-Length
|
---|
1023 | // but the data that is outgoing is random-access
|
---|
1024 | request.setHeader(QNetworkRequest::ContentLengthHeader, outgoingData->size());
|
---|
1025 | }
|
---|
1026 |
|
---|
1027 | if (static_cast<QNetworkRequest::LoadControl>
|
---|
1028 | (request.attribute(QNetworkRequest::CookieLoadControlAttribute,
|
---|
1029 | QNetworkRequest::Automatic).toInt()) == QNetworkRequest::Automatic) {
|
---|
1030 | if (d->cookieJar) {
|
---|
1031 | QList<QNetworkCookie> cookies = d->cookieJar->cookiesForUrl(request.url());
|
---|
1032 | if (!cookies.isEmpty())
|
---|
1033 | request.setHeader(QNetworkRequest::CookieHeader, qVariantFromValue(cookies));
|
---|
1034 | }
|
---|
1035 | }
|
---|
1036 |
|
---|
1037 | // first step: create the reply
|
---|
1038 | QUrl url = request.url();
|
---|
1039 | QNetworkReplyImpl *reply = new QNetworkReplyImpl(this);
|
---|
1040 | #ifndef QT_NO_BEARERMANAGEMENT
|
---|
1041 | if (req.url().scheme() != QLatin1String("file") && !req.url().scheme().isEmpty()) {
|
---|
1042 | connect(this, SIGNAL(networkSessionConnected()),
|
---|
1043 | reply, SLOT(_q_networkSessionConnected()));
|
---|
1044 | }
|
---|
1045 | #endif
|
---|
1046 | QNetworkReplyImplPrivate *priv = reply->d_func();
|
---|
1047 | priv->manager = this;
|
---|
1048 |
|
---|
1049 | // second step: fetch cached credentials
|
---|
1050 | // This is not done for the time being, we should use signal emissions to request
|
---|
1051 | // the credentials from cache.
|
---|
1052 |
|
---|
1053 | // third step: find a backend
|
---|
1054 | priv->backend = d->findBackend(op, request);
|
---|
1055 |
|
---|
1056 | #ifndef QT_NO_NETWORKPROXY
|
---|
1057 | QList<QNetworkProxy> proxyList = d->queryProxy(QNetworkProxyQuery(request.url()));
|
---|
1058 | priv->proxyList = proxyList;
|
---|
1059 | #endif
|
---|
1060 | if (priv->backend) {
|
---|
1061 | priv->backend->setParent(reply);
|
---|
1062 | priv->backend->reply = priv;
|
---|
1063 | }
|
---|
1064 |
|
---|
1065 | #ifndef QT_NO_OPENSSL
|
---|
1066 | reply->setSslConfiguration(request.sslConfiguration());
|
---|
1067 | #endif
|
---|
1068 |
|
---|
1069 | // fourth step: setup the reply
|
---|
1070 | priv->setup(op, request, outgoingData);
|
---|
1071 |
|
---|
1072 | return reply;
|
---|
1073 | }
|
---|
1074 |
|
---|
1075 | void QNetworkAccessManagerPrivate::_q_replyFinished()
|
---|
1076 | {
|
---|
1077 | Q_Q(QNetworkAccessManager);
|
---|
1078 |
|
---|
1079 | QNetworkReply *reply = qobject_cast<QNetworkReply *>(q->sender());
|
---|
1080 | if (reply)
|
---|
1081 | emit q->finished(reply);
|
---|
1082 |
|
---|
1083 | #ifndef QT_NO_BEARERMANAGEMENT
|
---|
1084 | if (networkSession && q->findChildren<QNetworkReply *>().count() == 1)
|
---|
1085 | networkSession->setSessionProperty(QLatin1String("AutoCloseSessionTimeout"), 120000);
|
---|
1086 | #endif
|
---|
1087 | }
|
---|
1088 |
|
---|
1089 | void QNetworkAccessManagerPrivate::_q_replySslErrors(const QList<QSslError> &errors)
|
---|
1090 | {
|
---|
1091 | #ifndef QT_NO_OPENSSL
|
---|
1092 | Q_Q(QNetworkAccessManager);
|
---|
1093 | QNetworkReply *reply = qobject_cast<QNetworkReply *>(q->sender());
|
---|
1094 | if (reply)
|
---|
1095 | emit q->sslErrors(reply, errors);
|
---|
1096 | #else
|
---|
1097 | Q_UNUSED(errors);
|
---|
1098 | #endif
|
---|
1099 | }
|
---|
1100 |
|
---|
1101 | QNetworkReply *QNetworkAccessManagerPrivate::postProcess(QNetworkReply *reply)
|
---|
1102 | {
|
---|
1103 | Q_Q(QNetworkAccessManager);
|
---|
1104 | QNetworkReplyPrivate::setManager(reply, q);
|
---|
1105 | q->connect(reply, SIGNAL(finished()), SLOT(_q_replyFinished()));
|
---|
1106 | #ifndef QT_NO_OPENSSL
|
---|
1107 | /* In case we're compiled without SSL support, we don't have this signal and we need to
|
---|
1108 | * avoid getting a connection error. */
|
---|
1109 | q->connect(reply, SIGNAL(sslErrors(QList<QSslError>)), SLOT(_q_replySslErrors(QList<QSslError>)));
|
---|
1110 | #endif
|
---|
1111 |
|
---|
1112 | return reply;
|
---|
1113 | }
|
---|
1114 |
|
---|
1115 | void QNetworkAccessManagerPrivate::createCookieJar() const
|
---|
1116 | {
|
---|
1117 | if (!cookieJarCreated) {
|
---|
1118 | // keep the ugly hack in here
|
---|
1119 | QNetworkAccessManagerPrivate *that = const_cast<QNetworkAccessManagerPrivate *>(this);
|
---|
1120 | that->cookieJar = new QNetworkCookieJar(that->q_func());
|
---|
1121 | that->cookieJarCreated = true;
|
---|
1122 | }
|
---|
1123 | }
|
---|
1124 |
|
---|
1125 | void QNetworkAccessManagerPrivate::authenticationRequired(QNetworkAccessBackend *backend,
|
---|
1126 | QAuthenticator *authenticator)
|
---|
1127 | {
|
---|
1128 | Q_Q(QNetworkAccessManager);
|
---|
1129 |
|
---|
1130 | // FIXME: Add support for domains (i.e., the leading path)
|
---|
1131 | QUrl url = backend->reply->url;
|
---|
1132 |
|
---|
1133 | // don't try the cache for the same URL twice in a row
|
---|
1134 | // being called twice for the same URL means the authentication failed
|
---|
1135 | // also called when last URL is empty, e.g. on first call
|
---|
1136 | if (backend->reply->urlForLastAuthentication.isEmpty()
|
---|
1137 | || url != backend->reply->urlForLastAuthentication) {
|
---|
1138 | QNetworkAuthenticationCredential *cred = fetchCachedCredentials(url, authenticator);
|
---|
1139 | if (cred) {
|
---|
1140 | authenticator->setUser(cred->user);
|
---|
1141 | authenticator->setPassword(cred->password);
|
---|
1142 | backend->reply->urlForLastAuthentication = url;
|
---|
1143 | return;
|
---|
1144 | }
|
---|
1145 | }
|
---|
1146 |
|
---|
1147 | // if we emit a signal here in synchronous mode, the user might spin
|
---|
1148 | // an event loop, which might recurse and lead to problems
|
---|
1149 | if (backend->isSynchronous())
|
---|
1150 | return;
|
---|
1151 |
|
---|
1152 | backend->reply->urlForLastAuthentication = url;
|
---|
1153 | emit q->authenticationRequired(backend->reply->q_func(), authenticator);
|
---|
1154 | cacheCredentials(url, authenticator);
|
---|
1155 | }
|
---|
1156 |
|
---|
1157 | #ifndef QT_NO_NETWORKPROXY
|
---|
1158 | void QNetworkAccessManagerPrivate::proxyAuthenticationRequired(QNetworkAccessBackend *backend,
|
---|
1159 | const QNetworkProxy &proxy,
|
---|
1160 | QAuthenticator *authenticator)
|
---|
1161 | {
|
---|
1162 | Q_Q(QNetworkAccessManager);
|
---|
1163 | // ### FIXME Tracking of successful authentications
|
---|
1164 | // This code is a bit broken right now for SOCKS authentication
|
---|
1165 | // first request: proxyAuthenticationRequired gets emitted, credentials gets saved
|
---|
1166 | // second request: (proxy != backend->reply->lastProxyAuthentication) does not evaluate to true,
|
---|
1167 | // proxyAuthenticationRequired gets emitted again
|
---|
1168 | // possible solution: some tracking inside the authenticator
|
---|
1169 | // or a new function proxyAuthenticationSucceeded(true|false)
|
---|
1170 | if (proxy != backend->reply->lastProxyAuthentication) {
|
---|
1171 | QNetworkAuthenticationCredential *cred = fetchCachedProxyCredentials(proxy);
|
---|
1172 | if (cred) {
|
---|
1173 | authenticator->setUser(cred->user);
|
---|
1174 | authenticator->setPassword(cred->password);
|
---|
1175 | return;
|
---|
1176 | }
|
---|
1177 | }
|
---|
1178 |
|
---|
1179 | // if we emit a signal here in synchronous mode, the user might spin
|
---|
1180 | // an event loop, which might recurse and lead to problems
|
---|
1181 | if (backend->isSynchronous())
|
---|
1182 | return;
|
---|
1183 |
|
---|
1184 | backend->reply->lastProxyAuthentication = proxy;
|
---|
1185 | emit q->proxyAuthenticationRequired(proxy, authenticator);
|
---|
1186 | cacheProxyCredentials(proxy, authenticator);
|
---|
1187 | }
|
---|
1188 |
|
---|
1189 | void QNetworkAccessManagerPrivate::cacheProxyCredentials(const QNetworkProxy &p,
|
---|
1190 | const QAuthenticator *authenticator)
|
---|
1191 | {
|
---|
1192 | Q_ASSERT(authenticator);
|
---|
1193 | Q_ASSERT(p.type() != QNetworkProxy::DefaultProxy);
|
---|
1194 | Q_ASSERT(p.type() != QNetworkProxy::NoProxy);
|
---|
1195 |
|
---|
1196 | QString realm = authenticator->realm();
|
---|
1197 | QNetworkProxy proxy = p;
|
---|
1198 | proxy.setUser(authenticator->user());
|
---|
1199 | // Set two credentials: one with the username and one without
|
---|
1200 | do {
|
---|
1201 | // Set two credentials actually: one with and one without the realm
|
---|
1202 | do {
|
---|
1203 | QByteArray cacheKey = proxyAuthenticationKey(proxy, realm);
|
---|
1204 | if (cacheKey.isEmpty())
|
---|
1205 | return; // should not happen
|
---|
1206 |
|
---|
1207 | QNetworkAuthenticationCache *auth = new QNetworkAuthenticationCache;
|
---|
1208 | auth->insert(QString(), authenticator->user(), authenticator->password());
|
---|
1209 | objectCache.addEntry(cacheKey, auth); // replace the existing one, if there's any
|
---|
1210 |
|
---|
1211 | if (realm.isEmpty()) {
|
---|
1212 | break;
|
---|
1213 | } else {
|
---|
1214 | realm.clear();
|
---|
1215 | }
|
---|
1216 | } while (true);
|
---|
1217 |
|
---|
1218 | if (proxy.user().isEmpty())
|
---|
1219 | break;
|
---|
1220 | else
|
---|
1221 | proxy.setUser(QString());
|
---|
1222 | } while (true);
|
---|
1223 | }
|
---|
1224 |
|
---|
1225 | QNetworkAuthenticationCredential *
|
---|
1226 | QNetworkAccessManagerPrivate::fetchCachedProxyCredentials(const QNetworkProxy &p,
|
---|
1227 | const QAuthenticator *authenticator)
|
---|
1228 | {
|
---|
1229 | QNetworkProxy proxy = p;
|
---|
1230 | if (proxy.type() == QNetworkProxy::DefaultProxy) {
|
---|
1231 | proxy = QNetworkProxy::applicationProxy();
|
---|
1232 | }
|
---|
1233 | if (!proxy.password().isEmpty())
|
---|
1234 | return 0; // no need to set credentials if it already has them
|
---|
1235 |
|
---|
1236 | QString realm;
|
---|
1237 | if (authenticator)
|
---|
1238 | realm = authenticator->realm();
|
---|
1239 |
|
---|
1240 | QByteArray cacheKey = proxyAuthenticationKey(proxy, realm);
|
---|
1241 | if (cacheKey.isEmpty())
|
---|
1242 | return 0;
|
---|
1243 | if (!objectCache.hasEntry(cacheKey))
|
---|
1244 | return 0;
|
---|
1245 |
|
---|
1246 | QNetworkAuthenticationCache *auth =
|
---|
1247 | static_cast<QNetworkAuthenticationCache *>(objectCache.requestEntryNow(cacheKey));
|
---|
1248 | QNetworkAuthenticationCredential *cred = auth->findClosestMatch(QString());
|
---|
1249 | objectCache.releaseEntry(cacheKey);
|
---|
1250 |
|
---|
1251 | // proxy cache credentials always have exactly one item
|
---|
1252 | Q_ASSERT_X(cred, "QNetworkAccessManager",
|
---|
1253 | "Internal inconsistency: found a cache key for a proxy, but it's empty");
|
---|
1254 | return cred;
|
---|
1255 | }
|
---|
1256 |
|
---|
1257 | QList<QNetworkProxy> QNetworkAccessManagerPrivate::queryProxy(const QNetworkProxyQuery &query)
|
---|
1258 | {
|
---|
1259 | QList<QNetworkProxy> proxies;
|
---|
1260 | if (proxyFactory) {
|
---|
1261 | proxies = proxyFactory->queryProxy(query);
|
---|
1262 | if (proxies.isEmpty()) {
|
---|
1263 | qWarning("QNetworkAccessManager: factory %p has returned an empty result set",
|
---|
1264 | proxyFactory);
|
---|
1265 | proxies << QNetworkProxy::NoProxy;
|
---|
1266 | }
|
---|
1267 | } else if (proxy.type() == QNetworkProxy::DefaultProxy) {
|
---|
1268 | // no proxy set, query the application
|
---|
1269 | return QNetworkProxyFactory::proxyForQuery(query);
|
---|
1270 | } else {
|
---|
1271 | proxies << proxy;
|
---|
1272 | }
|
---|
1273 |
|
---|
1274 | return proxies;
|
---|
1275 | }
|
---|
1276 | #endif
|
---|
1277 |
|
---|
1278 | void QNetworkAccessManagerPrivate::cacheCredentials(const QUrl &url,
|
---|
1279 | const QAuthenticator *authenticator)
|
---|
1280 | {
|
---|
1281 | Q_ASSERT(authenticator);
|
---|
1282 | QString domain = QString::fromLatin1("/"); // FIXME: make QAuthenticator return the domain
|
---|
1283 | QString realm = authenticator->realm();
|
---|
1284 |
|
---|
1285 | // Set two credentials actually: one with and one without the username in the URL
|
---|
1286 | QUrl copy = url;
|
---|
1287 | copy.setUserName(authenticator->user());
|
---|
1288 | do {
|
---|
1289 | QByteArray cacheKey = authenticationKey(copy, realm);
|
---|
1290 | if (objectCache.hasEntry(cacheKey)) {
|
---|
1291 | QNetworkAuthenticationCache *auth =
|
---|
1292 | static_cast<QNetworkAuthenticationCache *>(objectCache.requestEntryNow(cacheKey));
|
---|
1293 | auth->insert(domain, authenticator->user(), authenticator->password());
|
---|
1294 | objectCache.releaseEntry(cacheKey);
|
---|
1295 | } else {
|
---|
1296 | QNetworkAuthenticationCache *auth = new QNetworkAuthenticationCache;
|
---|
1297 | auth->insert(domain, authenticator->user(), authenticator->password());
|
---|
1298 | objectCache.addEntry(cacheKey, auth);
|
---|
1299 | }
|
---|
1300 |
|
---|
1301 | if (copy.userName().isEmpty()) {
|
---|
1302 | break;
|
---|
1303 | } else {
|
---|
1304 | copy.setUserName(QString());
|
---|
1305 | }
|
---|
1306 | } while (true);
|
---|
1307 | }
|
---|
1308 |
|
---|
1309 | /*!
|
---|
1310 | Fetch the credential data from the credential cache.
|
---|
1311 |
|
---|
1312 | If auth is 0 (as it is when called from createRequest()), this will try to
|
---|
1313 | look up with an empty realm. That fails in most cases for HTTP (because the
|
---|
1314 | realm is seldom empty for HTTP challenges). In any case, QHttpNetworkConnection
|
---|
1315 | never sends the credentials on the first attempt: it needs to find out what
|
---|
1316 | authentication methods the server supports.
|
---|
1317 |
|
---|
1318 | For FTP, realm is always empty.
|
---|
1319 | */
|
---|
1320 | QNetworkAuthenticationCredential *
|
---|
1321 | QNetworkAccessManagerPrivate::fetchCachedCredentials(const QUrl &url,
|
---|
1322 | const QAuthenticator *authentication)
|
---|
1323 | {
|
---|
1324 | if (!url.password().isEmpty())
|
---|
1325 | return 0; // no need to set credentials if it already has them
|
---|
1326 |
|
---|
1327 | QString realm;
|
---|
1328 | if (authentication)
|
---|
1329 | realm = authentication->realm();
|
---|
1330 |
|
---|
1331 | QByteArray cacheKey = authenticationKey(url, realm);
|
---|
1332 | if (!objectCache.hasEntry(cacheKey))
|
---|
1333 | return 0;
|
---|
1334 |
|
---|
1335 | QNetworkAuthenticationCache *auth =
|
---|
1336 | static_cast<QNetworkAuthenticationCache *>(objectCache.requestEntryNow(cacheKey));
|
---|
1337 | QNetworkAuthenticationCredential *cred = auth->findClosestMatch(url.path());
|
---|
1338 | objectCache.releaseEntry(cacheKey);
|
---|
1339 | return cred;
|
---|
1340 | }
|
---|
1341 |
|
---|
1342 | void QNetworkAccessManagerPrivate::clearCache(QNetworkAccessManager *manager)
|
---|
1343 | {
|
---|
1344 | manager->d_func()->objectCache.clear();
|
---|
1345 | }
|
---|
1346 |
|
---|
1347 | QNetworkAccessManagerPrivate::~QNetworkAccessManagerPrivate()
|
---|
1348 | {
|
---|
1349 | }
|
---|
1350 |
|
---|
1351 | #ifndef QT_NO_BEARERMANAGEMENT
|
---|
1352 | void QNetworkAccessManagerPrivate::createSession(const QNetworkConfiguration &config)
|
---|
1353 | {
|
---|
1354 | Q_Q(QNetworkAccessManager);
|
---|
1355 |
|
---|
1356 | initializeSession = false;
|
---|
1357 |
|
---|
1358 | if (!config.isValid()) {
|
---|
1359 | networkSession.clear();
|
---|
1360 | online = false;
|
---|
1361 |
|
---|
1362 | if (networkAccessible == QNetworkAccessManager::NotAccessible)
|
---|
1363 | emit q->networkAccessibleChanged(QNetworkAccessManager::NotAccessible);
|
---|
1364 | else
|
---|
1365 | emit q->networkAccessibleChanged(QNetworkAccessManager::UnknownAccessibility);
|
---|
1366 |
|
---|
1367 | return;
|
---|
1368 | }
|
---|
1369 |
|
---|
1370 | networkSession = QSharedNetworkSessionManager::getSession(config);
|
---|
1371 |
|
---|
1372 | QObject::connect(networkSession.data(), SIGNAL(opened()), q, SIGNAL(networkSessionConnected()), Qt::QueuedConnection);
|
---|
1373 | //QueuedConnection is used to avoid deleting the networkSession inside its closed signal
|
---|
1374 | QObject::connect(networkSession.data(), SIGNAL(closed()), q, SLOT(_q_networkSessionClosed()), Qt::QueuedConnection);
|
---|
1375 | QObject::connect(networkSession.data(), SIGNAL(stateChanged(QNetworkSession::State)),
|
---|
1376 | q, SLOT(_q_networkSessionStateChanged(QNetworkSession::State)), Qt::QueuedConnection);
|
---|
1377 |
|
---|
1378 | _q_networkSessionStateChanged(networkSession->state());
|
---|
1379 | }
|
---|
1380 |
|
---|
1381 | void QNetworkAccessManagerPrivate::_q_networkSessionClosed()
|
---|
1382 | {
|
---|
1383 | if (networkSession) {
|
---|
1384 | networkConfiguration = networkSession->configuration().identifier();
|
---|
1385 |
|
---|
1386 | networkSession.clear();
|
---|
1387 | }
|
---|
1388 | }
|
---|
1389 |
|
---|
1390 | void QNetworkAccessManagerPrivate::_q_networkSessionStateChanged(QNetworkSession::State state)
|
---|
1391 | {
|
---|
1392 | Q_Q(QNetworkAccessManager);
|
---|
1393 |
|
---|
1394 | if (state == QNetworkSession::Connected)
|
---|
1395 | emit q->networkSessionConnected();
|
---|
1396 | if (online) {
|
---|
1397 | if (state != QNetworkSession::Connected && state != QNetworkSession::Roaming) {
|
---|
1398 | online = false;
|
---|
1399 | emit q->networkAccessibleChanged(QNetworkAccessManager::NotAccessible);
|
---|
1400 | }
|
---|
1401 | } else {
|
---|
1402 | if (state == QNetworkSession::Connected || state == QNetworkSession::Roaming) {
|
---|
1403 | online = true;
|
---|
1404 | emit q->networkAccessibleChanged(networkAccessible);
|
---|
1405 | }
|
---|
1406 | }
|
---|
1407 | }
|
---|
1408 | #endif // QT_NO_BEARERMANAGEMENT
|
---|
1409 |
|
---|
1410 | QT_END_NAMESPACE
|
---|
1411 |
|
---|
1412 | #include "moc_qnetworkaccessmanager.cpp"
|
---|