1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
6 ** This file is part of the QtNetwork module of the Qt Toolkit.
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.
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.
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.
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.
40 ****************************************************************************/
42 #include "qnetworkaccessfilebackend_p.h"
43 #include "qfileinfo.h"
46 #include "private/qnoncontiguousbytedevice_p.h"
48 #include <QtCore/QCoreApplication>
52 QNetworkAccessBackend *
53 QNetworkAccessFileBackendFactory::create(QNetworkAccessManager::Operation op,
54 const QNetworkRequest &request) const
56 // is it an operation we know of?
58 case QNetworkAccessManager::GetOperation:
59 case QNetworkAccessManager::PutOperation:
63 // no, we can't handle this operation
67 QUrl url = request.url();
68 if (url.scheme().compare(QLatin1String("qrc"), Qt::CaseInsensitive) == 0 || url.isLocalFile()) {
69 return new QNetworkAccessFileBackend;
70 } else if (!url.scheme().isEmpty() && url.authority().isEmpty() && (url.scheme().length() > 1)) {
71 // check if QFile could, in theory, open this URL via the file engines
72 // it has to be in the format:
73 // prefix:path/to/file
74 // or prefix:/path/to/file
76 // this construct here must match the one below in open()
77 QFileInfo fi(url.toString(QUrl::RemoveAuthority | QUrl::RemoveFragment | QUrl::RemoveQuery));
78 if (fi.exists() || (op == QNetworkAccessManager::PutOperation && fi.dir().exists()))
79 return new QNetworkAccessFileBackend;
85 QNetworkAccessFileBackend::QNetworkAccessFileBackend()
86 : uploadByteDevice(0), totalBytes(0), hasUploadFinished(false)
90 QNetworkAccessFileBackend::~QNetworkAccessFileBackend()
94 void QNetworkAccessFileBackend::open()
96 QUrl url = this->url();
98 if (url.host() == QLatin1String("localhost"))
99 url.setHost(QString());
100 #if !defined(Q_OS_WIN)
101 // do not allow UNC paths on Unix
102 if (!url.host().isEmpty()) {
103 // we handle only local files
104 error(QNetworkReply::ProtocolInvalidOperationError,
105 QCoreApplication::translate("QNetworkAccessFileBackend", "Request for opening non-local file %1").arg(url.toString()));
109 #endif // !defined(Q_OS_WIN)
110 if (url.path().isEmpty())
111 url.setPath(QLatin1String("/"));
114 QString fileName = url.toLocalFile();
115 if (fileName.isEmpty()) {
116 if (url.scheme() == QLatin1String("qrc"))
117 fileName = QLatin1Char(':') + url.path();
119 fileName = url.toString(QUrl::RemoveAuthority | QUrl::RemoveFragment | QUrl::RemoveQuery);
121 file.setFileName(fileName);
123 if (operation() == QNetworkAccessManager::GetOperation) {
128 QIODevice::OpenMode mode;
129 switch (operation()) {
130 case QNetworkAccessManager::GetOperation:
131 mode = QIODevice::ReadOnly;
133 case QNetworkAccessManager::PutOperation:
134 mode = QIODevice::WriteOnly | QIODevice::Truncate;
135 uploadByteDevice = createUploadByteDevice();
136 QObject::connect(uploadByteDevice, SIGNAL(readyRead()), this, SLOT(uploadReadyReadSlot()));
137 QMetaObject::invokeMethod(this, "uploadReadyReadSlot", Qt::QueuedConnection);
140 Q_ASSERT_X(false, "QNetworkAccessFileBackend::open",
141 "Got a request operation I cannot handle!!");
145 mode |= QIODevice::Unbuffered;
146 bool opened = file.open(mode);
148 // could we open the file?
150 QString msg = QCoreApplication::translate("QNetworkAccessFileBackend", "Error opening %1: %2")
151 .arg(this->url().toString(), file.errorString());
153 // why couldn't we open the file?
154 // if we're opening for reading, either it doesn't exist, or it's access denied
155 // if we're opening for writing, not existing means it's access denied too
156 if (file.exists() || operation() == QNetworkAccessManager::PutOperation)
157 error(QNetworkReply::ContentAccessDenied, msg);
159 error(QNetworkReply::ContentNotFoundError, msg);
164 void QNetworkAccessFileBackend::uploadReadyReadSlot()
166 if (hasUploadFinished)
171 const char *readPointer = uploadByteDevice->readPointer(-1, haveRead);
172 if (haveRead == -1) {
174 hasUploadFinished = true;
179 } else if (haveRead == 0 || readPointer == 0) {
180 // nothing to read right now, we will be called again later
184 haveWritten = file.write(readPointer, haveRead);
186 if (haveWritten < 0) {
188 QString msg = QCoreApplication::translate("QNetworkAccessFileBackend", "Write error writing to %1: %2")
189 .arg(url().toString(), file.errorString());
190 error(QNetworkReply::ProtocolFailure, msg);
195 uploadByteDevice->advanceReadPointer(haveWritten);
204 void QNetworkAccessFileBackend::closeDownstreamChannel()
206 if (operation() == QNetworkAccessManager::GetOperation) {
211 void QNetworkAccessFileBackend::downstreamReadyWrite()
213 Q_ASSERT_X(operation() == QNetworkAccessManager::GetOperation, "QNetworkAccessFileBackend",
214 "We're being told to download data but operation isn't GET!");
219 bool QNetworkAccessFileBackend::loadFileInfo()
222 setHeader(QNetworkRequest::LastModifiedHeader, fi.lastModified());
223 setHeader(QNetworkRequest::ContentLengthHeader, fi.size());
229 error(QNetworkReply::ContentOperationNotPermittedError,
230 QCoreApplication::translate("QNetworkAccessFileBackend", "Cannot open %1: Path is a directory").arg(url().toString()));
238 bool QNetworkAccessFileBackend::readMoreFromFile()
241 while ((wantToRead = nextDownstreamBlockSize()) > 0) {
243 // Obtain a pointer from the ringbuffer!
246 data.reserve(wantToRead);
247 qint64 actuallyRead = file.read(data.data(), wantToRead);
248 if (actuallyRead <= 0) {
250 if (file.error() != QFile::NoError) {
251 QString msg = QCoreApplication::translate("QNetworkAccessFileBackend", "Read error reading from %1: %2")
252 .arg(url().toString(), file.errorString());
253 error(QNetworkReply::ProtocolFailure, msg);
263 data.resize(actuallyRead);
264 totalBytes += actuallyRead;
266 QByteDataBuffer list;
268 data.clear(); // important because of implicit sharing!
269 writeDownstreamData(list);