diff --git a/autotests/kfileitemtest.h b/autotests/kfileitemtest.h --- a/autotests/kfileitemtest.h +++ b/autotests/kfileitemtest.h @@ -36,6 +36,7 @@ void testHiddenFile(); void testMimeTypeOnDemand(); void testCmp(); + void testCmpAndInit(); void testCmpByUrl(); void testRename(); void testRefresh(); diff --git a/autotests/kfileitemtest.cpp b/autotests/kfileitemtest.cpp --- a/autotests/kfileitemtest.cpp +++ b/autotests/kfileitemtest.cpp @@ -294,6 +294,33 @@ QVERIFY(fileItem.cmp(fileItem2)); } +void KFileItemTest::testCmpAndInit() +{ + QTemporaryDir tempDir; + KFileItem dirItem(QUrl::fromLocalFile(tempDir.path())); + QVERIFY(dirItem.isDir()); // this calls init() + + KFileItem dirItem2(QUrl::fromLocalFile(tempDir.path())); + // not yet init() called on dirItem2, but must be equal + // compare init()ialized to un-init()ialized KFileItem + QVERIFY(dirItem.cmp(dirItem2)); + QVERIFY(dirItem2.isDir()); + QVERIFY(dirItem.cmp(dirItem2)); + QVERIFY(dirItem == dirItem2); + QVERIFY(dirItem.d != dirItem2.d); + QVERIFY(!(dirItem != dirItem2)); + + // now the other way around, compare un-init()ialized to init()ialized KFileItem + KFileItem dirItem3(QUrl::fromLocalFile(tempDir.path())); + // not yet init() called on dirItem3, but must be equal + QVERIFY(dirItem3.cmp(dirItem)); + QVERIFY(dirItem3.isDir()); + QVERIFY(dirItem3.cmp(dirItem)); + QVERIFY(dirItem == dirItem3); + QVERIFY(dirItem.d != dirItem3.d); + QVERIFY(!(dirItem != dirItem3)); +} + void KFileItemTest::testCmpByUrl() { const QUrl nulUrl; diff --git a/src/core/kfileitem.h b/src/core/kfileitem.h --- a/src/core/kfileitem.h +++ b/src/core/kfileitem.h @@ -63,6 +63,11 @@ //ChangeTime }; + enum MimeTypeDetermination { + NormalMimeTypeDetermination = 0, + SkipMimeTypeFromContent + }; + /** * Null KFileItem. Doesn't represent any file, only exists for convenience. */ @@ -115,6 +120,20 @@ */ KFileItem(const QUrl &url, const QString &mimeType = QString(), mode_t mode = KFileItem::Unknown); // KF6 TODO: explicit! + /** + * Creates an item representing a file, with the option of skipping mime type determination. + * @param url the file url + * @param mimeTypeDetermination the mode of determining the mime type: + * NormalMimeTypeDetermination by content if local file, i.e. access the file, + * open and read part of it; + * by QMimeDatabase::MatchMode::MatchExtension if not local. + * SkipMimeTypeFromContent always by QMimeDatabase::MatchMode::MatchExtension, + * i.e. won't access the file by stat() or opening it; + * only suitable for files, directories won't be recognized. + * @since 5.57 + */ + KFileItem(const QUrl &url, KFileItem::MimeTypeDetermination mimeTypeDetermination); + /** * Copy constructor */ diff --git a/src/core/kfileitem.cpp b/src/core/kfileitem.cpp --- a/src/core/kfileitem.cpp +++ b/src/core/kfileitem.cpp @@ -51,7 +51,8 @@ mode_t mode, mode_t permissions, const QUrl &itemOrDirUrl, bool urlIsDirectory, - bool delayedMimeTypes) + bool delayedMimeTypes, + KFileItem::MimeTypeDetermination mimeTypeDetermination) : m_entry(entry), m_url(itemOrDirUrl), m_strName(), @@ -67,25 +68,28 @@ m_delayedMimeTypes(delayedMimeTypes), m_useIconNameCache(false), m_hidden(Auto), - m_slow(SlowUnknown) + m_slow(SlowUnknown), + m_bSkipMimeTypeFromContent(mimeTypeDetermination == KFileItem::SkipMimeTypeFromContent), + m_bInitCalled(false) { if (entry.count() != 0) { readUDSEntry(urlIsDirectory); } else { Q_ASSERT(!urlIsDirectory); m_strName = itemOrDirUrl.fileName(); m_strText = KIO::decodeFileName(m_strName); } - init(); } /** - * Computes the text and mode from the UDSEntry - * Called by constructor, but can be called again later - * Nothing does that anymore though (I guess some old KonqFileItem did) - * so it's not a protected method of the public class anymore. + * Call init() if not yet done. + */ + void ensureInitialized() const; + + /** + * Computes the text and mode from the UDSEntry. */ - void init(); + void init() const; QString localPath() const; KIO::filesize_t size() const; @@ -106,6 +110,11 @@ */ QString parsePermissions(mode_t perm) const; + /** + * Mime type helper + */ + void determineMimeTypeHelper(const QUrl &url) const; + /** * The UDSEntry that contains the data for this fileitem, if it came from a directory listing. */ @@ -144,16 +153,16 @@ /** * The file mode */ - mode_t m_fileMode; + mutable mode_t m_fileMode; /** * The permissions */ - mode_t m_permissions; + mutable mode_t m_permissions; /** * Whether the file is a link */ - bool m_bLink: 1; + mutable bool m_bLink: 1; /** * True if local file */ @@ -171,18 +180,35 @@ // Slow? (nfs/smb/ssh) mutable enum { SlowUnknown, Fast, Slow } m_slow: 3; + /** + * True if mime type determination by content should be skipped + */ + bool m_bSkipMimeTypeFromContent: 1; + + /** + * True if init() was called on demand + */ + mutable bool m_bInitCalled: 1; + // For special case like link to dirs over FTP QString m_guessedMimeType; mutable QString m_access; + }; -void KFileItemPrivate::init() +void KFileItemPrivate::ensureInitialized() const +{ + if (!m_bInitCalled) { + init(); + } +} + +void KFileItemPrivate::init() const { m_access.clear(); // metaInfo = KFileMetaInfo(); // stat() local files if needed - // TODO: delay this until requested if (m_fileMode == KFileItem::Unknown || m_permissions == KFileItem::Unknown || m_entry.count() == 0) { if (m_url.isLocalFile()) { /* directories may not have a slash at the end if @@ -229,7 +255,10 @@ } } } + + m_bInitCalled = true; } + void KFileItemPrivate::readUDSEntry(bool _urlIsDirectory) { // extract fields from the KIO::UDS Entry @@ -276,6 +305,8 @@ inline //because it is used only in one place KIO::filesize_t KFileItemPrivate::size() const { + ensureInitialized(); + // Extract it from the KIO::UDSEntry long long fieldVal = m_entry.numberValue(KIO::UDSEntry::UDS_SIZE, -1); if (fieldVal != -1) { @@ -315,6 +346,8 @@ QDateTime KFileItemPrivate::time(KFileItem::FileTimes mappedWhich) const { + ensureInitialized(); + // Extract it from the KIO::UDSEntry const uint uds = udsFieldForTime(mappedWhich); if (uds > 0) { @@ -330,6 +363,14 @@ inline //because it is used only in one place bool KFileItemPrivate::cmp(const KFileItemPrivate &item) const { + if (item.m_bInitCalled) { + ensureInitialized(); + } + + if (m_bInitCalled) { + item.ensureInitialized(); + } + #if 0 //qDebug() << "Comparing" << m_url << "and" << item.m_url; //qDebug() << " name" << (m_strName == item.m_strName); @@ -368,6 +409,8 @@ inline //because it is used only in one place QString KFileItemPrivate::parsePermissions(mode_t perm) const { + ensureInitialized(); + static char buffer[ 12 ]; char uxbit, gxbit, oxbit; @@ -448,6 +491,20 @@ return QString::fromLatin1(buffer); } +void KFileItemPrivate::determineMimeTypeHelper(const QUrl &url) const +{ + QMimeDatabase db; + if (m_bSkipMimeTypeFromContent) { + const QString scheme = url.scheme(); + if (scheme.startsWith(QLatin1String("http")) || scheme == QLatin1String("mailto")) + m_mimeType = db.mimeTypeForName(QLatin1String("application/octet-stream")); + else + m_mimeType = db.mimeTypeForFile(url.path(), QMimeDatabase::MatchMode::MatchExtension); + } else { + m_mimeType = db.mimeTypeForUrl(url); + } +} + /////// KFileItem::KFileItem() @@ -457,27 +514,34 @@ KFileItem::KFileItem(const KIO::UDSEntry &entry, const QUrl &itemOrDirUrl, bool delayedMimeTypes, bool urlIsDirectory) - : d(new KFileItemPrivate(entry, KFileItem::Unknown, KFileItem::Unknown, itemOrDirUrl, urlIsDirectory, delayedMimeTypes)) + : d(new KFileItemPrivate(entry, KFileItem::Unknown, KFileItem::Unknown, itemOrDirUrl, urlIsDirectory, delayedMimeTypes, KFileItem::NormalMimeTypeDetermination)) { } KFileItem::KFileItem(mode_t mode, mode_t permissions, const QUrl &url, bool delayedMimeTypes) : d(new KFileItemPrivate(KIO::UDSEntry(), mode, permissions, - url, false, delayedMimeTypes)) + url, false, delayedMimeTypes, KFileItem::NormalMimeTypeDetermination)) { } KFileItem::KFileItem(const QUrl &url, const QString &mimeType, mode_t mode) : d(new KFileItemPrivate(KIO::UDSEntry(), mode, KFileItem::Unknown, - url, false, false)) + url, false, false, KFileItem::NormalMimeTypeDetermination)) { d->m_bMimeTypeKnown = !mimeType.isEmpty(); if (d->m_bMimeTypeKnown) { QMimeDatabase db; d->m_mimeType = db.mimeTypeForName(mimeType); } } +KFileItem::KFileItem(const QUrl &url, KFileItem::MimeTypeDetermination mimeTypeDetermination) + : d(new KFileItemPrivate(KIO::UDSEntry(), KFileItem::Unknown, KFileItem::Unknown, + url, false, false, mimeTypeDetermination)) +{ +} + + // Default implementations for: // - Copy constructor // - Move constructor @@ -558,6 +622,8 @@ return; } + d->ensureInitialized(); + d->m_strName = name; if (!d->m_strName.isEmpty()) { d->m_strText = KIO::decodeFileName(d->m_strName); @@ -574,6 +640,8 @@ return QString(); } + d->ensureInitialized(); + // Extract it from the KIO::UDSEntry const QString linkStr = d->m_entry.stringValue(KIO::UDSEntry::UDS_LINK_DEST); if (!linkStr.isEmpty()) { @@ -593,6 +661,8 @@ return m_url.toLocalFile(); } + ensureInitialized(); + // Extract the local path from the KIO::UDSEntry return m_entry.stringValue(KIO::UDSEntry::UDS_LOCAL_PATH); } @@ -622,7 +692,7 @@ } // Check if the field exists; its value doesn't matter - return d->m_entry.contains(KIO::UDSEntry::UDS_EXTENDED_ACL); + return entry().contains(KIO::UDSEntry::UDS_EXTENDED_ACL); } KACL KFileItem::ACL() const @@ -638,6 +708,7 @@ return KACL(fieldVal); } } + // create one from the basic permissions return KACL(d->m_permissions); } @@ -649,7 +720,7 @@ } // Extract it from the KIO::UDSEntry - const QString fieldVal = d->m_entry.stringValue(KIO::UDSEntry::UDS_DEFAULT_ACL_STRING); + const QString fieldVal = entry().stringValue(KIO::UDSEntry::UDS_DEFAULT_ACL_STRING); if (!fieldVal.isEmpty()) { return KACL(fieldVal); } else { @@ -672,16 +743,16 @@ return QString(); } - return d->m_entry.stringValue(KIO::UDSEntry::UDS_USER); + return entry().stringValue(KIO::UDSEntry::UDS_USER); } QString KFileItem::group() const { if (!d) { return QString(); } - return d->m_entry.stringValue(KIO::UDSEntry::UDS_GROUP); + return entry().stringValue(KIO::UDSEntry::UDS_GROUP); } bool KFileItemPrivate::isSlow() const @@ -730,7 +801,8 @@ } else { bool isLocalUrl; const QUrl url = mostLocalUrl(&isLocalUrl); - d->m_mimeType = db.mimeTypeForUrl(url); + d->determineMimeTypeHelper(url); + // was: d->m_mimeType = KMimeType::findByUrl( url, d->m_fileMode, isLocalUrl ); // => we are no longer using d->m_fileMode for remote URLs. Q_ASSERT(d->m_mimeType.isValid()); @@ -983,6 +1055,8 @@ return QStringList(); } + d->ensureInitialized(); + QStringList names = d->m_entry.stringValue(KIO::UDSEntry::UDS_ICON_OVERLAY_NAMES).split(QLatin1Char(','), QString::SkipEmptyParts); if (d->m_bLink) { @@ -1049,6 +1123,8 @@ return false; } + d->ensureInitialized(); + /* struct passwd * user = getpwuid( geteuid() ); bool isMyFile = (QString::fromLocal8Bit(user->pw_name) == d->m_user); @@ -1084,6 +1160,8 @@ return false; } + d->ensureInitialized(); + /* struct passwd * user = getpwuid( geteuid() ); bool isMyFile = (QString::fromLocal8Bit(user->pw_name) == d->m_user); @@ -1139,6 +1217,12 @@ return false; } + if (d->m_bSkipMimeTypeFromContent) { + return false; + } + + d->ensureInitialized(); + if (d->m_fileMode == KFileItem::Unknown) { // Probably the file was deleted already, and KDirLister hasn't told the world yet. //qDebug() << d << url() << "can't say -> false"; @@ -1269,6 +1353,8 @@ return QString(); } + d->ensureInitialized(); + if (d->m_access.isNull() && d->m_permissions != KFileItem::Unknown) { d->m_access = d->parsePermissions(d->m_permissions); } @@ -1393,6 +1479,8 @@ return 0; } + d->ensureInitialized(); + return d->m_permissions; } @@ -1402,6 +1490,8 @@ return 0; } + d->ensureInitialized(); + return d->m_fileMode; } @@ -1411,6 +1501,8 @@ return false; } + d->ensureInitialized(); + return d->m_bLink; } @@ -1499,7 +1591,7 @@ } } else { // ## d->m_fileMode isn't used anymore (for remote urls) - d->m_mimeType = db.mimeTypeForUrl(url); + d->determineMimeTypeHelper(url); d->m_bMimeTypeKnown = true; } } @@ -1512,6 +1604,8 @@ return KIO::UDSEntry(); } + d->ensureInitialized(); + return d->m_entry; } @@ -1586,6 +1680,8 @@ return false; } + d->ensureInitialized(); + return (d->m_fileMode & QT_STAT_MASK) == QT_STAT_REG; }