Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Toggle main menu visibility
Loading...
Searching...
No Matches
qnetworkdiskcache.cpp
Go to the documentation of this file.
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
//#define QNETWORKDISKCACHE_DEBUG
6
7
8
#
include
"qnetworkdiskcache.h"
9
#
include
"qnetworkdiskcache_p.h"
10
11
#
include
<
qfile
.
h
>
12
#
include
<
qdir
.
h
>
13
#
include
<
qdatastream
.
h
>
14
#
include
<
qdatetime
.
h
>
15
#
include
<
qdirlisting
.
h
>
16
#
include
<
qurl
.
h
>
17
#
include
<
qcryptographichash
.
h
>
18
#
include
<
qdebug
.
h
>
19
20
#
include
<
QtCore
/
private
/
qtools_p
.
h
>
21
22
#
include
<
memory
>
23
24
#
define
CACHE_POSTFIX
".d"_L1
25
#
define
CACHE_VERSION
9
26
#
define
DATA_DIR
"data"_L1
27
28
#
define
MAX_COMPRESSION_SIZE
(
1024
*
1024
*
3
)
29
30
QT_BEGIN_NAMESPACE
31
32
using
namespace
Qt::StringLiterals;
33
34
/*!
35
\class QNetworkDiskCachePrivate
36
\internal
37
*/
38
39
/*!
40
\class QNetworkDiskCache
41
\since 4.5
42
\inmodule QtNetwork
43
44
\brief The QNetworkDiskCache class provides a very basic disk cache.
45
46
QNetworkDiskCache stores each url in its own file inside of the
47
cacheDirectory using QDataStream. Files with a text MimeType
48
are compressed using qCompress. Data is written to disk only in insert()
49
and updateMetaData().
50
51
Currently you cannot share the same cache files with more than
52
one disk cache.
53
54
QNetworkDiskCache by default limits the amount of space that the cache will
55
use on the system to 50MB.
56
57
Note you have to set the cache directory before it will work.
58
59
A network disk cache can be enabled by:
60
61
\snippet code/src_network_access_qnetworkdiskcache.cpp 0
62
63
When sending requests, to control the preference of when to use the cache
64
and when to use the network, consider the following:
65
66
\snippet code/src_network_access_qnetworkdiskcache.cpp 1
67
68
To check whether the response came from the cache or from the network, the
69
following can be applied:
70
71
\snippet code/src_network_access_qnetworkdiskcache.cpp 2
72
73
\section1 Security Considerations
74
75
QNetworkDiskCache stores HTTP response bodies and metadata,
76
including response headers, to disk without encryption. The
77
compression applied to text content is for
78
storage efficiency only and does not provide confidentiality
79
protection.
80
81
Which responses are cached is primarily determined by the HTTP
82
cache control headers sent by the server. Well-configured servers
83
typically prevent caching of responses containing sensitive data
84
from authenticated sessions. However, the application cannot rely
85
on every server doing so correctly, and what the server considers
86
non-sensitive may not match the application's own requirements.
87
88
For cases where the server's caching policy is not sufficient:
89
\list
90
\li Set \l{QNetworkRequest::CacheSaveControlAttribute} to
91
\c false on requests whose responses should not be stored.
92
\li Subclass \l{QAbstractNetworkCache} to implement
93
application-specific filtering of which responses are
94
cached.
95
\endlist
96
97
Cache entries carry basic structural validation but may become
98
invalid due to disk corruption, unclean application shutdown, or
99
other causes. Applications should not treat cached data as
100
trustworthy without independent validation.
101
*/
102
103
/*!
104
Creates a new disk cache. The \a parent argument is passed to
105
QAbstractNetworkCache's constructor.
106
*/
107
QNetworkDiskCache::QNetworkDiskCache(QObject *parent)
108
: QAbstractNetworkCache(*
new
QNetworkDiskCachePrivate, parent)
109
{
110
}
111
112
/*!
113
Destroys the cache object. This does not clear the disk cache.
114
*/
115
QNetworkDiskCache::~QNetworkDiskCache()
116
{
117
Q_D(QNetworkDiskCache);
118
qDeleteAll(d->inserting);
119
}
120
121
/*!
122
Returns the location where cached files will be stored.
123
*/
124
QString QNetworkDiskCache::cacheDirectory()
const
125
{
126
Q_D(
const
QNetworkDiskCache);
127
return
d->cacheDirectory;
128
}
129
130
/*!
131
Sets the directory where cached files will be stored to \a cacheDir
132
133
QNetworkDiskCache will create this directory if it does not exists.
134
135
Prepared cache items will be stored in the new cache directory when
136
they are inserted.
137
138
\sa QStandardPaths::CacheLocation
139
*/
140
void
QNetworkDiskCache::setCacheDirectory(
const
QString &cacheDir)
141
{
142
#
if
defined
(
QNETWORKDISKCACHE_DEBUG
)
143
qDebug() <<
"QNetworkDiskCache::setCacheDirectory()"
<< cacheDir;
144
#
endif
145
Q_D(QNetworkDiskCache);
146
if
(cacheDir.isEmpty())
147
return
;
148
d->cacheDirectory = cacheDir;
149
QDir dir(d->cacheDirectory);
150
d->cacheDirectory = dir.absolutePath();
151
if
(!d->cacheDirectory.endsWith(u'/'))
152
d->cacheDirectory += u'/';
153
154
d->dataDirectory = d->cacheDirectory +
DATA_DIR
+ QString::number(
CACHE_VERSION
) + u'/';
155
d->prepareLayout();
156
}
157
158
/*!
159
\reimp
160
*/
161
qint64 QNetworkDiskCache::cacheSize()
const
162
{
163
#
if
defined
(
QNETWORKDISKCACHE_DEBUG
)
164
qDebug(
"QNetworkDiskCache::cacheSize()"
);
165
#
endif
166
Q_D(
const
QNetworkDiskCache);
167
if
(d->cacheDirectory.isEmpty())
168
return
0;
169
if
(d->currentCacheSize < 0) {
170
QNetworkDiskCache *that =
const_cast
<QNetworkDiskCache*>(
this
);
171
that->d_func()->currentCacheSize = that->expire();
172
}
173
return
d->currentCacheSize;
174
}
175
176
/*!
177
\reimp
178
*/
179
QIODevice *QNetworkDiskCache::prepare(
const
QNetworkCacheMetaData &metaData)
180
{
181
#
if
defined
(
QNETWORKDISKCACHE_DEBUG
)
182
qDebug() <<
"QNetworkDiskCache::prepare()"
<< metaData.url();
183
#
endif
184
Q_D(QNetworkDiskCache);
185
if
(!metaData.isValid() || !metaData.url().isValid() || !metaData.saveToDisk())
186
return
nullptr
;
187
188
if
(d->cacheDirectory.isEmpty()) {
189
qWarning(
"QNetworkDiskCache::prepare() The cache directory is not set"
);
190
return
nullptr
;
191
}
192
193
const
auto
sizeValue = metaData.headers().value(QHttpHeaders::WellKnownHeader::ContentLength);
194
const
qint64 size = sizeValue.toLongLong();
195
if
(size > (maximumCacheSize() * 3)/4)
196
return
nullptr
;
197
198
std::unique_ptr<QCacheItem> cacheItem = std::make_unique<QCacheItem>();
199
cacheItem->metaData = metaData;
200
201
QIODevice *device =
nullptr
;
202
if
(cacheItem->canCompress()) {
203
cacheItem->data.open(QBuffer::ReadWrite);
204
device = &(cacheItem->data);
205
}
else
{
206
QString fileName = d->cacheFileName(cacheItem->metaData.url());
207
cacheItem->file =
new
(std::nothrow) QSaveFile(fileName, &cacheItem->data);
208
if
(!cacheItem->file || !cacheItem->file->open(QFileDevice::WriteOnly)) {
209
qWarning(
"QNetworkDiskCache::prepare() unable to open temporary file"
);
210
cacheItem.reset();
211
return
nullptr
;
212
}
213
cacheItem->writeHeader(cacheItem->file);
214
device = cacheItem->file;
215
}
216
d->inserting[device] = cacheItem.release();
217
return
device;
218
}
219
220
/*!
221
\reimp
222
*/
223
void
QNetworkDiskCache::insert(QIODevice *device)
224
{
225
#
if
defined
(
QNETWORKDISKCACHE_DEBUG
)
226
qDebug() <<
"QNetworkDiskCache::insert()"
<< device;
227
#
endif
228
Q_D(QNetworkDiskCache);
229
const
auto
it = d->inserting.constFind(device);
230
if
(Q_UNLIKELY(it == d->inserting.cend())) {
231
qWarning() <<
"QNetworkDiskCache::insert() called on a device we don't know about"
<< device;
232
return
;
233
}
234
235
d->storeItem(it.value());
236
delete
it.value();
237
d->inserting.erase(it);
238
}
239
240
241
/*!
242
Create subdirectories and other housekeeping on the filesystem.
243
Prevents too many files from being present in any single directory.
244
*/
245
void
QNetworkDiskCachePrivate
::
prepareLayout
()
246
{
247
QDir helper;
248
static
constexpr
auto
dirPermissions =
249
QFileDevice::ReadOwner | QFileDevice::WriteOwner | QFileDevice::ExeOwner;
250
251
//Create directory and subdirectories 0-F
252
//Ensure the cache directory and any parents exist.
253
helper.mkpath(cacheDirectory);
254
// Create the data directory with owner-only permissions.
255
// If it already exists, tighten its permissions
256
if
(!helper.mkdir(dataDirectory, dirPermissions)
257
&& !QFile::setPermissions(dataDirectory, dirPermissions)) {
258
qWarning(
"QNetworkDiskCache::prepareLayout: could not create or set permissions on %ls"
,
259
qUtf16Printable(dataDirectory));
260
return
;
261
}
262
for
(uint i = 0; i < 16 ; i++) {
263
QString subdir = dataDirectory +
char16_t
(QtMiscUtils::toHexLower(i));
264
helper.mkdir(subdir, dirPermissions);
265
}
266
}
267
268
269
void
QNetworkDiskCachePrivate
::
storeItem
(QCacheItem *cacheItem)
270
{
271
Q_Q(QNetworkDiskCache);
272
Q_ASSERT(cacheItem->metaData.saveToDisk());
273
274
QString fileName = cacheFileName(cacheItem->metaData.url());
275
Q_ASSERT(!fileName.isEmpty());
276
277
if
(QFile::exists(fileName)) {
278
if
(!removeFile(fileName)) {
279
qWarning() <<
"QNetworkDiskCache: couldn't remove the cache file "
<< fileName;
280
return
;
281
}
282
}
283
284
currentCacheSize = q->expire();
285
if
(!cacheItem->file) {
286
cacheItem->file =
new
QSaveFile(fileName, &cacheItem->data);
287
if
(cacheItem->file->open(QFileDevice::WriteOnly)) {
288
cacheItem->writeHeader(cacheItem->file);
289
cacheItem->writeCompressedData(cacheItem->file);
290
}
291
}
292
293
if
(cacheItem->file
294
&& cacheItem->file->isOpen()
295
&& cacheItem->file->error() == QFileDevice::NoError) {
296
// We have to call size() here instead of inside the if-body because
297
// commit() invalidates the file-engine, and size() will create a new
298
// one, pointing at an empty filename.
299
qint64 size = cacheItem->file->size();
300
if
(cacheItem->file->commit())
301
currentCacheSize += size;
302
// Delete and unset the QSaveFile, it's invalid now.
303
delete
std
::exchange(cacheItem->file,
nullptr
);
304
}
305
if
(cacheItem->metaData.url() == lastItem.metaData.url())
306
lastItem.reset();
307
}
308
309
/*!
310
\reimp
311
*/
312
bool
QNetworkDiskCache::remove(
const
QUrl &url)
313
{
314
#
if
defined
(
QNETWORKDISKCACHE_DEBUG
)
315
qDebug() <<
"QNetworkDiskCache::remove()"
<< url;
316
#
endif
317
Q_D(QNetworkDiskCache);
318
319
// remove is also used to cancel insertions, not a common operation
320
for
(
auto
it = d->inserting.cbegin(), end = d->inserting.cend(); it != end; ++it) {
321
QCacheItem *item = it.value();
322
if
(item && item->metaData.url() == url) {
323
delete
item;
324
d->inserting.erase(it);
325
return
true
;
326
}
327
}
328
329
if
(d->lastItem.metaData.url() == url)
330
d->lastItem.reset();
331
return
d->removeFile(d->cacheFileName(url));
332
}
333
334
/*!
335
Put all of the misc file removing into one function to be extra safe
336
*/
337
bool
QNetworkDiskCachePrivate
::removeFile(
const
QString &file)
338
{
339
#
if
defined
(
QNETWORKDISKCACHE_DEBUG
)
340
qDebug() <<
"QNetworkDiskCache::removFile()"
<< file;
341
#
endif
342
if
(file.isEmpty())
343
return
false
;
344
QFileInfo info(file);
345
QString fileName = info.fileName();
346
if
(!fileName.endsWith(
CACHE_POSTFIX
))
347
return
false
;
348
qint64 size = info.size();
349
if
(QFile::remove(file)) {
350
currentCacheSize -= size;
351
return
true
;
352
}
353
return
false
;
354
}
355
356
/*!
357
\reimp
358
*/
359
QNetworkCacheMetaData QNetworkDiskCache::metaData(
const
QUrl &url)
360
{
361
#
if
defined
(
QNETWORKDISKCACHE_DEBUG
)
362
qDebug() <<
"QNetworkDiskCache::metaData()"
<< url;
363
#
endif
364
Q_D(QNetworkDiskCache);
365
if
(d->lastItem.metaData.url() == url)
366
return
d->lastItem.metaData;
367
return
fileMetaData(d->cacheFileName(url));
368
}
369
370
/*!
371
Returns the QNetworkCacheMetaData for the cache file \a fileName.
372
373
If \a fileName is not a cache file QNetworkCacheMetaData will be invalid.
374
*/
375
QNetworkCacheMetaData QNetworkDiskCache::fileMetaData(
const
QString &fileName)
const
376
{
377
#
if
defined
(
QNETWORKDISKCACHE_DEBUG
)
378
qDebug() <<
"QNetworkDiskCache::fileMetaData()"
<< fileName;
379
#
endif
380
Q_D(
const
QNetworkDiskCache);
381
QFile file(fileName);
382
if
(!file.open(QFile::ReadOnly))
383
return
QNetworkCacheMetaData();
384
if
(!d->lastItem.read(&file,
false
)) {
385
file.close();
386
QNetworkDiskCachePrivate *that =
const_cast
<QNetworkDiskCachePrivate*>(d);
387
that->removeFile(fileName);
388
}
389
return
d->lastItem.metaData;
390
}
391
392
/*!
393
\reimp
394
*/
395
QIODevice *QNetworkDiskCache::data(
const
QUrl &url)
396
{
397
#
if
defined
(
QNETWORKDISKCACHE_DEBUG
)
398
qDebug() <<
"QNetworkDiskCache::data()"
<< url;
399
#
endif
400
Q_D(QNetworkDiskCache);
401
std::unique_ptr<QBuffer> buffer;
402
if
(!url.isValid())
403
return
nullptr
;
404
if
(d->lastItem.metaData.url() == url && d->lastItem.data.isOpen()) {
405
buffer.reset(
new
QBuffer);
406
buffer->setData(d->lastItem.data.data());
407
}
else
{
408
QFile file(d->cacheFileName(url));
409
if
(!file.open(QFile::ReadOnly | QIODevice::Unbuffered))
410
return
nullptr
;
411
412
if
(!d->lastItem.read(&file,
true
)) {
413
file.close();
// On Windows the file can't be removed if it's open
414
remove(url);
415
return
nullptr
;
416
}
417
if
(d->lastItem.data.isOpen()) {
418
// compressed
419
buffer.reset(
new
QBuffer);
420
buffer->setData(d->lastItem.data.data());
421
}
else
{
422
buffer.reset(
new
QBuffer);
423
buffer->setData(file.readAll());
424
}
425
}
426
buffer->open(QBuffer::ReadOnly);
427
return
buffer.release();
428
}
429
430
/*!
431
\reimp
432
*/
433
void
QNetworkDiskCache::updateMetaData(
const
QNetworkCacheMetaData &metaData)
434
{
435
#
if
defined
(
QNETWORKDISKCACHE_DEBUG
)
436
qDebug() <<
"QNetworkDiskCache::updateMetaData()"
<< metaData.url();
437
#
endif
438
QUrl url = metaData.url();
439
QIODevice *oldDevice = data(url);
440
if
(!oldDevice) {
441
#
if
defined
(
QNETWORKDISKCACHE_DEBUG
)
442
qDebug(
"QNetworkDiskCache::updateMetaData(), no device!"
);
443
#
endif
444
return
;
445
}
446
447
QIODevice *newDevice = prepare(metaData);
448
if
(!newDevice) {
449
#
if
defined
(
QNETWORKDISKCACHE_DEBUG
)
450
qDebug() <<
"QNetworkDiskCache::updateMetaData(), no new device!"
<< url;
451
#
endif
452
return
;
453
}
454
char
data[1024];
455
while
(!oldDevice->atEnd()) {
456
qint64 s = oldDevice->read(data, 1024);
457
newDevice->write(data, s);
458
}
459
delete
oldDevice;
460
insert(newDevice);
461
}
462
463
/*!
464
Returns the current maximum size for the disk cache.
465
466
\sa setMaximumCacheSize()
467
*/
468
qint64 QNetworkDiskCache::maximumCacheSize()
const
469
{
470
Q_D(
const
QNetworkDiskCache);
471
return
d->maximumCacheSize;
472
}
473
474
/*!
475
Sets the maximum size of the disk cache to be \a size.
476
477
If the new size is smaller then the current cache size then the cache will call expire().
478
479
\sa maximumCacheSize()
480
*/
481
void
QNetworkDiskCache::setMaximumCacheSize(qint64 size)
482
{
483
Q_D(QNetworkDiskCache);
484
bool
expireCache = (size < d->maximumCacheSize);
485
d->maximumCacheSize = size;
486
if
(expireCache)
487
d->currentCacheSize = expire();
488
}
489
490
/*!
491
Cleans the cache so that its size is under the maximum cache size.
492
Returns the current size of the cache.
493
494
When the current size of the cache is greater than the maximumCacheSize()
495
older cache files are removed until the total size is less then 90% of
496
maximumCacheSize() starting with the oldest ones first using the file
497
creation date to determine how old a cache file is.
498
499
Subclasses can reimplement this function to change the order that cache
500
files are removed taking into account information in the application
501
knows about that QNetworkDiskCache does not, for example the number of times
502
a cache is accessed.
503
504
\note cacheSize() calls expire if the current cache size is unknown.
505
506
\sa maximumCacheSize(), fileMetaData()
507
*/
508
qint64 QNetworkDiskCache::expire()
509
{
510
Q_D(QNetworkDiskCache);
511
if
(d->currentCacheSize >= 0 && d->currentCacheSize < maximumCacheSize())
512
return
d->currentCacheSize;
513
514
if
(cacheDirectory().isEmpty()) {
515
qWarning(
"QNetworkDiskCache::expire() The cache directory is not set"
);
516
return
0;
517
}
518
519
// close file handle to prevent "in use" error when QFile::remove() is called
520
d->lastItem.reset();
521
522
struct
CacheItem
523
{
524
std::chrono::milliseconds msecs;
525
QString path;
526
qint64 size = 0;
527
};
528
std::vector<CacheItem> cacheItems;
529
qint64 totalSize = 0;
530
using
F = QDirListing::IteratorFlag;
531
for
(
const
auto
&dirEntry : QDirListing(cacheDirectory(), F::FilesOnly | F::Recursive)) {
532
if
(!dirEntry.fileName().endsWith(
CACHE_POSTFIX
))
533
continue
;
534
535
const
QFileInfo &info = dirEntry.fileInfo();
536
QDateTime fileTime = info.birthTime(QTimeZone::UTC);
537
if
(!fileTime.isValid())
538
fileTime = info.metadataChangeTime(QTimeZone::UTC);
539
const
std::chrono::milliseconds msecs{fileTime.toMSecsSinceEpoch()};
540
const
qint64 size = info.size();
541
cacheItems.push_back(CacheItem{msecs, info.filePath(), size});
542
totalSize += size;
543
}
544
545
const
qint64 goal = (maximumCacheSize() * 9) / 10;
546
if
(totalSize < goal)
547
return
totalSize;
// Nothing to do
548
549
auto
byFileTime = [&](
const
auto
&a,
const
auto
&b) {
return
a.msecs < b.msecs; };
550
std::sort(cacheItems.begin(), cacheItems.end(), byFileTime);
551
552
[[maybe_unused]]
int
removedFiles = 0;
// used under QNETWORKDISKCACHE_DEBUG
553
for
(
const
CacheItem &cached : cacheItems) {
554
QFile::remove(cached.path);
555
++removedFiles;
556
totalSize -= cached.size;
557
if
(totalSize < goal)
558
break
;
559
}
560
#
if
defined
(
QNETWORKDISKCACHE_DEBUG
)
561
if
(removedFiles > 0) {
562
qDebug() <<
"QNetworkDiskCache::expire()"
563
<<
"Removed:"
<< removedFiles
564
<<
"Kept:"
<< cacheItems.count() - removedFiles;
565
}
566
#
endif
567
return
totalSize;
568
}
569
570
/*!
571
\reimp
572
*/
573
void
QNetworkDiskCache::clear()
574
{
575
#
if
defined
(
QNETWORKDISKCACHE_DEBUG
)
576
qDebug(
"QNetworkDiskCache::clear()"
);
577
#
endif
578
Q_D(QNetworkDiskCache);
579
qint64 size = d->maximumCacheSize;
580
d->maximumCacheSize = 0;
581
d->currentCacheSize = expire();
582
d->maximumCacheSize = size;
583
}
584
585
/*!
586
Given a URL, generates a unique enough filename (and subdirectory)
587
*/
588
QString
QNetworkDiskCachePrivate
::uniqueFileName(
const
QUrl &url)
589
{
590
QUrl cleanUrl = url;
591
cleanUrl.setPassword(QString());
592
cleanUrl.setFragment(QString());
593
594
const
QByteArray
hash = QCryptographicHash::hash(cleanUrl.toEncoded(), QCryptographicHash::Sha1);
595
// convert sha1 to base36 form and return first 8 bytes for use as string
596
const
QByteArray
id = QByteArray::number(*(qlonglong*)hash.data(), 36).left(8);
597
// generates <one-char subdir>/<8-char filename.d>
598
uint code = (uint)id.at(id.size()-1) % 16;
599
QString pathFragment = QString::number(code, 16) + u'/' + QLatin1StringView(id) +
CACHE_POSTFIX
;
600
601
return
pathFragment;
602
}
603
604
/*!
605
Generates fully qualified path of cached resource from a URL.
606
*/
607
QString
QNetworkDiskCachePrivate
::cacheFileName(
const
QUrl &url)
const
608
{
609
if
(!url.isValid())
610
return
QString();
611
612
QString fullpath = dataDirectory + uniqueFileName(url);
613
return
fullpath;
614
}
615
616
/*!
617
\class QCacheItem
618
\internal
619
*/
620
621
/*!
622
We compress small text and JavaScript files.
623
*/
624
bool
QCacheItem::canCompress()
const
625
{
626
const
auto
h = metaData.headers();
627
628
const
auto
sizeValue = h.value(QHttpHeaders::WellKnownHeader::ContentLength);
629
if
(sizeValue.empty())
630
return
false
;
631
632
qint64 size = sizeValue.toLongLong();
633
if
(size >
MAX_COMPRESSION_SIZE
)
634
return
false
;
635
636
const
auto
type = h.value(QHttpHeaders::WellKnownHeader::ContentType);
637
if
(type.empty())
638
return
false
;
639
640
if
(!type.startsWith(
"text/"
)
641
&& !(type.startsWith(
"application/"
)
642
&& (type.endsWith(
"javascript"
) || type.endsWith(
"ecmascript"
)))) {
643
return
false
;
644
}
645
646
return
true
;
647
}
648
649
enum
650
{
651
CacheMagic
= 0xe8,
652
CurrentCacheVersion
=
CACHE_VERSION
653
};
654
655
void
QCacheItem::writeHeader(QFileDevice *device)
const
656
{
657
QDataStream out(device);
658
659
out << qint32(
CacheMagic
);
660
out << qint32(
CurrentCacheVersion
);
661
out <<
static_cast
<qint32>(out.version());
662
out << metaData;
663
bool
compressed = canCompress();
664
out << compressed;
665
}
666
667
void
QCacheItem::writeCompressedData(QFileDevice *device)
const
668
{
669
QDataStream out(device);
670
671
out << qCompress(data.data());
672
}
673
674
/*!
675
Returns \c false if the file is a cache file,
676
but is an older version and should be removed otherwise true.
677
*/
678
bool
QCacheItem::read(QFileDevice *device,
bool
readData)
679
{
680
reset();
681
682
QDataStream in(device);
683
684
qint32 marker;
685
qint32 v;
686
in >> marker;
687
in >> v;
688
if
(marker !=
CacheMagic
)
689
return
true
;
690
691
// If the cache magic is correct, but the version is not we should remove it
692
if
(v !=
CurrentCacheVersion
)
693
return
false
;
694
695
qint32 streamVersion;
696
in >> streamVersion;
697
// Default stream version is also the highest we can handle
698
if
(streamVersion > in.version())
699
return
false
;
700
in.setVersion(streamVersion);
701
702
bool
compressed;
703
QByteArray
dataBA;
704
in >> metaData;
705
in >> compressed;
706
if
(readData && compressed) {
707
in >> dataBA;
708
data.setData(qUncompress(dataBA));
709
data.open(QBuffer::ReadOnly);
710
}
711
712
// quick and dirty check if metadata's URL field and the file's name are in synch
713
QString expectedFilename = QNetworkDiskCachePrivate::uniqueFileName(metaData.url());
714
if
(!device->fileName().endsWith(expectedFilename))
715
return
false
;
716
717
return
metaData.isValid() && !metaData.headers().isEmpty();
718
}
719
720
QT_END_NAMESPACE
721
722
#
include
"moc_qnetworkdiskcache.cpp"
QByteArray::FromBase64Result
\inmodule QtCore
Definition
qbytearray.h:824
QNetworkDiskCachePrivate
Definition
qnetworkdiskcache_p.h:59
QNetworkDiskCachePrivate::prepareLayout
void prepareLayout()
Create subdirectories and other housekeeping on the filesystem.
Definition
qnetworkdiskcache.cpp:245
QNetworkDiskCachePrivate::storeItem
void storeItem(QCacheItem *item)
Definition
qnetworkdiskcache.cpp:269
QT_BEGIN_NAMESPACE
Combined button and popup list for selecting options.
Definition
qsequentialanimationgroup.cpp:47
std
[33]
Definition
src_corelib_tools_qhash.cpp:421
CACHE_VERSION
#define CACHE_VERSION
Definition
qnetworkdiskcache.cpp:25
DATA_DIR
#define DATA_DIR
Definition
qnetworkdiskcache.cpp:26
CACHE_POSTFIX
#define CACHE_POSTFIX
Definition
qnetworkdiskcache.cpp:24
CurrentCacheVersion
@ CurrentCacheVersion
Definition
qnetworkdiskcache.cpp:652
CacheMagic
@ CacheMagic
Definition
qnetworkdiskcache.cpp:651
MAX_COMPRESSION_SIZE
#define MAX_COMPRESSION_SIZE
Definition
qnetworkdiskcache.cpp:28
qtbase
src
network
access
qnetworkdiskcache.cpp
Generated on
for Qt by
1.18.0