1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:significant reason:default
4
5#include "qnetworkreplyfileimpl_p.h"
6
7#include "QtCore/qdatetime.h"
8#include "qnetworkaccessmanager_p.h"
9#include <QtCore/QCoreApplication>
10#include <QtCore/QFileInfo>
11#include <QtCore/QThread>
12#include "qnetworkfile_p.h"
13#include "qnetworkrequest.h"
14
15QT_BEGIN_NAMESPACE
16
17using namespace Qt::StringLiterals;
18
19QT_IMPL_METATYPE_EXTERN_TAGGED(QNetworkRequest::KnownHeaders, QNetworkRequest__KnownHeaders)
20
21QNetworkReplyFileImplPrivate::QNetworkReplyFileImplPrivate()
22 : QNetworkReplyPrivate(), managerPrivate(nullptr), realFile(nullptr)
23{
24 qRegisterMetaType<QNetworkRequest::KnownHeaders>();
25 qRegisterMetaType<QNetworkReply::NetworkError>();
26}
27
28QNetworkReplyFileImpl::~QNetworkReplyFileImpl()
29{
30 QNetworkReplyFileImplPrivate *d = (QNetworkReplyFileImplPrivate*) d_func();
31 if (d->realFile) {
32 if (d->realFile->thread() == QThread::currentThread())
33 delete d->realFile;
34 else
35 QMetaObject::invokeMethod(obj: d->realFile, member: "deleteLater", c: Qt::QueuedConnection);
36 }
37}
38
39QNetworkReplyFileImpl::QNetworkReplyFileImpl(QNetworkAccessManager *manager, const QNetworkRequest &req, const QNetworkAccessManager::Operation op)
40 : QNetworkReply(*new QNetworkReplyFileImplPrivate(), manager)
41{
42 setRequest(req);
43 setUrl(req.url());
44 setOperation(op);
45 QNetworkReply::open(mode: QIODevice::ReadOnly);
46
47 QNetworkReplyFileImplPrivate *d = (QNetworkReplyFileImplPrivate*) d_func();
48
49 d->managerPrivate = manager->d_func();
50
51 QUrl url = req.url();
52 if (url.host() == "localhost"_L1)
53 url.setHost(host: QString());
54
55#if !defined(Q_OS_WIN)
56 // do not allow UNC paths on Unix
57 if (!url.host().isEmpty()) {
58 // we handle only local files
59 QString msg = QCoreApplication::translate(context: "QNetworkAccessFileBackend", key: "Request for opening non-local file %1").arg(a: url.toString());
60 setError(errorCode: QNetworkReply::ProtocolInvalidOperationError, errorString: msg);
61 setFinished(true); // We're finished, will emit finished() after ctor is done.
62 QMetaObject::invokeMethod(obj: this, member: "errorOccurred", c: Qt::QueuedConnection,
63 Q_ARG(QNetworkReply::NetworkError, QNetworkReply::ProtocolInvalidOperationError));
64 QMetaObject::invokeMethod(object: this, function: &QNetworkReplyFileImpl::fileOpenFinished, type: Qt::QueuedConnection, args: false);
65 return;
66 }
67#endif
68 if (url.path().isEmpty())
69 url.setPath(path: "/"_L1);
70 setUrl(url);
71
72 QString fileName = url.toLocalFile();
73 if (fileName.isEmpty()) {
74 const QString scheme = url.scheme();
75 if (scheme == "qrc"_L1) {
76 fileName = u':' + url.path();
77 } else {
78#if defined(Q_OS_ANDROID)
79 if (scheme == "assets"_L1)
80 fileName = "assets:"_L1 + url.path();
81 else
82#endif
83 fileName = url.toString(options: QUrl::RemoveAuthority | QUrl::RemoveFragment | QUrl::RemoveQuery);
84 }
85 }
86
87 if (req.attribute(code: QNetworkRequest::BackgroundRequestAttribute).toBool()) { // Asynchronous open
88 auto realFile = new QNetworkFile(fileName);
89 connect(sender: realFile, signal: &QNetworkFile::headerRead, context: this, slot: &QNetworkReplyFileImpl::setWellKnownHeader,
90 type: Qt::QueuedConnection);
91 connect(sender: realFile, signal: &QNetworkFile::networkError, context: this, slot: &QNetworkReplyFileImpl::setError,
92 type: Qt::QueuedConnection);
93 connect(asender: realFile, SIGNAL(finished(bool)), SLOT(fileOpenFinished(bool)),
94 atype: Qt::QueuedConnection);
95
96 realFile->moveToThread(thread: d->managerPrivate->createThread());
97 QMetaObject::invokeMethod(obj: realFile, member: "open", c: Qt::QueuedConnection);
98
99 d->realFile = realFile;
100 } else { // Synch open
101 setFinished(true);
102
103 QFileInfo fi(fileName);
104 if (fi.isDir()) {
105 QString msg = QCoreApplication::translate(context: "QNetworkAccessFileBackend", key: "Cannot open %1: Path is a directory").arg(a: url.toString());
106 setError(errorCode: QNetworkReply::ContentOperationNotPermittedError, errorString: msg);
107 QMetaObject::invokeMethod(obj: this, member: "errorOccurred", c: Qt::QueuedConnection,
108 Q_ARG(QNetworkReply::NetworkError, QNetworkReply::ContentOperationNotPermittedError));
109 QMetaObject::invokeMethod(obj: this, member: "finished", c: Qt::QueuedConnection);
110 return;
111 }
112 d->realFile = new QFile(fileName, this);
113 bool opened = d->realFile->open(flags: QIODevice::ReadOnly | QIODevice::Unbuffered);
114
115 // could we open the file?
116 if (!opened) {
117 QString msg = QCoreApplication::translate(context: "QNetworkAccessFileBackend", key: "Error opening %1: %2")
118 .arg(args: d->realFile->fileName(), args: d->realFile->errorString());
119
120 if (fi.exists()) {
121 setError(errorCode: QNetworkReply::ContentAccessDenied, errorString: msg);
122 QMetaObject::invokeMethod(obj: this, member: "errorOccurred", c: Qt::QueuedConnection,
123 Q_ARG(QNetworkReply::NetworkError, QNetworkReply::ContentAccessDenied));
124 } else {
125 setError(errorCode: QNetworkReply::ContentNotFoundError, errorString: msg);
126 QMetaObject::invokeMethod(obj: this, member: "errorOccurred", c: Qt::QueuedConnection,
127 Q_ARG(QNetworkReply::NetworkError, QNetworkReply::ContentNotFoundError));
128 }
129 QMetaObject::invokeMethod(obj: this, member: "finished", c: Qt::QueuedConnection);
130 return;
131 }
132 auto h = headers();
133 h.replaceOrAppend(name: QHttpHeaders::WellKnownHeader::LastModified,
134 newValue: QNetworkHeadersPrivate::toHttpDate(dt: fi.lastModified()));
135 h.replaceOrAppend(name: QHttpHeaders::WellKnownHeader::ContentLength,
136 newValue: QByteArray::number(fi.size()));
137 setHeaders(std::move(h));
138
139 QMetaObject::invokeMethod(obj: this, member: "metaDataChanged", c: Qt::QueuedConnection);
140 QMetaObject::invokeMethod(obj: this, member: "downloadProgress", c: Qt::QueuedConnection,
141 Q_ARG(qint64, fi.size()), Q_ARG(qint64, fi.size()));
142 QMetaObject::invokeMethod(obj: this, member: "readyRead", c: Qt::QueuedConnection);
143 QMetaObject::invokeMethod(obj: this, member: "finished", c: Qt::QueuedConnection);
144 }
145}
146
147void QNetworkReplyFileImpl::close()
148{
149 Q_D(QNetworkReplyFileImpl);
150 QNetworkReply::close();
151 if (d->realFile) {
152 if (d->realFile->thread() == thread())
153 d->realFile->close();
154 else
155 QMetaObject::invokeMethod(obj: d->realFile, member: "close", c: Qt::QueuedConnection);
156 }
157}
158
159void QNetworkReplyFileImpl::abort()
160{
161 close();
162}
163
164qint64 QNetworkReplyFileImpl::bytesAvailable() const
165{
166 Q_D(const QNetworkReplyFileImpl);
167 if (!d->isFinished || !d->realFile || !d->realFile->isOpen())
168 return QNetworkReply::bytesAvailable();
169 return QNetworkReply::bytesAvailable() + d->realFile->bytesAvailable();
170}
171
172bool QNetworkReplyFileImpl::isSequential () const
173{
174 return true;
175}
176
177qint64 QNetworkReplyFileImpl::size() const
178{
179 const auto totalSizeOpt = QNetworkHeadersPrivate::toInt(
180 value: headers().value(name: QHttpHeaders::WellKnownHeader::ContentLength));
181 return totalSizeOpt.value_or(u: 0);
182}
183
184/*!
185 \internal
186*/
187qint64 QNetworkReplyFileImpl::readData(char *data, qint64 maxlen)
188{
189 Q_D(QNetworkReplyFileImpl);
190 if (!d->isFinished || !d->realFile || !d->realFile->isOpen())
191 return -1;
192 qint64 ret = d->realFile->read(data, maxlen);
193 if (bytesAvailable() == 0)
194 d->realFile->close();
195 if (ret == 0 && bytesAvailable() == 0)
196 return -1;
197 else {
198 setAttribute(code: QNetworkRequest::HttpStatusCodeAttribute, value: 200);
199 setAttribute(code: QNetworkRequest::HttpReasonPhraseAttribute, value: "OK"_L1);
200 return ret;
201 }
202}
203
204void QNetworkReplyFileImpl::fileOpenFinished(bool isOpen)
205{
206 setFinished(true);
207 if (isOpen) {
208 const auto fileSize = size();
209 Q_EMIT metaDataChanged();
210 Q_EMIT downloadProgress(bytesReceived: fileSize, bytesTotal: fileSize);
211 Q_EMIT readyRead();
212 }
213 Q_EMIT finished();
214}
215
216QT_END_NAMESPACE
217
218#include "moc_qnetworkreplyfileimpl_p.cpp"
219
220

source code of qtbase/src/network/access/qnetworkreplyfileimpl.cpp