Connect usagePoliciesChanged signal to the QNetworkReply
[profile/ivi/qtbase.git] / src / network / access / qnetworkreplyimpl.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
5 **
6 ** This file is part of the QtNetwork module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
16 **
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
20 **
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
28 **
29 ** Other Usage
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
32 **
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qnetworkreplyimpl_p.h"
43 #include "qnetworkaccessbackend_p.h"
44 #include "qnetworkcookie.h"
45 #include "qnetworkcookiejar.h"
46 #include "qabstractnetworkcache.h"
47 #include "QtCore/qcoreapplication.h"
48 #include "QtCore/qdatetime.h"
49 #include "QtNetwork/qsslconfiguration.h"
50 #include "QtNetwork/qnetworksession.h"
51 #include "qnetworkaccessmanager_p.h"
52
53 #include <QtCore/QCoreApplication>
54
55 Q_DECLARE_METATYPE(QSharedPointer<char>)
56
57 QT_BEGIN_NAMESPACE
58
59 inline QNetworkReplyImplPrivate::QNetworkReplyImplPrivate()
60     : backend(0), outgoingData(0),
61       copyDevice(0),
62       cacheEnabled(false), cacheSaveDevice(0),
63       notificationHandlingPaused(false),
64       bytesDownloaded(0), lastBytesDownloaded(-1), bytesUploaded(-1), preMigrationDownloaded(-1),
65       httpStatusCode(0),
66       state(Idle)
67       , downloadBufferReadPosition(0)
68       , downloadBufferCurrentSize(0)
69       , downloadBufferMaximumSize(0)
70       , downloadBuffer(0)
71 {
72 }
73
74 void QNetworkReplyImplPrivate::_q_startOperation()
75 {
76     // ensure this function is only being called once
77     if (state == Working || state == Finished) {
78         qDebug("QNetworkReplyImpl::_q_startOperation was called more than once");
79         return;
80     }
81     state = Working;
82
83     // note: if that method is called directly, it cannot happen that the backend is 0,
84     // because we just checked via a qobject_cast that we got a http backend (see
85     // QNetworkReplyImplPrivate::setup())
86     if (!backend) {
87         error(QNetworkReplyImpl::ProtocolUnknownError,
88               QCoreApplication::translate("QNetworkReply", "Protocol \"%1\" is unknown").arg(url.scheme())); // not really true!;
89         finished();
90         return;
91     }
92
93 #ifndef QT_NO_BEARERMANAGEMENT
94     // Do not start background requests if they are not allowed by session policy
95     QSharedPointer<QNetworkSession> session(manager->d_func()->networkSession);
96     QVariant isBackground = backend->request().attribute(QNetworkRequest::BackgroundRequestAttribute, QVariant::fromValue(false));
97     if (isBackground.toBool() && session && session->usagePolicies().testFlag(QNetworkSession::NoBackgroundTrafficPolicy)) {
98         error(QNetworkReply::BackgroundRequestNotAllowedError,
99             QCoreApplication::translate("QNetworkReply", "Background request not allowed."));
100         finished();
101         return;
102     }
103 #endif
104
105     if (!backend->start()) {
106 #ifndef QT_NO_BEARERMANAGEMENT
107         // backend failed to start because the session state is not Connected.
108         // QNetworkAccessManager will call _q_startOperation again for us when the session
109         // state changes.
110         state = WaitingForSession;
111
112         if (session) {
113             Q_Q(QNetworkReplyImpl);
114
115             QObject::connect(session.data(), SIGNAL(error(QNetworkSession::SessionError)),
116                              q, SLOT(_q_networkSessionFailed()));
117             QObject::connect(session.data(), SIGNAL(usagePoliciesChanged(QNetworkSession::UsagePolicies)),
118                              q, SLOT(_q_networkSessionUsagePoliciesChanged(QNetworkSession::UsagePolicies)));
119
120             if (!session->isOpen()) {
121                 session->setSessionProperty(QStringLiteral("ConnectInBackground"), isBackground);
122                 session->open();
123             }
124         } else {
125             qWarning("Backend is waiting for QNetworkSession to connect, but there is none!");
126             state = Working;
127             error(QNetworkReplyImpl::NetworkSessionFailedError,
128                   QCoreApplication::translate("QNetworkReply", "Network session error."));
129             finished();
130         }
131 #else
132         qWarning("Backend start failed");
133         state = Working;
134         error(QNetworkReplyImpl::UnknownNetworkError,
135               QCoreApplication::translate("QNetworkReply", "backend start error."));
136         finished();
137 #endif
138         return;
139     }
140
141     if (backend && backend->isSynchronous()) {
142         state = Finished;
143         q_func()->setFinished(true);
144     } else {
145         if (state != Finished) {
146             if (operation == QNetworkAccessManager::GetOperation)
147                 pendingNotifications.append(NotifyDownstreamReadyWrite);
148
149             handleNotifications();
150         }
151     }
152 }
153
154 void QNetworkReplyImplPrivate::_q_copyReadyRead()
155 {
156     Q_Q(QNetworkReplyImpl);
157     if (state != Working)
158         return;
159     if (!copyDevice || !q->isOpen())
160         return;
161
162     // FIXME Optimize to use download buffer if it is a QBuffer.
163     // Needs to be done where sendCacheContents() (?) of HTTP is emitting
164     // metaDataChanged ?
165
166     forever {
167         qint64 bytesToRead = nextDownstreamBlockSize();
168         if (bytesToRead == 0)
169             // we'll be called again, eventually
170             break;
171
172         bytesToRead = qBound<qint64>(1, bytesToRead, copyDevice->bytesAvailable());
173         QByteArray byteData;
174         byteData.resize(bytesToRead);
175         qint64 bytesActuallyRead = copyDevice->read(byteData.data(), byteData.size());
176         if (bytesActuallyRead == -1) {
177             byteData.clear();
178             backendNotify(NotifyCopyFinished);
179             break;
180         }
181
182         byteData.resize(bytesActuallyRead);
183         readBuffer.append(byteData);
184
185         if (!copyDevice->isSequential() && copyDevice->atEnd()) {
186             backendNotify(NotifyCopyFinished);
187             bytesDownloaded += bytesActuallyRead;
188             break;
189         }
190
191         bytesDownloaded += bytesActuallyRead;
192     }
193
194     if (bytesDownloaded == lastBytesDownloaded) {
195         // we didn't read anything
196         return;
197     }
198
199     lastBytesDownloaded = bytesDownloaded;
200     QVariant totalSize = cookedHeaders.value(QNetworkRequest::ContentLengthHeader);
201     if (preMigrationDownloaded != Q_INT64_C(-1))
202         totalSize = totalSize.toLongLong() + preMigrationDownloaded;
203     pauseNotificationHandling();
204     // emit readyRead before downloadProgress incase this will cause events to be
205     // processed and we get into a recursive call (as in QProgressDialog).
206     emit q->readyRead();
207     emit q->downloadProgress(bytesDownloaded,
208                              totalSize.isNull() ? Q_INT64_C(-1) : totalSize.toLongLong());
209     resumeNotificationHandling();
210 }
211
212 void QNetworkReplyImplPrivate::_q_copyReadChannelFinished()
213 {
214     _q_copyReadyRead();
215 }
216
217 void QNetworkReplyImplPrivate::_q_bufferOutgoingDataFinished()
218 {
219     Q_Q(QNetworkReplyImpl);
220
221     // make sure this is only called once, ever.
222     //_q_bufferOutgoingData may call it or the readChannelFinished emission
223     if (state != Buffering)
224         return;
225
226     // disconnect signals
227     QObject::disconnect(outgoingData, SIGNAL(readyRead()), q, SLOT(_q_bufferOutgoingData()));
228     QObject::disconnect(outgoingData, SIGNAL(readChannelFinished()), q, SLOT(_q_bufferOutgoingDataFinished()));
229
230     // finally, start the request
231     QMetaObject::invokeMethod(q, "_q_startOperation", Qt::QueuedConnection);
232 }
233
234 void QNetworkReplyImplPrivate::_q_bufferOutgoingData()
235 {
236     Q_Q(QNetworkReplyImpl);
237
238     if (!outgoingDataBuffer) {
239         // first call, create our buffer
240         outgoingDataBuffer = QSharedPointer<QRingBuffer>(new QRingBuffer());
241
242         QObject::connect(outgoingData, SIGNAL(readyRead()), q, SLOT(_q_bufferOutgoingData()));
243         QObject::connect(outgoingData, SIGNAL(readChannelFinished()), q, SLOT(_q_bufferOutgoingDataFinished()));
244     }
245
246     qint64 bytesBuffered = 0;
247     qint64 bytesToBuffer = 0;
248
249     // read data into our buffer
250     forever {
251         bytesToBuffer = outgoingData->bytesAvailable();
252         // unknown? just try 2 kB, this also ensures we always try to read the EOF
253         if (bytesToBuffer <= 0)
254             bytesToBuffer = 2*1024;
255
256         char *dst = outgoingDataBuffer->reserve(bytesToBuffer);
257         bytesBuffered = outgoingData->read(dst, bytesToBuffer);
258
259         if (bytesBuffered == -1) {
260             // EOF has been reached.
261             outgoingDataBuffer->chop(bytesToBuffer);
262
263             _q_bufferOutgoingDataFinished();
264             break;
265         } else if (bytesBuffered == 0) {
266             // nothing read right now, just wait until we get called again
267             outgoingDataBuffer->chop(bytesToBuffer);
268
269             break;
270         } else {
271             // don't break, try to read() again
272             outgoingDataBuffer->chop(bytesToBuffer - bytesBuffered);
273         }
274     }
275 }
276
277 #ifndef QT_NO_BEARERMANAGEMENT
278 void QNetworkReplyImplPrivate::_q_networkSessionConnected()
279 {
280     Q_Q(QNetworkReplyImpl);
281
282     if (manager.isNull())
283         return;
284
285     QNetworkSession *session = manager->d_func()->networkSession.data();
286     if (!session)
287         return;
288
289     if (session->state() != QNetworkSession::Connected)
290         return;
291
292     switch (state) {
293     case QNetworkReplyImplPrivate::Buffering:
294     case QNetworkReplyImplPrivate::Working:
295     case QNetworkReplyImplPrivate::Reconnecting:
296         // Migrate existing downloads to new network connection.
297         migrateBackend();
298         break;
299     case QNetworkReplyImplPrivate::WaitingForSession:
300         // Start waiting requests.
301         QMetaObject::invokeMethod(q, "_q_startOperation", Qt::QueuedConnection);
302         break;
303     default:
304         ;
305     }
306 }
307
308 void QNetworkReplyImplPrivate::_q_networkSessionFailed()
309 {
310     // Abort waiting and working replies.
311     if (state == WaitingForSession || state == Working) {
312         state = Working;
313         QSharedPointer<QNetworkSession> session(manager->d_func()->networkSession);
314         QString errorStr;
315         if (session)
316             errorStr = session->errorString();
317         else
318             errorStr = QCoreApplication::translate("QNetworkReply", "Network session error.");
319         error(QNetworkReplyImpl::NetworkSessionFailedError, errorStr);
320         finished();
321     }
322 }
323
324 void QNetworkReplyImplPrivate::_q_networkSessionUsagePoliciesChanged(QNetworkSession::UsagePolicies)
325 {
326
327 }
328 #endif
329
330 void QNetworkReplyImplPrivate::setup(QNetworkAccessManager::Operation op, const QNetworkRequest &req,
331                                      QIODevice *data)
332 {
333     Q_Q(QNetworkReplyImpl);
334
335     outgoingData = data;
336     request = req;
337     url = request.url();
338     operation = op;
339
340     q->QIODevice::open(QIODevice::ReadOnly);
341     // Internal code that does a HTTP reply for the synchronous Ajax
342     // in QtWebKit.
343     QVariant synchronousHttpAttribute = req.attribute(
344             static_cast<QNetworkRequest::Attribute>(QNetworkRequest::SynchronousRequestAttribute));
345     // The synchronous HTTP is a corner case, we will put all upload data in one big QByteArray in the outgoingDataBuffer.
346     // Yes, this is not the most efficient thing to do, but on the other hand synchronous XHR needs to die anyway.
347     if (synchronousHttpAttribute.toBool() && outgoingData) {
348         outgoingDataBuffer = QSharedPointer<QRingBuffer>(new QRingBuffer());
349         qint64 previousDataSize = 0;
350         do {
351             previousDataSize = outgoingDataBuffer->size();
352             outgoingDataBuffer->append(outgoingData->readAll());
353         } while (outgoingDataBuffer->size() != previousDataSize);
354     }
355
356     if (backend)
357         backend->setSynchronous(synchronousHttpAttribute.toBool());
358
359
360     if (outgoingData && backend && !backend->isSynchronous()) {
361         // there is data to be uploaded, e.g. HTTP POST.
362
363         if (!backend->needsResetableUploadData() || !outgoingData->isSequential()) {
364             // backend does not need upload buffering or
365             // fixed size non-sequential
366             // just start the operation
367             QMetaObject::invokeMethod(q, "_q_startOperation", Qt::QueuedConnection);
368         } else {
369             bool bufferingDisallowed =
370                     req.attribute(QNetworkRequest::DoNotBufferUploadDataAttribute,
371                                   false).toBool();
372
373             if (bufferingDisallowed) {
374                 // if a valid content-length header for the request was supplied, we can disable buffering
375                 // if not, we will buffer anyway
376                 if (req.header(QNetworkRequest::ContentLengthHeader).isValid()) {
377                     QMetaObject::invokeMethod(q, "_q_startOperation", Qt::QueuedConnection);
378                 } else {
379                     state = Buffering;
380                     QMetaObject::invokeMethod(q, "_q_bufferOutgoingData", Qt::QueuedConnection);
381                 }
382             } else {
383                 // _q_startOperation will be called when the buffering has finished.
384                 state = Buffering;
385                 QMetaObject::invokeMethod(q, "_q_bufferOutgoingData", Qt::QueuedConnection);
386             }
387         }
388     } else {
389         // for HTTP, we want to send out the request as fast as possible to the network, without
390         // invoking methods in a QueuedConnection
391 #ifndef QT_NO_HTTP
392         if (backend && backend->isSynchronous()) {
393             _q_startOperation();
394         } else {
395             QMetaObject::invokeMethod(q, "_q_startOperation", Qt::QueuedConnection);
396         }
397 #else
398         if (backend && backend->isSynchronous())
399             _q_startOperation();
400         else
401             QMetaObject::invokeMethod(q, "_q_startOperation", Qt::QueuedConnection);
402 #endif // QT_NO_HTTP
403         }
404 }
405
406 void QNetworkReplyImplPrivate::backendNotify(InternalNotifications notification)
407 {
408     Q_Q(QNetworkReplyImpl);
409     if (!pendingNotifications.contains(notification))
410         pendingNotifications.enqueue(notification);
411
412     if (pendingNotifications.size() == 1)
413         QCoreApplication::postEvent(q, new QEvent(QEvent::NetworkReplyUpdated));
414 }
415
416 void QNetworkReplyImplPrivate::handleNotifications()
417 {
418     if (notificationHandlingPaused)
419         return;
420
421     NotificationQueue current = pendingNotifications;
422     pendingNotifications.clear();
423
424     if (state != Working)
425         return;
426
427     while (state == Working && !current.isEmpty()) {
428         InternalNotifications notification = current.dequeue();
429         switch (notification) {
430         case NotifyDownstreamReadyWrite:
431             if (copyDevice)
432                 _q_copyReadyRead();
433             else
434                 backend->downstreamReadyWrite();
435             break;
436
437         case NotifyCloseDownstreamChannel:
438             backend->closeDownstreamChannel();
439             break;
440
441         case NotifyCopyFinished: {
442             QIODevice *dev = copyDevice;
443             copyDevice = 0;
444             backend->copyFinished(dev);
445             break;
446         }
447         }
448     }
449 }
450
451 // Do not handle the notifications while we are emitting downloadProgress
452 // or readyRead
453 void QNetworkReplyImplPrivate::pauseNotificationHandling()
454 {
455     notificationHandlingPaused = true;
456 }
457
458 // Resume notification handling
459 void QNetworkReplyImplPrivate::resumeNotificationHandling()
460 {
461     Q_Q(QNetworkReplyImpl);
462     notificationHandlingPaused = false;
463     if (pendingNotifications.size() >= 1)
464         QCoreApplication::postEvent(q, new QEvent(QEvent::NetworkReplyUpdated));
465 }
466
467 QAbstractNetworkCache *QNetworkReplyImplPrivate::networkCache() const
468 {
469     if (!backend)
470         return 0;
471     return backend->networkCache();
472 }
473
474 void QNetworkReplyImplPrivate::createCache()
475 {
476     // check if we can save and if we're allowed to
477     if (!networkCache()
478         || !request.attribute(QNetworkRequest::CacheSaveControlAttribute, true).toBool())
479         return;
480     cacheEnabled = true;
481 }
482
483 bool QNetworkReplyImplPrivate::isCachingEnabled() const
484 {
485     return (cacheEnabled && networkCache() != 0);
486 }
487
488 void QNetworkReplyImplPrivate::setCachingEnabled(bool enable)
489 {
490     if (!enable && !cacheEnabled)
491         return;                 // nothing to do
492     if (enable && cacheEnabled)
493         return;                 // nothing to do either!
494
495     if (enable) {
496         if (bytesDownloaded) {
497             // refuse to enable in this case
498             qCritical("QNetworkReplyImpl: backend error: caching was enabled after some bytes had been written");
499             return;
500         }
501
502         createCache();
503     } else {
504         // someone told us to turn on, then back off?
505         // ok... but you should make up your mind
506         qDebug("QNetworkReplyImpl: setCachingEnabled(true) called after setCachingEnabled(false) -- "
507                "backend %s probably needs to be fixed",
508                backend->metaObject()->className());
509         networkCache()->remove(url);
510         cacheSaveDevice = 0;
511         cacheEnabled = false;
512     }
513 }
514
515 void QNetworkReplyImplPrivate::completeCacheSave()
516 {
517     if (cacheEnabled && errorCode != QNetworkReplyImpl::NoError) {
518         networkCache()->remove(url);
519     } else if (cacheEnabled && cacheSaveDevice) {
520         networkCache()->insert(cacheSaveDevice);
521     }
522     cacheSaveDevice = 0;
523     cacheEnabled = false;
524 }
525
526 void QNetworkReplyImplPrivate::emitUploadProgress(qint64 bytesSent, qint64 bytesTotal)
527 {
528     Q_Q(QNetworkReplyImpl);
529     bytesUploaded = bytesSent;
530     pauseNotificationHandling();
531     emit q->uploadProgress(bytesSent, bytesTotal);
532     resumeNotificationHandling();
533 }
534
535
536 qint64 QNetworkReplyImplPrivate::nextDownstreamBlockSize() const
537 {
538     enum { DesiredBufferSize = 32 * 1024 };
539     if (readBufferMaxSize == 0)
540         return DesiredBufferSize;
541
542     return qMax<qint64>(0, readBufferMaxSize - readBuffer.byteAmount());
543 }
544
545 void QNetworkReplyImplPrivate::initCacheSaveDevice()
546 {
547     Q_Q(QNetworkReplyImpl);
548
549     // The disk cache does not support partial content, so don't even try to
550     // save any such content into the cache.
551     if (q->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() == 206) {
552         cacheEnabled = false;
553         return;
554     }
555
556     // save the meta data
557     QNetworkCacheMetaData metaData;
558     metaData.setUrl(url);
559     metaData = backend->fetchCacheMetaData(metaData);
560
561     // save the redirect request also in the cache
562     QVariant redirectionTarget = q->attribute(QNetworkRequest::RedirectionTargetAttribute);
563     if (redirectionTarget.isValid()) {
564         QNetworkCacheMetaData::AttributesMap attributes = metaData.attributes();
565         attributes.insert(QNetworkRequest::RedirectionTargetAttribute, redirectionTarget);
566         metaData.setAttributes(attributes);
567     }
568
569     cacheSaveDevice = networkCache()->prepare(metaData);
570
571     if (!cacheSaveDevice || (cacheSaveDevice && !cacheSaveDevice->isOpen())) {
572         if (cacheSaveDevice && !cacheSaveDevice->isOpen())
573             qCritical("QNetworkReplyImpl: network cache returned a device that is not open -- "
574                   "class %s probably needs to be fixed",
575                   networkCache()->metaObject()->className());
576
577         networkCache()->remove(url);
578         cacheSaveDevice = 0;
579         cacheEnabled = false;
580     }
581 }
582
583 // we received downstream data and send this to the cache
584 // and to our readBuffer (which in turn gets read by the user of QNetworkReply)
585 void QNetworkReplyImplPrivate::appendDownstreamData(QByteDataBuffer &data)
586 {
587     Q_Q(QNetworkReplyImpl);
588     if (!q->isOpen())
589         return;
590
591     if (cacheEnabled && !cacheSaveDevice) {
592         initCacheSaveDevice();
593     }
594
595     qint64 bytesWritten = 0;
596     for (int i = 0; i < data.bufferCount(); i++) {
597         QByteArray const &item = data[i];
598
599         if (cacheSaveDevice)
600             cacheSaveDevice->write(item.constData(), item.size());
601         readBuffer.append(item);
602
603         bytesWritten += item.size();
604     }
605     data.clear();
606
607     bytesDownloaded += bytesWritten;
608     lastBytesDownloaded = bytesDownloaded;
609
610     appendDownstreamDataSignalEmissions();
611 }
612
613 void QNetworkReplyImplPrivate::appendDownstreamDataSignalEmissions()
614 {
615     Q_Q(QNetworkReplyImpl);
616
617     QVariant totalSize = cookedHeaders.value(QNetworkRequest::ContentLengthHeader);
618     if (preMigrationDownloaded != Q_INT64_C(-1))
619         totalSize = totalSize.toLongLong() + preMigrationDownloaded;
620     pauseNotificationHandling();
621     // important: At the point of this readyRead(), the data parameter list must be empty,
622     // else implicit sharing will trigger memcpy when the user is reading data!
623     emit q->readyRead();
624     // emit readyRead before downloadProgress incase this will cause events to be
625     // processed and we get into a recursive call (as in QProgressDialog).
626     emit q->downloadProgress(bytesDownloaded,
627                              totalSize.isNull() ? Q_INT64_C(-1) : totalSize.toLongLong());
628
629     resumeNotificationHandling();
630     // do we still have room in the buffer?
631     if (nextDownstreamBlockSize() > 0)
632         backendNotify(QNetworkReplyImplPrivate::NotifyDownstreamReadyWrite);
633 }
634
635 // this is used when it was fetched from the cache, right?
636 void QNetworkReplyImplPrivate::appendDownstreamData(QIODevice *data)
637 {
638     Q_Q(QNetworkReplyImpl);
639     if (!q->isOpen())
640         return;
641
642     // read until EOF from data
643     if (copyDevice) {
644         qCritical("QNetworkReplyImpl: copy from QIODevice already in progress -- "
645                   "backend probly needs to be fixed");
646         return;
647     }
648
649     copyDevice = data;
650     q->connect(copyDevice, SIGNAL(readyRead()), SLOT(_q_copyReadyRead()));
651     q->connect(copyDevice, SIGNAL(readChannelFinished()), SLOT(_q_copyReadChannelFinished()));
652
653     // start the copy:
654     _q_copyReadyRead();
655 }
656
657 void QNetworkReplyImplPrivate::appendDownstreamData(const QByteArray &data)
658 {
659     Q_UNUSED(data)
660     // TODO implement
661
662     // TODO call
663
664     qFatal("QNetworkReplyImplPrivate::appendDownstreamData not implemented");
665 }
666
667 static void downloadBufferDeleter(char *ptr)
668 {
669     delete[] ptr;
670 }
671
672 char* QNetworkReplyImplPrivate::getDownloadBuffer(qint64 size)
673 {
674     Q_Q(QNetworkReplyImpl);
675
676     if (!downloadBuffer) {
677         // We are requested to create it
678         // Check attribute() if allocating a buffer of that size can be allowed
679         QVariant bufferAllocationPolicy = request.attribute(QNetworkRequest::MaximumDownloadBufferSizeAttribute);
680         if (bufferAllocationPolicy.isValid() && bufferAllocationPolicy.toLongLong() >= size) {
681             downloadBufferCurrentSize = 0;
682             downloadBufferMaximumSize = size;
683             downloadBuffer = new char[downloadBufferMaximumSize]; // throws if allocation fails
684             downloadBufferPointer = QSharedPointer<char>(downloadBuffer, downloadBufferDeleter);
685
686             q->setAttribute(QNetworkRequest::DownloadBufferAttribute, qVariantFromValue<QSharedPointer<char> > (downloadBufferPointer));
687         }
688     }
689
690     return downloadBuffer;
691 }
692
693 void QNetworkReplyImplPrivate::setDownloadBuffer(QSharedPointer<char> sp, qint64 size)
694 {
695     Q_Q(QNetworkReplyImpl);
696
697     downloadBufferPointer = sp;
698     downloadBuffer = downloadBufferPointer.data();
699     downloadBufferCurrentSize = 0;
700     downloadBufferMaximumSize = size;
701     q->setAttribute(QNetworkRequest::DownloadBufferAttribute, qVariantFromValue<QSharedPointer<char> > (downloadBufferPointer));
702 }
703
704
705 void QNetworkReplyImplPrivate::appendDownstreamDataDownloadBuffer(qint64 bytesReceived, qint64 bytesTotal)
706 {
707     Q_Q(QNetworkReplyImpl);
708     if (!q->isOpen())
709         return;
710
711     if (cacheEnabled && !cacheSaveDevice)
712         initCacheSaveDevice();
713
714     if (cacheSaveDevice && bytesReceived == bytesTotal) {
715 //        if (lastBytesDownloaded == -1)
716 //            lastBytesDownloaded = 0;
717 //        cacheSaveDevice->write(downloadBuffer + lastBytesDownloaded, bytesReceived - lastBytesDownloaded);
718
719         // Write everything in one go if we use a download buffer. might be more performant.
720         cacheSaveDevice->write(downloadBuffer, bytesTotal);
721     }
722
723     bytesDownloaded = bytesReceived;
724     lastBytesDownloaded = bytesReceived;
725
726     downloadBufferCurrentSize = bytesReceived;
727
728     // Only emit readyRead when actual data is there
729     // emit readyRead before downloadProgress incase this will cause events to be
730     // processed and we get into a recursive call (as in QProgressDialog).
731     if (bytesDownloaded > 0)
732         emit q->readyRead();
733     emit q->downloadProgress(bytesDownloaded, bytesTotal);
734 }
735
736 void QNetworkReplyImplPrivate::finished()
737 {
738     Q_Q(QNetworkReplyImpl);
739
740     if (state == Finished || state == Aborted || state == WaitingForSession)
741         return;
742
743     pauseNotificationHandling();
744     QVariant totalSize = cookedHeaders.value(QNetworkRequest::ContentLengthHeader);
745     if (preMigrationDownloaded != Q_INT64_C(-1))
746         totalSize = totalSize.toLongLong() + preMigrationDownloaded;
747
748     if (!manager.isNull()) {
749 #ifndef QT_NO_BEARERMANAGEMENT
750         QNetworkSession *session = manager->d_func()->networkSession.data();
751         if (session && session->state() == QNetworkSession::Roaming &&
752             state == Working && errorCode != QNetworkReply::OperationCanceledError) {
753             // only content with a known size will fail with a temporary network failure error
754             if (!totalSize.isNull()) {
755                 if (bytesDownloaded != totalSize) {
756                     if (migrateBackend()) {
757                         // either we are migrating or the request is finished/aborted
758                         if (state == Reconnecting || state == WaitingForSession) {
759                             resumeNotificationHandling();
760                             return; // exit early if we are migrating.
761                         }
762                     } else {
763                         error(QNetworkReply::TemporaryNetworkFailureError,
764                               QNetworkReply::tr("Temporary network failure."));
765                     }
766                 }
767             }
768         }
769 #endif
770     }
771     resumeNotificationHandling();
772
773     state = Finished;
774     q->setFinished(true);
775
776     pendingNotifications.clear();
777
778     pauseNotificationHandling();
779     if (totalSize.isNull() || totalSize == -1) {
780         emit q->downloadProgress(bytesDownloaded, bytesDownloaded);
781     }
782
783     if (bytesUploaded == -1 && (outgoingData || outgoingDataBuffer))
784         emit q->uploadProgress(0, 0);
785     resumeNotificationHandling();
786
787     // if we don't know the total size of or we received everything save the cache
788     if (totalSize.isNull() || totalSize == -1 || bytesDownloaded == totalSize)
789         completeCacheSave();
790
791     // note: might not be a good idea, since users could decide to delete us
792     // which would delete the backend too...
793     // maybe we should protect the backend
794     pauseNotificationHandling();
795     emit q->readChannelFinished();
796     emit q->finished();
797     resumeNotificationHandling();
798 }
799
800 void QNetworkReplyImplPrivate::error(QNetworkReplyImpl::NetworkError code, const QString &errorMessage)
801 {
802     Q_Q(QNetworkReplyImpl);
803     // Can't set and emit multiple errors.
804     if (errorCode != QNetworkReply::NoError) {
805         qWarning( "QNetworkReplyImplPrivate::error: Internal problem, this method must only be called once.");
806         return;
807     }
808
809     errorCode = code;
810     q->setErrorString(errorMessage);
811
812     // note: might not be a good idea, since users could decide to delete us
813     // which would delete the backend too...
814     // maybe we should protect the backend
815     emit q->error(code);
816 }
817
818 void QNetworkReplyImplPrivate::metaDataChanged()
819 {
820     Q_Q(QNetworkReplyImpl);
821     // 1. do we have cookies?
822     // 2. are we allowed to set them?
823     if (cookedHeaders.contains(QNetworkRequest::SetCookieHeader) && !manager.isNull()
824         && (static_cast<QNetworkRequest::LoadControl>
825             (request.attribute(QNetworkRequest::CookieSaveControlAttribute,
826                                QNetworkRequest::Automatic).toInt()) == QNetworkRequest::Automatic)) {
827         QList<QNetworkCookie> cookies =
828             qvariant_cast<QList<QNetworkCookie> >(cookedHeaders.value(QNetworkRequest::SetCookieHeader));
829         QNetworkCookieJar *jar = manager->cookieJar();
830         if (jar)
831             jar->setCookiesFromUrl(cookies, url);
832     }
833     emit q->metaDataChanged();
834 }
835
836 void QNetworkReplyImplPrivate::redirectionRequested(const QUrl &target)
837 {
838     attributes.insert(QNetworkRequest::RedirectionTargetAttribute, target);
839 }
840
841 void QNetworkReplyImplPrivate::sslErrors(const QList<QSslError> &errors)
842 {
843 #ifndef QT_NO_SSL
844     Q_Q(QNetworkReplyImpl);
845     emit q->sslErrors(errors);
846 #else
847     Q_UNUSED(errors);
848 #endif
849 }
850
851 QNetworkReplyImpl::QNetworkReplyImpl(QObject *parent)
852     : QNetworkReply(*new QNetworkReplyImplPrivate, parent)
853 {
854 }
855
856 QNetworkReplyImpl::~QNetworkReplyImpl()
857 {
858     Q_D(QNetworkReplyImpl);
859
860     // This code removes the data from the cache if it was prematurely aborted.
861     // See QNetworkReplyImplPrivate::completeCacheSave(), we disable caching there after the cache
862     // save had been properly finished. So if it is still enabled it means we got deleted/aborted.
863     if (d->isCachingEnabled())
864         d->networkCache()->remove(url());
865 }
866
867 void QNetworkReplyImpl::abort()
868 {
869     Q_D(QNetworkReplyImpl);
870     if (d->state == QNetworkReplyImplPrivate::Finished || d->state == QNetworkReplyImplPrivate::Aborted)
871         return;
872
873     // stop both upload and download
874     if (d->outgoingData)
875         disconnect(d->outgoingData, 0, this, 0);
876     if (d->copyDevice)
877         disconnect(d->copyDevice, 0, this, 0);
878
879     QNetworkReply::close();
880
881     if (d->state != QNetworkReplyImplPrivate::Finished) {
882         // call finished which will emit signals
883         d->error(OperationCanceledError, tr("Operation canceled"));
884         if (d->state == QNetworkReplyImplPrivate::WaitingForSession)
885             d->state = QNetworkReplyImplPrivate::Working;
886         d->finished();
887     }
888     d->state = QNetworkReplyImplPrivate::Aborted;
889
890     // finished may access the backend
891     if (d->backend) {
892         d->backend->deleteLater();
893         d->backend = 0;
894     }
895 }
896
897 void QNetworkReplyImpl::close()
898 {
899     Q_D(QNetworkReplyImpl);
900     if (d->state == QNetworkReplyImplPrivate::Aborted ||
901         d->state == QNetworkReplyImplPrivate::Finished)
902         return;
903
904     // stop the download
905     if (d->backend)
906         d->backend->closeDownstreamChannel();
907     if (d->copyDevice)
908         disconnect(d->copyDevice, 0, this, 0);
909
910     QNetworkReply::close();
911
912     // call finished which will emit signals
913     d->error(OperationCanceledError, tr("Operation canceled"));
914     d->finished();
915 }
916
917 bool QNetworkReplyImpl::canReadLine () const
918 {
919     Q_D(const QNetworkReplyImpl);
920     return QNetworkReply::canReadLine() || d->readBuffer.canReadLine();
921 }
922
923
924 /*!
925     Returns the number of bytes available for reading with
926     QIODevice::read(). The number of bytes available may grow until
927     the finished() signal is emitted.
928 */
929 qint64 QNetworkReplyImpl::bytesAvailable() const
930 {
931     // Special case for the "zero copy" download buffer
932     Q_D(const QNetworkReplyImpl);
933     if (d->downloadBuffer) {
934         qint64 maxAvail = d->downloadBufferCurrentSize - d->downloadBufferReadPosition;
935         return QNetworkReply::bytesAvailable() + maxAvail;
936     }
937
938     return QNetworkReply::bytesAvailable() + d_func()->readBuffer.byteAmount();
939 }
940
941 void QNetworkReplyImpl::setReadBufferSize(qint64 size)
942 {
943     Q_D(QNetworkReplyImpl);
944     if (size > d->readBufferMaxSize &&
945         size > d->readBuffer.byteAmount())
946         d->backendNotify(QNetworkReplyImplPrivate::NotifyDownstreamReadyWrite);
947
948     QNetworkReply::setReadBufferSize(size);
949
950     if (d->backend)
951         d->backend->setDownstreamLimited(d->readBufferMaxSize > 0);
952 }
953
954 #ifndef QT_NO_SSL
955 void QNetworkReplyImpl::sslConfigurationImplementation(QSslConfiguration &configuration) const
956 {
957     Q_D(const QNetworkReplyImpl);
958     if (d->backend)
959         d->backend->fetchSslConfiguration(configuration);
960 }
961
962 void QNetworkReplyImpl::setSslConfigurationImplementation(const QSslConfiguration &config)
963 {
964     Q_D(QNetworkReplyImpl);
965     if (d->backend && !config.isNull())
966         d->backend->setSslConfiguration(config);
967 }
968
969 void QNetworkReplyImpl::ignoreSslErrors()
970 {
971     Q_D(QNetworkReplyImpl);
972     if (d->backend)
973         d->backend->ignoreSslErrors();
974 }
975
976 void QNetworkReplyImpl::ignoreSslErrorsImplementation(const QList<QSslError> &errors)
977 {
978     Q_D(QNetworkReplyImpl);
979     if (d->backend)
980         d->backend->ignoreSslErrors(errors);
981 }
982 #endif  // QT_NO_SSL
983
984 /*!
985     \internal
986 */
987 qint64 QNetworkReplyImpl::readData(char *data, qint64 maxlen)
988 {
989     Q_D(QNetworkReplyImpl);
990
991     // Special case code if we have the "zero copy" download buffer
992     if (d->downloadBuffer) {
993         qint64 maxAvail = qMin<qint64>(d->downloadBufferCurrentSize - d->downloadBufferReadPosition, maxlen);
994         if (maxAvail == 0)
995             return d->state == QNetworkReplyImplPrivate::Finished ? -1 : 0;
996         // FIXME what about "Aborted" state?
997         memcpy(data, d->downloadBuffer + d->downloadBufferReadPosition, maxAvail);
998         d->downloadBufferReadPosition += maxAvail;
999         return maxAvail;
1000     }
1001
1002
1003     if (d->readBuffer.isEmpty())
1004         return d->state == QNetworkReplyImplPrivate::Finished ? -1 : 0;
1005     // FIXME what about "Aborted" state?
1006
1007     d->backendNotify(QNetworkReplyImplPrivate::NotifyDownstreamReadyWrite);
1008     if (maxlen == 1) {
1009         // optimization for getChar()
1010         *data = d->readBuffer.getChar();
1011         return 1;
1012     }
1013
1014     maxlen = qMin<qint64>(maxlen, d->readBuffer.byteAmount());
1015     return d->readBuffer.read(data, maxlen);
1016 }
1017
1018 /*!
1019    \internal Reimplemented for internal purposes
1020 */
1021 bool QNetworkReplyImpl::event(QEvent *e)
1022 {
1023     if (e->type() == QEvent::NetworkReplyUpdated) {
1024         d_func()->handleNotifications();
1025         return true;
1026     }
1027
1028     return QObject::event(e);
1029 }
1030
1031 /*
1032     Migrates the backend of the QNetworkReply to a new network connection if required.  Returns
1033     true if the reply is migrated or it is not required; otherwise returns false.
1034 */
1035 bool QNetworkReplyImplPrivate::migrateBackend()
1036 {
1037     Q_Q(QNetworkReplyImpl);
1038
1039     // Network reply is already finished or aborted, don't need to migrate.
1040     if (state == Finished || state == Aborted)
1041         return true;
1042
1043     // Request has outgoing data, not migrating.
1044     if (outgoingData)
1045         return false;
1046
1047     // Request is serviced from the cache, don't need to migrate.
1048     if (copyDevice)
1049         return true;
1050
1051     // Backend does not support resuming download.
1052     if (!backend->canResume())
1053         return false;
1054
1055     state = QNetworkReplyImplPrivate::Reconnecting;
1056
1057     if (backend) {
1058         delete backend;
1059         backend = 0;
1060     }
1061
1062     cookedHeaders.clear();
1063     rawHeaders.clear();
1064
1065     preMigrationDownloaded = bytesDownloaded;
1066
1067     backend = manager->d_func()->findBackend(operation, request);
1068
1069     if (backend) {
1070         backend->setParent(q);
1071         backend->reply = this;
1072         backend->setResumeOffset(bytesDownloaded);
1073     }
1074
1075 #ifndef QT_NO_HTTP
1076     QMetaObject::invokeMethod(q, "_q_startOperation", Qt::QueuedConnection);
1077 #else
1078     QMetaObject::invokeMethod(q, "_q_startOperation", Qt::QueuedConnection);
1079 #endif // QT_NO_HTTP
1080
1081     return true;
1082 }
1083
1084 #ifndef QT_NO_BEARERMANAGEMENT
1085 QDisabledNetworkReply::QDisabledNetworkReply(QObject *parent,
1086                                              const QNetworkRequest &req,
1087                                              QNetworkAccessManager::Operation op)
1088 :   QNetworkReply(parent)
1089 {
1090     setRequest(req);
1091     setUrl(req.url());
1092     setOperation(op);
1093
1094     qRegisterMetaType<QNetworkReply::NetworkError>("QNetworkReply::NetworkError");
1095
1096     QString msg = QCoreApplication::translate("QNetworkAccessManager",
1097                                               "Network access is disabled.");
1098     setError(UnknownNetworkError, msg);
1099
1100     QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection,
1101         Q_ARG(QNetworkReply::NetworkError, UnknownNetworkError));
1102     QMetaObject::invokeMethod(this, "finished", Qt::QueuedConnection);
1103 }
1104
1105 QDisabledNetworkReply::~QDisabledNetworkReply()
1106 {
1107 }
1108 #endif
1109
1110 QT_END_NAMESPACE
1111
1112 #include "moc_qnetworkreplyimpl_p.cpp"
1113