diff --git a/autotests/kfileitemtest.cpp b/autotests/kfileitemtest.cpp index 24dad633..b897e10e 100644 --- a/autotests/kfileitemtest.cpp +++ b/autotests/kfileitemtest.cpp @@ -1,755 +1,782 @@ /* This file is part of the KDE project Copyright (C) 2006 David Faure This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kfileitemtest.h" #include #include #include #include #include #include #include #include #include "kiotesthelper.h" #include QTEST_MAIN(KFileItemTest) void KFileItemTest::initTestCase() { } void KFileItemTest::testPermissionsString() { // Directory QTemporaryDir tempDir; KFileItem dirItem(QUrl::fromLocalFile(tempDir.path() + '/')); QCOMPARE((uint)dirItem.permissions(), (uint)0700); QCOMPARE(dirItem.permissionsString(), QStringLiteral("drwx------")); QVERIFY(dirItem.isReadable()); // File QFile file(tempDir.path() + "/afile"); QVERIFY(file.open(QIODevice::WriteOnly)); file.setPermissions(QFile::ReadOwner | QFile::WriteOwner | QFile::ReadOther); // 0604 KFileItem fileItem(QUrl::fromLocalFile(file.fileName()), QString(), KFileItem::Unknown); QCOMPARE((uint)fileItem.permissions(), (uint)0604); QCOMPARE(fileItem.permissionsString(), QStringLiteral("-rw----r--")); QVERIFY(fileItem.isReadable()); // Symlink to file QString symlink = tempDir.path() + "/asymlink"; QVERIFY(file.link(symlink)); QUrl symlinkUrl = QUrl::fromLocalFile(symlink); KFileItem symlinkItem(symlinkUrl, QString(), KFileItem::Unknown); QCOMPARE((uint)symlinkItem.permissions(), (uint)0604); // This is a bit different from "ls -l": we get the 'l' but we see the permissions of the target. // This is actually useful though; the user sees it's a link, and can check if he can read the [target] file. QCOMPARE(symlinkItem.permissionsString(), QStringLiteral("lrw----r--")); QVERIFY(symlinkItem.isReadable()); // Symlink to directory (#162544) QVERIFY(QFile::remove(symlink)); QVERIFY(QFile(tempDir.path() + '/').link(symlink)); KFileItem symlinkToDirItem(symlinkUrl, QString(), KFileItem::Unknown); QCOMPARE((uint)symlinkToDirItem.permissions(), (uint)0700); QCOMPARE(symlinkToDirItem.permissionsString(), QStringLiteral("lrwx------")); } void KFileItemTest::testNull() { KFileItem null; QVERIFY(null.isNull()); KFileItem fileItem(QUrl::fromLocalFile(QStringLiteral("/")), QString(), KFileItem::Unknown); QVERIFY(!fileItem.isNull()); null = fileItem; // ok, now 'null' isn't so null anymore QVERIFY(!null.isNull()); QVERIFY(null.isReadable()); QVERIFY(!null.isHidden()); } void KFileItemTest::testDoesNotExist() { KFileItem fileItem(QUrl::fromLocalFile(QStringLiteral("/doesnotexist")), QString(), KFileItem::Unknown); QVERIFY(!fileItem.isNull()); QVERIFY(!fileItem.isReadable()); QVERIFY(fileItem.user().isEmpty()); QVERIFY(fileItem.group().isEmpty()); } void KFileItemTest::testDetach() { KFileItem fileItem(QUrl::fromLocalFile(QStringLiteral("/one")), QString(), KFileItem::Unknown); QCOMPARE(fileItem.name(), QStringLiteral("one")); KFileItem fileItem2(fileItem); QVERIFY(fileItem == fileItem2); QVERIFY(fileItem.d == fileItem2.d); fileItem2.setName(QStringLiteral("two")); QCOMPARE(fileItem2.name(), QStringLiteral("two")); QCOMPARE(fileItem.name(), QStringLiteral("one")); // it detached QVERIFY(fileItem == fileItem2); QVERIFY(fileItem.d != fileItem2.d); fileItem = fileItem2; QCOMPARE(fileItem.name(), QStringLiteral("two")); QVERIFY(fileItem == fileItem2); QVERIFY(fileItem.d == fileItem2.d); QVERIFY(!(fileItem != fileItem2)); } void KFileItemTest::testMove() { // Test move constructor { KFileItem fileItem(QUrl::fromLocalFile(QStringLiteral("/one")), QString(), KFileItem::Unknown); QCOMPARE(fileItem.name(), QStringLiteral("one")); KFileItem fileItem2(std::move(fileItem)); QCOMPARE(fileItem2.name(), QStringLiteral("one")); } // Test move assignment { KFileItem fileItem(QUrl::fromLocalFile(QStringLiteral("/one")), QString(), KFileItem::Unknown); QCOMPARE(fileItem.name(), QStringLiteral("one")); KFileItem fileItem2(QUrl::fromLocalFile(QStringLiteral("/two")), QString(), KFileItem::Unknown); fileItem2 = std::move(fileItem); QCOMPARE(fileItem2.name(), QStringLiteral("one")); } // Now to test some value changes to make sure moving works as intended. KFileItem fileItem(QUrl::fromLocalFile(QStringLiteral("/one")), QString(), KFileItem::Unknown); QCOMPARE(fileItem.name(), QStringLiteral("one")); fileItem.setUrl(QUrl::fromLocalFile(QStringLiteral("/two"))); QCOMPARE(fileItem.name(), QStringLiteral("two")); // Move fileitem to fileItem2, it should now contain everything fileItem had. // Just testing a property to make sure it does. KFileItem fileItem2(std::move(fileItem)); QCOMPARE(fileItem2.name(), QStringLiteral("two")); } void KFileItemTest::testBasic() { QTemporaryFile file; QVERIFY(file.open()); QFile fileObj(file.fileName()); QVERIFY(fileObj.open(QIODevice::WriteOnly)); fileObj.write(QByteArray("Hello")); fileObj.close(); QUrl url = QUrl::fromLocalFile(file.fileName()); KFileItem fileItem(url, QString(), KFileItem::Unknown); QCOMPARE(fileItem.text(), url.fileName()); QVERIFY(fileItem.isLocalFile()); QCOMPARE(fileItem.localPath(), url.toLocalFile()); QCOMPARE(fileItem.size(), KIO::filesize_t(5)); QVERIFY(fileItem.linkDest().isEmpty()); QVERIFY(!fileItem.isHidden()); QVERIFY(fileItem.isReadable()); QVERIFY(fileItem.isWritable()); QVERIFY(fileItem.isFile()); QVERIFY(!fileItem.isDir()); QVERIFY(!fileItem.isDesktopFile()); #ifndef Q_OS_WIN QCOMPARE(fileItem.user(), KUser().loginName()); #endif } void KFileItemTest::testRootDirectory() { const QString rootPath = QDir::rootPath(); QUrl url = QUrl::fromLocalFile(rootPath); KIO::UDSEntry entry; entry.fastInsert(KIO::UDSEntry::UDS_NAME, QStringLiteral(".")); entry.fastInsert(KIO::UDSEntry::UDS_FILE_TYPE, S_IFDIR); KFileItem fileItem(entry, url); QCOMPARE(fileItem.text(), QStringLiteral(".")); QVERIFY(fileItem.isLocalFile()); QCOMPARE(fileItem.localPath(), url.toLocalFile()); QVERIFY(fileItem.linkDest().isEmpty()); QVERIFY(!fileItem.isHidden()); QVERIFY(!fileItem.isFile()); QVERIFY(fileItem.isDir()); QVERIFY(!fileItem.isDesktopFile()); } void KFileItemTest::testHiddenFile() { QTemporaryDir tempDir; QFile file(tempDir.path() + "/.hiddenfile"); QVERIFY(file.open(QIODevice::WriteOnly)); KFileItem fileItem(QUrl::fromLocalFile(file.fileName()), QString(), KFileItem::Unknown); QCOMPARE(fileItem.text(), QStringLiteral(".hiddenfile")); QVERIFY(fileItem.isLocalFile()); QVERIFY(fileItem.isHidden()); } void KFileItemTest::testMimeTypeOnDemand() { QTemporaryFile file; QVERIFY(file.open()); { KFileItem fileItem(QUrl::fromLocalFile(file.fileName())); fileItem.setDelayedMimeTypes(true); QVERIFY(fileItem.currentMimeType().isDefault()); QVERIFY(!fileItem.isMimeTypeKnown()); QVERIFY(!fileItem.isFinalIconKnown()); //qDebug() << fileItem.determineMimeType().name(); QCOMPARE(fileItem.determineMimeType().name(), QStringLiteral("application/x-zerosize")); QCOMPARE(fileItem.mimetype(), QStringLiteral("application/x-zerosize")); QVERIFY(fileItem.isMimeTypeKnown()); QVERIFY(fileItem.isFinalIconKnown()); } { // Calling mimeType directly also does mimetype determination KFileItem fileItem(QUrl::fromLocalFile(file.fileName())); fileItem.setDelayedMimeTypes(true); QVERIFY(!fileItem.isMimeTypeKnown()); QCOMPARE(fileItem.mimetype(), QStringLiteral("application/x-zerosize")); QVERIFY(fileItem.isMimeTypeKnown()); } { // Calling overlays should NOT do mimetype determination (#237668) KFileItem fileItem(QUrl::fromLocalFile(file.fileName())); fileItem.setDelayedMimeTypes(true); QVERIFY(!fileItem.isMimeTypeKnown()); fileItem.overlays(); QVERIFY(!fileItem.isMimeTypeKnown()); } { QTemporaryFile file; QVERIFY(file.open()); // Check whether mime-magic is used. // No known extension, so it should be used by determineMimeType. file.write(QByteArray("%PDF-")); QString fileName = file.fileName(); QVERIFY(!fileName.isEmpty()); file.close(); KFileItem fileItem(QUrl::fromLocalFile(fileName)); fileItem.setDelayedMimeTypes(true); QCOMPARE(fileItem.currentMimeType().name(), QLatin1String("application/octet-stream")); QVERIFY(fileItem.currentMimeType().isValid()); QVERIFY(fileItem.currentMimeType().isDefault()); QVERIFY(!fileItem.isMimeTypeKnown()); QCOMPARE(fileItem.determineMimeType().name(), QStringLiteral("application/pdf")); QCOMPARE(fileItem.mimetype(), QStringLiteral("application/pdf")); } { QTemporaryFile file(QDir::tempPath() + QLatin1String("/kfileitemtest_XXXXXX.txt")); QVERIFY(file.open()); // Check whether mime-magic is used. // Known extension, so it should NOT be used. file.write(QByteArray("("filename"); QTest::addColumn("expectedText"); QTest::newRow("simple") << "filename" << "filename"; QTest::newRow("/ at end") << QString(QStringLiteral("foo") + QChar(0x2044)) << QString(QStringLiteral("foo") + QChar(0x2044)); QTest::newRow("/ at begin") << QString(QChar(0x2044)) << QString(QChar(0x2044)); } void KFileItemTest::testDecodeFileName() { QFETCH(QString, filename); QFETCH(QString, expectedText); QCOMPARE(KIO::decodeFileName(filename), expectedText); } void KFileItemTest::testEncodeFileName_data() { QTest::addColumn("text"); QTest::addColumn("expectedFileName"); QTest::newRow("simple") << "filename" << "filename"; QTest::newRow("/ at end") << "foo/" << QString(QStringLiteral("foo") + QChar(0x2044)); QTest::newRow("/ at begin") << "/" << QString(QChar(0x2044)); } void KFileItemTest::testEncodeFileName() { QFETCH(QString, text); QFETCH(QString, expectedFileName); QCOMPARE(KIO::encodeFileName(text), expectedFileName); } void KFileItemTest::testListProperties_data() { QTest::addColumn("itemDescriptions"); QTest::addColumn("expectedReading"); QTest::addColumn("expectedDeleting"); QTest::addColumn("expectedIsLocal"); QTest::addColumn("expectedIsDirectory"); QTest::addColumn("expectedIsFile"); QTest::addColumn("expectedMimeType"); QTest::addColumn("expectedMimeGroup"); QTest::newRow("one file") << "f" << true << true << true << false << true << "text/plain" << "text"; QTest::newRow("one dir") << "d" << true << true << true << true << false << "inode/directory" << "inode"; QTest::newRow("root dir") << "/" << true << false << true << true << false << "inode/directory" << "inode"; QTest::newRow("file+dir") << "fd" << true << true << true << false << false << "" << ""; QTest::newRow("two dirs") << "dd" << true << true << true << true << false << "inode/directory" << "inode"; QTest::newRow("dir+root dir") << "d/" << true << false << true << true << false << "inode/directory" << "inode"; QTest::newRow("two (text+html) files") << "ff" << true << true << true << false << true << "" << "text"; QTest::newRow("three (text+html+empty) files") << "fff" << true << true << true << false << true << "" << ""; QTest::newRow("http url") << "h" << true << true /*says kio_http...*/ << false << false << true << "application/octet-stream" << "application"; QTest::newRow("2 http urls") << "hh" << true << true /*says kio_http...*/ << false << false << true << "application/octet-stream" << "application"; } void KFileItemTest::testListProperties() { QFETCH(QString, itemDescriptions); QFETCH(bool, expectedReading); QFETCH(bool, expectedDeleting); QFETCH(bool, expectedIsLocal); QFETCH(bool, expectedIsDirectory); QFETCH(bool, expectedIsFile); QFETCH(QString, expectedMimeType); QFETCH(QString, expectedMimeGroup); QTemporaryDir tempDir; QDir baseDir(tempDir.path()); KFileItemList items; for (int i = 0; i < itemDescriptions.size(); ++i) { QString fileName = tempDir.path() + "/file" + QString::number(i); switch (itemDescriptions[i].toLatin1()) { case 'f': { if (i == 1) { // 2nd file is html fileName += QLatin1String(".html"); } QFile file(fileName); QVERIFY(file.open(QIODevice::WriteOnly)); if (i != 2) { // 3rd file is empty file.write("Hello"); } items << KFileItem(QUrl::fromLocalFile(fileName), QString(), KFileItem::Unknown); } break; case 'd': QVERIFY(baseDir.mkdir(fileName)); items << KFileItem(QUrl::fromLocalFile(fileName), QString(), KFileItem::Unknown); break; case '/': items << KFileItem(QUrl::fromLocalFile(QStringLiteral("/")), QString(), KFileItem::Unknown); break; case 'h': items << KFileItem(QUrl(QStringLiteral("http://www.kde.org")), QString(), KFileItem::Unknown); break; default: QVERIFY(false); } } KFileItemListProperties props(items); QCOMPARE(props.supportsReading(), expectedReading); QCOMPARE(props.supportsDeleting(), expectedDeleting); QCOMPARE(props.isLocal(), expectedIsLocal); QCOMPARE(props.isDirectory(), expectedIsDirectory); QCOMPARE(props.isFile(), expectedIsFile); QCOMPARE(props.mimeType(), expectedMimeType); QCOMPARE(props.mimeGroup(), expectedMimeGroup); } void KFileItemTest::testIconNameForUrl_data() { QTest::addColumn("url"); QTest::addColumn("expectedIcon"); QTest::newRow("root") << QUrl("file:/") << "folder"; // the icon comes from KProtocolInfo if (QFile::exists(QStringLiteral("/tmp"))) { QTest::newRow("subdir") << QUrl::fromLocalFile("/tmp") << "folder-temp"; } QTest::newRow("home") << QUrl::fromLocalFile(QDir::homePath()) << "user-home"; const QString moviesPath = QStandardPaths::standardLocations(QStandardPaths::MoviesLocation).first(); if (QFileInfo::exists(moviesPath)) { QTest::newRow("videos") << QUrl::fromLocalFile(moviesPath) << (moviesPath == QDir::homePath() ? "user-home" : "folder-videos"); } QTest::newRow("empty") << QUrl() << "unknown"; QTest::newRow("relative") << QUrl("foo") << "unknown"; QTest::newRow("tilde") << QUrl("~") << "unknown"; // TODO more tests } void KFileItemTest::testIconNameForUrl() { QFETCH(QUrl, url); QFETCH(QString, expectedIcon); if (KIO::iconNameForUrl(url) != expectedIcon) { qDebug() << url; QCOMPARE(KIO::iconNameForUrl(url), expectedIcon); } } void KFileItemTest::testMimetypeForRemoteFolder() { KIO::UDSEntry entry; entry.fastInsert(KIO::UDSEntry::UDS_NAME, QStringLiteral("foo")); entry.fastInsert(KIO::UDSEntry::UDS_FILE_TYPE, S_IFDIR); QUrl url(QStringLiteral("smb://remoteFolder/foo")); KFileItem fileItem(entry, url); QCOMPARE(fileItem.mimetype(), QStringLiteral("inode/directory")); } void KFileItemTest::testMimetypeForRemoteFolderWithFileType() { QString udsMimeType = QStringLiteral("application/x-smb-workgroup"); QVERIFY2(QMimeDatabase().mimeTypeForName(udsMimeType).isValid(), qPrintable(QStandardPaths::standardLocations(QStandardPaths::GenericDataLocation).join(':'))); // kcoreaddons installed? XDG_DATA_DIRS set? KIO::UDSEntry entry; entry.fastInsert(KIO::UDSEntry::UDS_NAME, QStringLiteral("foo")); entry.fastInsert(KIO::UDSEntry::UDS_FILE_TYPE, S_IFDIR); entry.fastInsert(KIO::UDSEntry::UDS_MIME_TYPE, udsMimeType); QUrl url(QStringLiteral("smb://remoteFolder/foo")); KFileItem fileItem(entry, url); QCOMPARE(fileItem.mimetype(), udsMimeType); } void KFileItemTest::testCurrentMimetypeForRemoteFolder() { KIO::UDSEntry entry; entry.fastInsert(KIO::UDSEntry::UDS_NAME, QStringLiteral("foo")); entry.fastInsert(KIO::UDSEntry::UDS_FILE_TYPE, S_IFDIR); QUrl url(QStringLiteral("smb://remoteFolder/foo")); KFileItem fileItem(entry, url); QCOMPARE(fileItem.currentMimeType().name(), QStringLiteral("inode/directory")); } void KFileItemTest::testCurrentMimetypeForRemoteFolderWithFileType() { QString udsMimeType = QStringLiteral("application/x-smb-workgroup"); KIO::UDSEntry entry; entry.fastInsert(KIO::UDSEntry::UDS_NAME, QStringLiteral("foo")); entry.fastInsert(KIO::UDSEntry::UDS_FILE_TYPE, S_IFDIR); entry.fastInsert(KIO::UDSEntry::UDS_MIME_TYPE, udsMimeType); QUrl url(QStringLiteral("smb://remoteFolder/foo")); KFileItem fileItem(entry, url); QCOMPARE(fileItem.currentMimeType().name(), udsMimeType); } void KFileItemTest::testIconNameForCustomFolderIcons() { // Custom folder icons should be displayed (bug 350612) const QString iconName = QStringLiteral("folder-music"); QTemporaryDir tempDir; const QUrl url = QUrl::fromLocalFile(tempDir.path()); KDesktopFile cfg(tempDir.path() + QLatin1String("/.directory")); cfg.desktopGroup().writeEntry("Icon", iconName); cfg.sync(); KIO::UDSEntry entry; entry.fastInsert(KIO::UDSEntry::UDS_FILE_TYPE, S_IFDIR); KFileItem fileItem(entry, url); QCOMPARE(fileItem.iconName(), iconName); } void KFileItemTest::testIconNameForStandardPath() { const QString iconName = QStringLiteral("folder-videos"); const QUrl url = QUrl::fromLocalFile(QDir::homePath() + QLatin1String("/Videos")); QStandardPaths::setTestModeEnabled(true); KIO::UDSEntry entry; entry.fastInsert(KIO::UDSEntry::UDS_FILE_TYPE, S_IFDIR); KFileItem fileItem(entry, url); QCOMPARE(fileItem.iconName(), iconName); } #ifndef Q_OS_WIN // user/group/other write permissions are not handled on windows void KFileItemTest::testIsReadable_data() { QTest::addColumn("mode"); QTest::addColumn("readable"); QTest::newRow("fully-readable") << 0444 << true; QTest::newRow("user-readable") << 0400 << true; QTest::newRow("not-readable-by-us") << 0004 << false; QTest::newRow("not-readable-at-all") << 0000 << false; } void KFileItemTest::testIsReadable() { QFETCH(int, mode); QFETCH(bool, readable); QTemporaryFile file; QVERIFY(file.open()); int ret = fchmod(file.handle(), (mode_t)mode); QCOMPARE(ret, 0); KFileItem fileItem(QUrl::fromLocalFile(file.fileName())); QCOMPARE(fileItem.isReadable(), readable); } // Restore permissions so that the QTemporaryDir cleanup can happen (taken from tst_qsavefile.cpp) class PermissionRestorer { Q_DISABLE_COPY(PermissionRestorer) public: explicit PermissionRestorer(const QString& path) : m_path(path) {} ~PermissionRestorer() { restore(); } inline void restore() { QFile file(m_path); #ifdef Q_OS_UNIX file.setPermissions(QFile::Permissions(QFile::ReadOwner | QFile::WriteOwner | QFile::ExeOwner)); #else file.setPermissions(QFile::WriteOwner); file.remove(); #endif } private: const QString m_path; }; void KFileItemTest::testNonWritableDirectory() { // Given a directory with a file in it QTemporaryDir dir; QVERIFY2(dir.isValid(), qPrintable(dir.errorString())); QFile file(dir.path() + "/file1"); QVERIFY(file.open(QIODevice::WriteOnly)); QCOMPARE(file.write("Hello"), Q_INT64_C(5)); file.close(); // ... which is then made non-writable QVERIFY(QFile(dir.path()).setPermissions(QFile::ReadOwner | QFile::ExeOwner)); PermissionRestorer permissionRestorer(dir.path()); // When using KFileItemListProperties on the file const KFileItem item(QUrl::fromLocalFile(file.fileName())); KFileItemListProperties props(KFileItemList() << item); // Then it should say moving is not supported QVERIFY(!props.supportsMoving()); QVERIFY(props.supportsWriting()); // but we can write to the file itself } #endif // Q_OS_WIN diff --git a/autotests/kfileitemtest.h b/autotests/kfileitemtest.h index 272cf19a..f89478dd 100644 --- a/autotests/kfileitemtest.h +++ b/autotests/kfileitemtest.h @@ -1,72 +1,73 @@ /* This file is part of the KDE project Copyright (C) 2006 David Faure This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KFILEITEMTEST_H #define KFILEITEMTEST_H #include class KFileItemTest : public QObject { Q_OBJECT private Q_SLOTS: void initTestCase(); void testPermissionsString(); void testNull(); void testDoesNotExist(); void testDetach(); void testMove(); void testBasic(); void testRootDirectory(); void testHiddenFile(); void testMimeTypeOnDemand(); void testCmp(); + void testCmpAndInit(); void testCmpByUrl(); void testRename(); void testRefresh(); void testDotDirectory(); void testMimetypeForRemoteFolder(); void testMimetypeForRemoteFolderWithFileType(); void testCurrentMimetypeForRemoteFolder(); void testCurrentMimetypeForRemoteFolderWithFileType(); void testIconNameForCustomFolderIcons(); void testIconNameForStandardPath(); #ifndef Q_OS_WIN void testIsReadable_data(); void testIsReadable(); #endif void testDecodeFileName_data(); void testDecodeFileName(); void testEncodeFileName_data(); void testEncodeFileName(); // KFileItemListProperties tests void testListProperties_data(); void testListProperties(); #ifndef Q_OS_WIN void testNonWritableDirectory(); #endif // KIO global tests void testIconNameForUrl_data(); void testIconNameForUrl(); }; #endif diff --git a/src/core/kfileitem.cpp b/src/core/kfileitem.cpp index 0993c8d0..9ade49a5 100644 --- a/src/core/kfileitem.cpp +++ b/src/core/kfileitem.cpp @@ -1,1602 +1,1698 @@ /* This file is part of the KDE project Copyright (C) 1999-2011 David Faure 2001 Carsten Pfeiffer This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kfileitem.h" #include "kioglobal_p.h" #include "kiocoredebug.h" #include "../pathhelpers_p.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef Q_OS_WIN #include #include #endif #include class KFileItemPrivate : public QSharedData { public: KFileItemPrivate(const KIO::UDSEntry &entry, 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(), m_strText(), m_iconName(), m_strLowerCaseName(), m_mimeType(), m_fileMode(mode), m_permissions(permissions), m_bLink(false), m_bIsLocalUrl(itemOrDirUrl.isLocalFile()), m_bMimeTypeKnown(false), 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; QDateTime time(KFileItem::FileTimes which) const; void setTime(KFileItem::FileTimes which, uint time_t_val) const; void setTime(KFileItem::FileTimes which, const QDateTime &val) const; bool cmp(const KFileItemPrivate &item) const; bool isSlow() const; /** * Extracts the data from the UDSEntry member and updates the KFileItem * accordingly. */ void readUDSEntry(bool _urlIsDirectory); /** * Parses the given permission set and provides it for access() */ 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. */ mutable KIO::UDSEntry m_entry; /** * The url of the file */ QUrl m_url; /** * The text for this item, i.e. the file name without path, */ QString m_strName; /** * The text for this item, i.e. the file name without path, decoded * ('%%' becomes '%', '%2F' becomes '/') */ QString m_strText; /** * The icon name for this item. */ mutable QString m_iconName; /** * The filename in lower case (to speed up sorting) */ mutable QString m_strLowerCaseName; /** * The mimetype of the file */ mutable QMimeType m_mimeType; /** * 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 */ bool m_bIsLocalUrl: 1; mutable bool m_bMimeTypeKnown: 1; mutable bool m_delayedMimeTypes: 1; /** True if m_iconName should be used as cache. */ mutable bool m_useIconNameCache: 1; // Auto: check leading dot. enum { Auto, Hidden, Shown } m_hidden: 3; // 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 * we want to stat() them; it requires that we * change into it .. which may not be allowed * stat("/is/unaccessible") -> rwx------ * stat("/is/unaccessible/") -> EPERM H.Z. * This is the reason for the StripTrailingSlash */ QT_STATBUF buf; const QString path = m_url.adjusted(QUrl::StripTrailingSlash).toLocalFile(); const QByteArray pathBA = QFile::encodeName(path); if (QT_LSTAT(pathBA.constData(), &buf) == 0) { m_entry.reserve(9); m_entry.replace(KIO::UDSEntry::UDS_DEVICE_ID, buf.st_dev); m_entry.replace(KIO::UDSEntry::UDS_INODE, buf.st_ino); mode_t mode = buf.st_mode; if ((buf.st_mode & QT_STAT_MASK) == QT_STAT_LNK) { m_bLink = true; if (QT_STAT(pathBA.constData(), &buf) == 0) { mode = buf.st_mode; } else {// link pointing to nowhere (see FileProtocol::createUDSEntry() in ioslaves/file/file.cpp) mode = (QT_STAT_MASK - 1) | S_IRWXU | S_IRWXG | S_IRWXO; } } m_entry.replace(KIO::UDSEntry::UDS_SIZE, buf.st_size); m_entry.replace(KIO::UDSEntry::UDS_FILE_TYPE, buf.st_mode & QT_STAT_MASK); // extract file type m_entry.replace(KIO::UDSEntry::UDS_ACCESS, buf.st_mode & 07777); // extract permissions m_entry.replace(KIO::UDSEntry::UDS_MODIFICATION_TIME, buf.st_mtime); // TODO: we could use msecs too... m_entry.replace(KIO::UDSEntry::UDS_ACCESS_TIME, buf.st_atime); #ifndef Q_OS_WIN m_entry.replace(KIO::UDSEntry::UDS_USER, KUser(buf.st_uid).loginName()); m_entry.replace(KIO::UDSEntry::UDS_GROUP, KUserGroup(buf.st_gid).name()); #endif // TODO: these can be removed, we can use UDS_FILE_TYPE and UDS_ACCESS everywhere if (m_fileMode == KFileItem::Unknown) { m_fileMode = mode & QT_STAT_MASK; // extract file type } if (m_permissions == KFileItem::Unknown) { m_permissions = mode & 07777; // extract permissions } } } } + + m_bInitCalled = true; } + void KFileItemPrivate::readUDSEntry(bool _urlIsDirectory) { // extract fields from the KIO::UDS Entry m_fileMode = m_entry.numberValue(KIO::UDSEntry::UDS_FILE_TYPE, KFileItem::Unknown); m_permissions = m_entry.numberValue(KIO::UDSEntry::UDS_ACCESS, KFileItem::Unknown); m_strName = m_entry.stringValue(KIO::UDSEntry::UDS_NAME); const QString displayName = m_entry.stringValue(KIO::UDSEntry::UDS_DISPLAY_NAME); if (!displayName.isEmpty()) { m_strText = displayName; } else { m_strText = KIO::decodeFileName(m_strName); } const QString urlStr = m_entry.stringValue(KIO::UDSEntry::UDS_URL); const bool UDS_URL_seen = !urlStr.isEmpty(); if (UDS_URL_seen) { m_url = QUrl(urlStr); if (m_url.isLocalFile()) { m_bIsLocalUrl = true; } } QMimeDatabase db; const QString mimeTypeStr = m_entry.stringValue(KIO::UDSEntry::UDS_MIME_TYPE); m_bMimeTypeKnown = !mimeTypeStr.isEmpty(); if (m_bMimeTypeKnown) { m_mimeType = db.mimeTypeForName(mimeTypeStr); } m_guessedMimeType = m_entry.stringValue(KIO::UDSEntry::UDS_GUESSED_MIME_TYPE); m_bLink = !m_entry.stringValue(KIO::UDSEntry::UDS_LINK_DEST).isEmpty(); // we don't store the link dest const int hiddenVal = m_entry.numberValue(KIO::UDSEntry::UDS_HIDDEN, -1); m_hidden = hiddenVal == 1 ? Hidden : (hiddenVal == 0 ? Shown : Auto); if (_urlIsDirectory && !UDS_URL_seen && !m_strName.isEmpty() && m_strName != QLatin1String(".")) { m_url.setPath(concatPaths(m_url.path(), m_strName)); } m_iconName.clear(); } 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) { return fieldVal; } // If not in the KIO::UDSEntry, or if UDSEntry empty, use stat() [if local URL] if (m_bIsLocalUrl) { return QFileInfo(m_url.toLocalFile()).size(); } return 0; } static uint udsFieldForTime(KFileItem::FileTimes mappedWhich) { switch (mappedWhich) { case KFileItem::ModificationTime: return KIO::UDSEntry::UDS_MODIFICATION_TIME; case KFileItem::AccessTime: return KIO::UDSEntry::UDS_ACCESS_TIME; case KFileItem::CreationTime: return KIO::UDSEntry::UDS_CREATION_TIME; } return 0; } void KFileItemPrivate::setTime(KFileItem::FileTimes mappedWhich, uint time_t_val) const { m_entry.replace(udsFieldForTime(mappedWhich), time_t_val); } void KFileItemPrivate::setTime(KFileItem::FileTimes mappedWhich, const QDateTime &val) const { const QDateTime dt = val.toLocalTime(); // #160979 setTime(mappedWhich, dt.toTime_t()); } QDateTime KFileItemPrivate::time(KFileItem::FileTimes mappedWhich) const { + ensureInitialized(); + // Extract it from the KIO::UDSEntry const uint uds = udsFieldForTime(mappedWhich); if (uds > 0) { const long long fieldVal = m_entry.numberValue(uds, -1); if (fieldVal != -1) { return QDateTime::fromMSecsSinceEpoch(1000 * fieldVal); } } return QDateTime(); } 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); //qDebug() << " local" << (m_bIsLocalUrl == item.m_bIsLocalUrl); //qDebug() << " mode" << (m_fileMode == item.m_fileMode); //qDebug() << " perm" << (m_permissions == item.m_permissions); //qDebug() << " UDS_EXTENDED_ACL" << (m_entry.stringValue( KIO::UDSEntry::UDS_EXTENDED_ACL ) == item.m_entry.stringValue( KIO::UDSEntry::UDS_EXTENDED_ACL )); //qDebug() << " UDS_ACL_STRING" << (m_entry.stringValue( KIO::UDSEntry::UDS_ACL_STRING ) == item.m_entry.stringValue( KIO::UDSEntry::UDS_ACL_STRING )); //qDebug() << " UDS_DEFAULT_ACL_STRING" << (m_entry.stringValue( KIO::UDSEntry::UDS_DEFAULT_ACL_STRING ) == item.m_entry.stringValue( KIO::UDSEntry::UDS_DEFAULT_ACL_STRING )); //qDebug() << " m_bLink" << (m_bLink == item.m_bLink); //qDebug() << " m_hidden" << (m_hidden == item.m_hidden); //qDebug() << " size" << (size() == item.size()); //qDebug() << " ModificationTime" << m_entry.numberValue(KIO::UDSEntry::UDS_MODIFICATION_TIME) << item.m_entry.numberValue(KIO::UDSEntry::UDS_MODIFICATION_TIME); //qDebug() << " UDS_ICON_NAME" << (m_entry.stringValue( KIO::UDSEntry::UDS_ICON_NAME ) == item.m_entry.stringValue( KIO::UDSEntry::UDS_ICON_NAME )); #endif return (m_strName == item.m_strName && m_bIsLocalUrl == item.m_bIsLocalUrl && m_fileMode == item.m_fileMode && m_permissions == item.m_permissions && m_entry.stringValue(KIO::UDSEntry::UDS_EXTENDED_ACL) == item.m_entry.stringValue(KIO::UDSEntry::UDS_EXTENDED_ACL) && m_entry.stringValue(KIO::UDSEntry::UDS_ACL_STRING) == item.m_entry.stringValue(KIO::UDSEntry::UDS_ACL_STRING) && m_entry.stringValue(KIO::UDSEntry::UDS_DEFAULT_ACL_STRING) == item.m_entry.stringValue(KIO::UDSEntry::UDS_DEFAULT_ACL_STRING) && m_bLink == item.m_bLink && m_hidden == item.m_hidden && size() == item.size() && m_entry.numberValue(KIO::UDSEntry::UDS_MODIFICATION_TIME) == item.m_entry.numberValue(KIO::UDSEntry::UDS_MODIFICATION_TIME) && m_entry.stringValue(KIO::UDSEntry::UDS_ICON_NAME) == item.m_entry.stringValue(KIO::UDSEntry::UDS_ICON_NAME) && m_entry.stringValue(KIO::UDSEntry::UDS_TARGET_URL) == item.m_entry.stringValue(KIO::UDSEntry::UDS_TARGET_URL) && m_entry.stringValue(KIO::UDSEntry::UDS_LOCAL_PATH) == item.m_entry.stringValue(KIO::UDSEntry::UDS_LOCAL_PATH) ); // Don't compare the mimetypes here. They might not be known, and we don't want to // do the slow operation of determining them here. } 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; if ((perm & (S_IXUSR | S_ISUID)) == (S_IXUSR | S_ISUID)) { uxbit = 's'; } else if ((perm & (S_IXUSR | S_ISUID)) == S_ISUID) { uxbit = 'S'; } else if ((perm & (S_IXUSR | S_ISUID)) == S_IXUSR) { uxbit = 'x'; } else { uxbit = '-'; } if ((perm & (S_IXGRP | S_ISGID)) == (S_IXGRP | S_ISGID)) { gxbit = 's'; } else if ((perm & (S_IXGRP | S_ISGID)) == S_ISGID) { gxbit = 'S'; } else if ((perm & (S_IXGRP | S_ISGID)) == S_IXGRP) { gxbit = 'x'; } else { gxbit = '-'; } if ((perm & (S_IXOTH | S_ISVTX)) == (S_IXOTH | S_ISVTX)) { oxbit = 't'; } else if ((perm & (S_IXOTH | S_ISVTX)) == S_ISVTX) { oxbit = 'T'; } else if ((perm & (S_IXOTH | S_ISVTX)) == S_IXOTH) { oxbit = 'x'; } else { oxbit = '-'; } // Include the type in the first char like ls does; people are more used to seeing it, // even though it's not really part of the permissions per se. if (m_bLink) { buffer[0] = 'l'; } else if (m_fileMode != KFileItem::Unknown) { if ((m_fileMode & QT_STAT_MASK) == QT_STAT_DIR) { buffer[0] = 'd'; } #ifdef Q_OS_UNIX else if (S_ISSOCK(m_fileMode)) { buffer[0] = 's'; } else if (S_ISCHR(m_fileMode)) { buffer[0] = 'c'; } else if (S_ISBLK(m_fileMode)) { buffer[0] = 'b'; } else if (S_ISFIFO(m_fileMode)) { buffer[0] = 'p'; } #endif // Q_OS_UNIX else { buffer[0] = '-'; } } else { buffer[0] = '-'; } buffer[1] = (((perm & S_IRUSR) == S_IRUSR) ? 'r' : '-'); buffer[2] = (((perm & S_IWUSR) == S_IWUSR) ? 'w' : '-'); buffer[3] = uxbit; buffer[4] = (((perm & S_IRGRP) == S_IRGRP) ? 'r' : '-'); buffer[5] = (((perm & S_IWGRP) == S_IWGRP) ? 'w' : '-'); buffer[6] = gxbit; buffer[7] = (((perm & S_IROTH) == S_IROTH) ? 'r' : '-'); buffer[8] = (((perm & S_IWOTH) == S_IWOTH) ? 'w' : '-'); buffer[9] = oxbit; // if (hasExtendedACL()) if (m_entry.contains(KIO::UDSEntry::UDS_EXTENDED_ACL)) { buffer[10] = '+'; buffer[11] = 0; } else { buffer[10] = 0; } 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() : d(nullptr) { } 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 // - Copy assignment // - Move assignment // - Destructor // The compiler will now generate the content of those. KFileItem::KFileItem(const KFileItem&) = default; KFileItem::~KFileItem() = default; KFileItem::KFileItem(KFileItem&&) = default; KFileItem& KFileItem::operator=(const KFileItem&) = default; KFileItem& KFileItem::operator=(KFileItem&&) = default; void KFileItem::refresh() { if (!d) { qCWarning(KIO_CORE) << "null item"; return; } d->m_fileMode = KFileItem::Unknown; d->m_permissions = KFileItem::Unknown; d->m_hidden = KFileItemPrivate::Auto; refreshMimeType(); // Basically, we can't trust any information we got while listing. // Everything could have changed... // Clearing m_entry makes it possible to detect changes in the size of the file, // the time information, etc. d->m_entry.clear(); d->init(); // re-populates d->m_entry } void KFileItem::refreshMimeType() { if (!d) { return; } d->m_mimeType = QMimeType(); d->m_bMimeTypeKnown = false; d->m_iconName.clear(); } void KFileItem::setDelayedMimeTypes(bool b) { if (!d) { return; } d->m_delayedMimeTypes = b; } void KFileItem::setUrl(const QUrl &url) { if (!d) { qCWarning(KIO_CORE) << "null item"; return; } d->m_url = url; setName(url.fileName()); } void KFileItem::setLocalPath(const QString &path) { if (!d) { qCWarning(KIO_CORE) << "null item"; return; } d->m_entry.replace(KIO::UDSEntry::UDS_LOCAL_PATH, path); } void KFileItem::setName(const QString &name) { if (!d) { qCWarning(KIO_CORE) << "null item"; return; } + d->ensureInitialized(); + d->m_strName = name; if (!d->m_strName.isEmpty()) { d->m_strText = KIO::decodeFileName(d->m_strName); } if (d->m_entry.contains(KIO::UDSEntry::UDS_NAME)) { d->m_entry.replace(KIO::UDSEntry::UDS_NAME, d->m_strName); // #195385 } } QString KFileItem::linkDest() const { if (!d) { 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()) { return linkStr; } // If not in the KIO::UDSEntry, or if UDSEntry empty, use readlink() [if local URL] if (d->m_bIsLocalUrl) { return QFile::symLinkTarget(d->m_url.adjusted(QUrl::StripTrailingSlash).toLocalFile()); } return QString(); } QString KFileItemPrivate::localPath() const { if (m_bIsLocalUrl) { return m_url.toLocalFile(); } + ensureInitialized(); + // Extract the local path from the KIO::UDSEntry return m_entry.stringValue(KIO::UDSEntry::UDS_LOCAL_PATH); } QString KFileItem::localPath() const { if (!d) { return QString(); } return d->localPath(); } KIO::filesize_t KFileItem::size() const { if (!d) { return 0; } return d->size(); } bool KFileItem::hasExtendedACL() const { if (!d) { return false; } // 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 { if (!d) { return KACL(); } if (hasExtendedACL()) { // Extract it from the KIO::UDSEntry const QString fieldVal = d->m_entry.stringValue(KIO::UDSEntry::UDS_ACL_STRING); if (!fieldVal.isEmpty()) { return KACL(fieldVal); } } + // create one from the basic permissions return KACL(d->m_permissions); } KACL KFileItem::defaultACL() const { if (!d) { return KACL(); } // 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 { return KACL(); } } QDateTime KFileItem::time(FileTimes which) const { if (!d) { return QDateTime(); } return d->time(which); } QString KFileItem::user() const { if (!d) { 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 { if (m_slow == SlowUnknown) { const QString path = localPath(); if (!path.isEmpty()) { const KFileSystemType::Type fsType = KFileSystemType::fileSystemType(path); m_slow = (fsType == KFileSystemType::Nfs || fsType == KFileSystemType::Smb) ? Slow : Fast; } else { m_slow = Slow; } } return m_slow == Slow; } bool KFileItem::isSlow() const { if (!d) { return false; } return d->isSlow(); } QString KFileItem::mimetype() const { if (!d) { return QString(); } KFileItem *that = const_cast(this); return that->determineMimeType().name(); } QMimeType KFileItem::determineMimeType() const { if (!d) { return QMimeType(); } if (!d->m_mimeType.isValid() || !d->m_bMimeTypeKnown) { QMimeDatabase db; if (isDir()) { d->m_mimeType = db.mimeTypeForName(QStringLiteral("inode/directory")); } 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()); //qDebug() << d << "finding final mimetype for" << url << ":" << d->m_mimeType.name(); } d->m_bMimeTypeKnown = true; } if (d->m_delayedMimeTypes) { // if we delayed getting the iconName up till now, this is the right point in time to do so d->m_delayedMimeTypes = false; d->m_useIconNameCache = false; (void)iconName(); } return d->m_mimeType; } bool KFileItem::isMimeTypeKnown() const { if (!d) { return false; } // The mimetype isn't known if determineMimeType was never called (on-demand determination) // or if this fileitem has a guessed mimetype (e.g. ftp symlink) - in which case // it always remains "not fully determined" return d->m_bMimeTypeKnown && d->m_guessedMimeType.isEmpty(); } static bool isDirectoryMounted(const QUrl &url) { // Stating .directory files can cause long freezes when e.g. /home // uses autofs for every user's home directory, i.e. opening /home // in a file dialog will mount every single home directory. // These non-mounted directories can be identified by having 0 size. // There are also other directories with 0 size, such as /proc, that may // be mounted, but those are unlikely to contain .directory (and checking // this would require checking with KMountPoint). // TODO: maybe this could be checked with KFileSystemType instead? QFileInfo info(url.toLocalFile()); if (info.isDir() && info.size() == 0) { return false; } return true; } bool KFileItem::isFinalIconKnown() const { if (!d) { return false; } return d->m_bMimeTypeKnown && (!d->m_delayedMimeTypes); } // KDE5 TODO: merge with comment()? Need to see what lxr says about the usage of both. QString KFileItem::mimeComment() const { if (!d) { return QString(); } const QString displayType = d->m_entry.stringValue(KIO::UDSEntry::UDS_DISPLAY_TYPE); if (!displayType.isEmpty()) { return displayType; } bool isLocalUrl; QUrl url = mostLocalUrl(&isLocalUrl); QMimeType mime = currentMimeType(); // This cannot move to kio_file (with UDS_DISPLAY_TYPE) because it needs // the mimetype to be determined, which is done here, and possibly delayed... if (isLocalUrl && !d->isSlow() && mime.inherits(QStringLiteral("application/x-desktop"))) { KDesktopFile cfg(url.toLocalFile()); QString comment = cfg.desktopGroup().readEntry("Comment"); if (!comment.isEmpty()) { return comment; } } // Support for .directory file in directories if (isLocalUrl && isDir() && !d->isSlow() && isDirectoryMounted(url)) { QUrl u(url); u.setPath(concatPaths(u.path(), QStringLiteral(".directory"))); const KDesktopFile cfg(u.toLocalFile()); const QString comment = cfg.readComment(); if (!comment.isEmpty()) { return comment; } } const QString comment = mime.comment(); //qDebug() << "finding comment for " << url.url() << " : " << d->m_mimeType->name(); if (!comment.isEmpty()) { return comment; } else { return mime.name(); } } static QString iconFromDirectoryFile(const QString &path) { const QString filePath = path + QLatin1String("/.directory"); if (!QFileInfo(filePath).isFile()) { // exists -and- is a file return QString(); } KDesktopFile cfg(filePath); QString icon = cfg.readIcon(); const KConfigGroup group = cfg.desktopGroup(); const QString emptyIcon = group.readEntry("EmptyIcon"); if (!emptyIcon.isEmpty()) { bool isDirEmpty = true; QDirIterator dirIt(path, QDir::Dirs | QDir::Files | QDir::NoDotAndDotDot); while (dirIt.hasNext()) { dirIt.next(); if (dirIt.fileName() != QLatin1String(".directory")) { isDirEmpty = false; break; } } if (isDirEmpty) { icon = emptyIcon; } } if (icon.startsWith(QLatin1String("./"))) { // path is relative with respect to the location // of the .directory file (#73463) return path + icon.midRef(1); } return icon; } static QString iconFromDesktopFile(const QString &path) { KDesktopFile cfg(path); const QString icon = cfg.readIcon(); if (cfg.hasLinkType()) { const KConfigGroup group = cfg.desktopGroup(); const QString emptyIcon = group.readEntry("EmptyIcon"); if (!emptyIcon.isEmpty()) { const QString u = cfg.readUrl(); const QUrl url(u); if (url.scheme() == QLatin1String("trash")) { // We need to find if the trash is empty, preferably without using a KIO job. // So instead kio_trash leaves an entry in its config file for us. KConfig trashConfig(QStringLiteral("trashrc"), KConfig::SimpleConfig); if (trashConfig.group("Status").readEntry("Empty", true)) { return emptyIcon; } } } } return icon; } QString KFileItem::iconName() const { if (!d) { return QString(); } if (d->m_useIconNameCache && !d->m_iconName.isEmpty()) { return d->m_iconName; } d->m_iconName = d->m_entry.stringValue(KIO::UDSEntry::UDS_ICON_NAME); if (!d->m_iconName.isEmpty()) { d->m_useIconNameCache = d->m_bMimeTypeKnown; return d->m_iconName; } bool isLocalUrl; QUrl url = mostLocalUrl(&isLocalUrl); QMimeDatabase db; QMimeType mime; // Use guessed mimetype for the icon if (!d->m_guessedMimeType.isEmpty()) { mime = db.mimeTypeForName(d->m_guessedMimeType); } else { mime = currentMimeType(); } const bool delaySlowOperations = d->m_delayedMimeTypes; if (isLocalUrl && !delaySlowOperations && mime.inherits(QStringLiteral("application/x-desktop"))) { d->m_iconName = iconFromDesktopFile(url.toLocalFile()); if (!d->m_iconName.isEmpty()) { d->m_useIconNameCache = d->m_bMimeTypeKnown; return d->m_iconName; } } if (isLocalUrl && !delaySlowOperations && isDir()) { if (isDirectoryMounted(url)) { d->m_iconName = iconFromDirectoryFile(url.toLocalFile()); if (!d->m_iconName.isEmpty()) { d->m_useIconNameCache = d->m_bMimeTypeKnown; return d->m_iconName; } } d->m_iconName = KIOPrivate::iconForStandardPath(url.toLocalFile()); if (!d->m_iconName.isEmpty()) { d->m_useIconNameCache = d->m_bMimeTypeKnown; return d->m_iconName; } } d->m_iconName = mime.iconName(); d->m_useIconNameCache = d->m_bMimeTypeKnown; return d->m_iconName; } /** * Returns true if this is a desktop file. * Mimetype determination is optional. */ static bool checkDesktopFile(const KFileItem &item, bool _determineMimeType) { // only local files bool isLocalUrl; item.mostLocalUrl(&isLocalUrl); if (!isLocalUrl) { return false; } // only regular files if (!item.isRegularFile()) { return false; } // only if readable if (!item.isReadable()) { return false; } // return true if desktop file QMimeType mime = _determineMimeType ? item.determineMimeType() : item.currentMimeType(); return mime.inherits(QStringLiteral("application/x-desktop")); } QStringList KFileItem::overlays() const { if (!d) { return QStringList(); } + d->ensureInitialized(); + QStringList names = d->m_entry.stringValue(KIO::UDSEntry::UDS_ICON_OVERLAY_NAMES).split(QLatin1Char(','), QString::SkipEmptyParts); if (d->m_bLink) { names.append(QStringLiteral("emblem-symbolic-link")); } if (!isReadable()) { names.append(QStringLiteral("emblem-locked")); } if (checkDesktopFile(*this, false)) { KDesktopFile cfg(localPath()); const KConfigGroup group = cfg.desktopGroup(); // Add a warning emblem if this is an executable desktop file // which is untrusted. if (group.hasKey("Exec") && !KDesktopFile::isAuthorizedDesktopFile(localPath())) { names.append(QStringLiteral("emblem-important")); } if (cfg.hasDeviceType()) { const QString dev = cfg.readDevice(); if (!dev.isEmpty()) { KMountPoint::Ptr mountPoint = KMountPoint::currentMountPoints().findByDevice(dev); if (mountPoint) { // mounted? names.append(QStringLiteral("emblem-mounted")); } } } } if (isHidden()) { names.append(QStringLiteral("hidden")); } #ifndef Q_OS_WIN if (isDir()) { bool isLocalUrl; const QUrl url = mostLocalUrl(&isLocalUrl); if (isLocalUrl) { const QString path = url.toLocalFile(); if (KSambaShare::instance()->isDirectoryShared(path) || KNFSShare::instance()->isDirectoryShared(path)) { names.append(QStringLiteral("emblem-shared")); } } } #endif // Q_OS_WIN return names; } QString KFileItem::comment() const { if (!d) { return QString(); } return d->m_entry.stringValue(KIO::UDSEntry::UDS_COMMENT); } bool KFileItem::isReadable() const { if (!d) { return false; } + d->ensureInitialized(); + /* struct passwd * user = getpwuid( geteuid() ); bool isMyFile = (QString::fromLocal8Bit(user->pw_name) == d->m_user); // This gets ugly for the group.... // Maybe we want a static QString for the user and a static QStringList // for the groups... then we need to handle the deletion properly... */ if (d->m_permissions != KFileItem::Unknown) { const mode_t readMask = S_IRUSR | S_IRGRP | S_IROTH; // No read permission at all if ((d->m_permissions & readMask) == 0) { return false; } // Read permissions for all: save a stat call if ((d->m_permissions & readMask) == readMask) { return true; } } // Or if we can't read it - not network transparent if (d->m_bIsLocalUrl && !QFileInfo(d->m_url.toLocalFile()).isReadable()) { return false; } return true; } bool KFileItem::isWritable() const { if (!d) { return false; } + d->ensureInitialized(); + /* struct passwd * user = getpwuid( geteuid() ); bool isMyFile = (QString::fromLocal8Bit(user->pw_name) == d->m_user); // This gets ugly for the group.... // Maybe we want a static QString for the user and a static QStringList // for the groups... then we need to handle the deletion properly... */ if (d->m_permissions != KFileItem::Unknown) { // No write permission at all if (!(S_IWUSR & d->m_permissions) && !(S_IWGRP & d->m_permissions) && !(S_IWOTH & d->m_permissions)) { return false; } } // Or if we can't write it - not network transparent if (d->m_bIsLocalUrl && !QFileInfo(d->m_url.toLocalFile()).isWritable()) { return false; } return true; } bool KFileItem::isHidden() const { if (!d) { return false; } // The kioslave can specify explicitly that a file is hidden or shown if (d->m_hidden != KFileItemPrivate::Auto) { return d->m_hidden == KFileItemPrivate::Hidden; } // Prefer the filename that is part of the URL, in case the display name is different. QString fileName = d->m_url.fileName(); if (fileName.isEmpty()) { // e.g. "trash:/" fileName = d->m_strName; } return fileName.length() > 1 && fileName[0] == QLatin1Char('.'); // Just "." is current directory, not hidden. } void KFileItem::setHidden() { if (d) { d->m_hidden = KFileItemPrivate::Hidden; } } bool KFileItem::isDir() const { if (!d) { 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"; return false; // can't say for sure, so no } return (d->m_fileMode & QT_STAT_MASK) == QT_STAT_DIR; } bool KFileItem::isFile() const { if (!d) { return false; } return !isDir(); } #ifndef KIOCORE_NO_DEPRECATED bool KFileItem::acceptsDrops() const { // A directory ? if (isDir()) { return isWritable(); } // But only local .desktop files and executables if (!d->m_bIsLocalUrl) { return false; } if (mimetype() == QLatin1String("application/x-desktop")) { return true; } // Executable, shell script ... ? if (QFileInfo(d->m_url.toLocalFile()).isExecutable()) { return true; } return false; } #endif QString KFileItem::getStatusBarInfo() const { if (!d) { return QString(); } QString text = d->m_strText; const QString comment = mimeComment(); if (d->m_bLink) { text += QLatin1Char(' '); if (comment.isEmpty()) { text += i18n("(Symbolic Link to %1)", linkDest()); } else { text += i18n("(%1, Link to %2)", comment, linkDest()); } } else if (targetUrl() != url()) { text += i18n(" (Points to %1)", targetUrl().toDisplayString()); } else if ((d->m_fileMode & QT_STAT_MASK) == QT_STAT_REG) { text += QStringLiteral(" (%1, %2)").arg(comment, KIO::convertSize(size())); } else { text += QStringLiteral(" (%1)").arg(comment); } return text; } bool KFileItem::cmp(const KFileItem &item) const { if (!d && !item.d) { return true; } if (!d || !item.d) { return false; } return d->cmp(*item.d); } bool KFileItem::operator==(const KFileItem &other) const { if (!d && !other.d) { return true; } if (!d || !other.d) { return false; } return d->m_url == other.d->m_url; } bool KFileItem::operator!=(const KFileItem &other) const { return !operator==(other); } bool KFileItem::operator<(const KFileItem &other) const { if (!other.d) { return false; } if (!d) { return other.d->m_url.isValid(); } return d->m_url < other.d->m_url; } bool KFileItem::operator<(const QUrl &other) const { if (!d) { return other.isValid(); } return d->m_url < other; } KFileItem::operator QVariant() const { return qVariantFromValue(*this); } QString KFileItem::permissionsString() const { if (!d) { return QString(); } + d->ensureInitialized(); + if (d->m_access.isNull() && d->m_permissions != KFileItem::Unknown) { d->m_access = d->parsePermissions(d->m_permissions); } return d->m_access; } // check if we need to cache this QString KFileItem::timeString(FileTimes which) const { if (!d) { return QString(); } return d->time(which).toString(); } #ifndef KIOCORE_NO_DEPRECATED QString KFileItem::timeString(unsigned int which) const { if (!d) { return QString(); } switch (which) { case KIO::UDSEntry::UDS_ACCESS_TIME: return timeString(AccessTime); case KIO::UDSEntry::UDS_CREATION_TIME: return timeString(CreationTime); case KIO::UDSEntry::UDS_MODIFICATION_TIME: default: return timeString(ModificationTime); } } #endif #ifndef KIOCORE_NO_DEPRECATED void KFileItem::assign(const KFileItem &item) { *this = item; } #endif QUrl KFileItem::mostLocalUrl(bool *local) const { if (!d) { return QUrl(); } const QString local_path = localPath(); if (!local_path.isEmpty()) { if (local) { *local = true; } return QUrl::fromLocalFile(local_path); } else { if (local) { *local = d->m_bIsLocalUrl; } return d->m_url; } } QDataStream &operator<< (QDataStream &s, const KFileItem &a) { if (a.d) { // We don't need to save/restore anything that refresh() invalidates, // since that means we can re-determine those by ourselves. s << a.d->m_url; s << a.d->m_strName; s << a.d->m_strText; } else { s << QUrl(); s << QString(); s << QString(); } return s; } QDataStream &operator>> (QDataStream &s, KFileItem &a) { QUrl url; QString strName, strText; s >> url; s >> strName; s >> strText; if (!a.d) { qCWarning(KIO_CORE) << "null item"; return s; } if (url.isEmpty()) { a.d = nullptr; return s; } a.d->m_url = url; a.d->m_strName = strName; a.d->m_strText = strText; a.d->m_bIsLocalUrl = a.d->m_url.isLocalFile(); a.d->m_bMimeTypeKnown = false; a.refresh(); return s; } QUrl KFileItem::url() const { if (!d) { return QUrl(); } return d->m_url; } mode_t KFileItem::permissions() const { if (!d) { return 0; } + d->ensureInitialized(); + return d->m_permissions; } mode_t KFileItem::mode() const { if (!d) { return 0; } + d->ensureInitialized(); + return d->m_fileMode; } bool KFileItem::isLink() const { if (!d) { return false; } + d->ensureInitialized(); + return d->m_bLink; } bool KFileItem::isLocalFile() const { if (!d) { return false; } return d->m_bIsLocalUrl; } QString KFileItem::text() const { if (!d) { return QString(); } return d->m_strText; } QString KFileItem::name(bool lowerCase) const { if (!d) { return QString(); } if (!lowerCase) { return d->m_strName; } else if (d->m_strLowerCaseName.isNull()) { d->m_strLowerCaseName = d->m_strName.toLower(); } return d->m_strLowerCaseName; } QUrl KFileItem::targetUrl() const { if (!d) { return QUrl(); } const QString targetUrlStr = d->m_entry.stringValue(KIO::UDSEntry::UDS_TARGET_URL); if (!targetUrlStr.isEmpty()) { return QUrl(targetUrlStr); } else { return url(); } } /* * Mimetype handling. * * Initial state: m_mimeType = QMimeType(). * When currentMimeType() is called first: fast mimetype determination, * might either find an accurate mimetype (-> Final state), otherwise we * set m_mimeType but not m_bMimeTypeKnown (-> Intermediate state) * Intermediate state: determineMimeType() does the real determination -> Final state. * * If delayedMimeTypes isn't set, then we always go to the Final state directly. */ QMimeType KFileItem::currentMimeType() const { if (!d) { return QMimeType(); } if (!d->m_mimeType.isValid()) { // On-demand fast (but not always accurate) mimetype determination Q_ASSERT(!d->m_url.isEmpty()); QMimeDatabase db; if (isDir()) { d->m_mimeType = db.mimeTypeForName(QStringLiteral("inode/directory")); return d->m_mimeType; } const QUrl url = mostLocalUrl(); if (d->m_delayedMimeTypes) { const QList mimeTypes = db.mimeTypesForFileName(url.path()); if (mimeTypes.isEmpty()) { d->m_mimeType = db.mimeTypeForName(QStringLiteral("application/octet-stream")); d->m_bMimeTypeKnown = false; } else { d->m_mimeType = mimeTypes.first(); // If there were conflicting globs. determineMimeType will be able to do better. d->m_bMimeTypeKnown = (mimeTypes.count() == 1); } } else { // ## d->m_fileMode isn't used anymore (for remote urls) - d->m_mimeType = db.mimeTypeForUrl(url); + d->determineMimeTypeHelper(url); d->m_bMimeTypeKnown = true; } } return d->m_mimeType; } KIO::UDSEntry KFileItem::entry() const { if (!d) { return KIO::UDSEntry(); } + d->ensureInitialized(); + return d->m_entry; } bool KFileItem::isNull() const { return d == nullptr; } KFileItemList::KFileItemList() { } KFileItemList::KFileItemList(const QList &items) : QList(items) { } KFileItem KFileItemList::findByName(const QString &fileName) const { const_iterator it = begin(); const const_iterator itend = end(); for (; it != itend; ++it) { if ((*it).name() == fileName) { return *it; } } return KFileItem(); } KFileItem KFileItemList::findByUrl(const QUrl &url) const { const_iterator it = begin(); const const_iterator itend = end(); for (; it != itend; ++it) { if ((*it).url() == url) { return *it; } } return KFileItem(); } QList KFileItemList::urlList() const { QList lst; const_iterator it = begin(); const const_iterator itend = end(); for (; it != itend; ++it) { lst.append((*it).url()); } return lst; } QList KFileItemList::targetUrlList() const { QList lst; const_iterator it = begin(); const const_iterator itend = end(); for (; it != itend; ++it) { lst.append((*it).targetUrl()); } return lst; } bool KFileItem::isDesktopFile() const { return checkDesktopFile(*this, true); } bool KFileItem::isRegularFile() const { if (!d) { return false; } + d->ensureInitialized(); + return (d->m_fileMode & QT_STAT_MASK) == QT_STAT_REG; } QDebug operator<<(QDebug stream, const KFileItem &item) { QDebugStateSaver saver(stream); stream.nospace(); if (item.isNull()) { stream << "[null KFileItem]"; } else { stream << "[KFileItem for " << item.url() << "]"; } return stream; } diff --git a/src/core/kfileitem.h b/src/core/kfileitem.h index bb72ceab..e080b6e6 100644 --- a/src/core/kfileitem.h +++ b/src/core/kfileitem.h @@ -1,605 +1,624 @@ /* This file is part of the KDE project Copyright (C) 1999-2006 David Faure This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KFILEITEM_H #define KFILEITEM_H #include "kiocore_export.h" #include #include #include #include #include #include #include #include #include class KFileItemPrivate; /** * @class KFileItem kfileitem.h * * A KFileItem is a generic class to handle a file, local or remote. * In particular, it makes it easier to handle the result of KIO::listDir * (UDSEntry isn't very friendly to use). * It includes many file attributes such as mimetype, icon, text, mode, link... * * KFileItem is implicitly shared, i.e. it can be used as a value and copied around at almost no cost. */ class KIOCORE_EXPORT KFileItem { public: enum { Unknown = static_cast(-1) }; /** * The timestamps associated with a file. * - ModificationTime: the time the file's contents were last modified * - AccessTime: the time the file was last accessed (last read or written to) * - CreationTime: the time the file was created */ enum FileTimes { // warning: don't change without looking at the Private class ModificationTime = 0, AccessTime = 1, CreationTime = 2 //ChangeTime }; + enum MimeTypeDetermination { + NormalMimeTypeDetermination = 0, + SkipMimeTypeFromContent + }; + /** * Null KFileItem. Doesn't represent any file, only exists for convenience. */ KFileItem(); /** * Creates an item representing a file, from a UDSEntry. * This is the preferred constructor when using KIO::listDir(). * * @param entry the KIO entry used to get the file, contains info about it * @param itemOrDirUrl the URL of the item or of the directory containing this item (see urlIsDirectory). * @param delayedMimeTypes specifies if the mimetype of the given * URL should be determined immediately or on demand. * See the bool delayedMimeTypes in the KDirLister constructor. * @param urlIsDirectory specifies if the url is just the directory of the * fileitem and the filename from the UDSEntry should be used. * * When creating KFileItems out of the UDSEntry emitted by a KIO list job, * use KFileItem(entry, listjob->url(), delayedMimeTypes, true); */ KFileItem(const KIO::UDSEntry &entry, const QUrl &itemOrDirUrl, bool delayedMimeTypes = false, bool urlIsDirectory = false); /** * Creates an item representing a file, from all the necessary info for it. * @param mode the file mode (according to stat() (e.g. S_IFDIR...) * Set to KFileItem::Unknown if unknown. For local files, KFileItem will use stat(). * @param permissions the access permissions * If you set both the mode and the permissions, you save a ::stat() for * local files. * Set to KFileItem::Unknown if you don't know the mode or the permission. * @param url the file url * * @param delayedMimeTypes specify if the mimetype of the given URL * should be determined immediately or on demand * @deprecated since 5.0. Most callers gave Unknown for mode and permissions, * so just port to KFileItem(url) and setDelayedMimeTypes(true) if necessary. */ #ifndef KIOCORE_NO_DEPRECATED KIOCORE_DEPRECATED KFileItem(mode_t mode, mode_t permissions, const QUrl &url, bool delayedMimeTypes = false); #endif /** * Creates an item representing a file, for which the mimetype is already known. * @param url the file url * @param mimeType the name of the file's mimetype * @param mode the mode (S_IFDIR...) */ 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 */ KFileItem(const KFileItem&); /** * Destructor */ ~KFileItem(); /** * Move constructor * @since 5.43 */ KFileItem(KFileItem&&); /** * Copy assignment */ KFileItem& operator=(const KFileItem&); /** * Move assignment * @since 5.43 */ KFileItem& operator=(KFileItem&&); /** * Throw away and re-read (for local files) all information about the file. * This is called when the _file_ changes. */ void refresh(); /** * Re-reads mimetype information. * This is called when the mimetype database changes. */ void refreshMimeType(); /** * Sets mimetype determination to be immediate or on demand. * Call this after the constructor, and before using any mimetype-related method. * @since 5.0 */ void setDelayedMimeTypes(bool b); /** * Returns the url of the file. * @return the url of the file */ QUrl url() const; /** * Sets the item's URL. Do not call unless you know what you are doing! * (used for example when an item got renamed). * @param url the item's URL */ void setUrl(const QUrl &url); /** * Sets the item's local path (UDS_LOCAL_PATH). Do not call unless you know what you are doing! * This won't change the item's name or URL. * (used for example when an item got renamed). * @param path the item's local path * @since 5.20 */ void setLocalPath(const QString &path); /** * Sets the item's name (i.e. the filename). * This is automatically done by setUrl, to set the name from the URL's fileName(). * This method is provided for some special cases like relative paths as names (KFindPart) * @param name the item's name */ void setName(const QString &name); /** * Returns the permissions of the file (stat.st_mode containing only permissions). * @return the permissions of the file */ mode_t permissions() const; /** * Returns the access permissions for the file as a string. * @return the access permission as string */ QString permissionsString() const; /** * Tells if the file has extended access level information ( Posix ACL ) * @return true if the file has extend ACL information or false if it hasn't */ bool hasExtendedACL() const; /** * Returns the access control list for the file. * @return the access control list as a KACL */ KACL ACL() const; /** * Returns the default access control list for the directory. * @return the default access control list as a KACL */ KACL defaultACL() const; /** * Returns the file type (stat.st_mode containing only S_IFDIR, S_IFLNK, ...). * @return the file type */ mode_t mode() const; /** * Returns the owner of the file. * @return the file's owner */ QString user() const; /** * Returns the group of the file. * @return the file's group */ QString group() const; /** * Returns true if this item represents a link in the UNIX sense of * a link. * @return true if the file is a link */ bool isLink() const; /** * Returns true if this item represents a directory. * @return true if the item is a directory */ bool isDir() const; /** * Returns true if this item represents a file (and not a directory) * @return true if the item is a file */ bool isFile() const; /** * Checks whether the file or directory is readable. In some cases * (remote files), we may return true even though it can't be read. * @return true if the file can be read - more precisely, * false if we know for sure it can't */ bool isReadable() const; /** * Checks whether the file or directory is writable. In some cases * (remote files), we may return true even though it can't be written to. * @return true if the file or directory can be written to - more precisely, * false if we know for sure it can't */ bool isWritable() const; /** * Checks whether the file is hidden. * @return true if the file is hidden. */ bool isHidden() const; /** * @return true if the file is a remote URL, or a local file on a network mount. * It will return false only for really-local file systems. * @since 4.7.4 */ bool isSlow() const; /** * Checks whether the file is a readable local .desktop file, * i.e. a file whose path can be given to KDesktopFile * @return true if the file is a desktop file. * @since 4.1 */ bool isDesktopFile() const; /** * Returns the link destination if isLink() == true. * @return the link destination. QString() if the item is not a link */ QString linkDest() const; /** * Returns the target url of the file, which is the same as url() * in cases where the slave doesn't specify UDS_TARGET_URL * @return the target url. * @since 4.1 */ QUrl targetUrl() const; /** * Returns the local path if isLocalFile() == true or the KIO item has * a UDS_LOCAL_PATH atom. * @return the item local path, or QString() if not known */ QString localPath() const; /** * Returns the size of the file, if known. * @return the file size, or 0 if not known */ KIO::filesize_t size() const; /** * Requests the modification, access or creation time, depending on @p which. * @param which the timestamp * @return the time asked for, QDateTime() if not available * @see timeString() */ QDateTime time(FileTimes which) const; /** * Requests the modification, access or creation time as a string, depending * on @p which. * @param which the timestamp * @returns a formatted string of the requested time. * @see time */ QString timeString(FileTimes which = ModificationTime) const; #ifndef KIOCORE_NO_DEPRECATED KIOCORE_DEPRECATED QString timeString(unsigned int which) const; #endif /** * Returns true if the file is a local file. * @return true if the file is local, false otherwise */ bool isLocalFile() const; /** * Returns the text of the file item. * It's not exactly the filename since some decoding happens ('%2F'->'/'). * @return the text of the file item */ QString text() const; /** * Return the name of the file item (without a path). * Similar to text(), but unencoded, i.e. the original name. * @param lowerCase if true, the name will be returned in lower case, * which is useful to speed up sorting by name, case insensitively. * @return the file's name */ QString name(bool lowerCase = false) const; /** * Returns the mimetype of the file item. * If @p delayedMimeTypes was used in the constructor, this will determine * the mimetype first. Equivalent to determineMimeType()->name() * @return the mime type of the file */ QString mimetype() const; /** * Returns the mimetype of the file item. * If delayedMimeTypes was used in the constructor, this will determine * the mimetype first. * @return the mime type */ QMimeType determineMimeType() const; /** * Returns the currently known mimetype of the file item. * This will not try to determine the mimetype if unknown. * @return the known mime type */ QMimeType currentMimeType() const; /** * @return true if we have determined the final icon of this file already. * @since 4.10.2 */ bool isFinalIconKnown() const; /** * @return true if we have determined the mimetype of this file already, * i.e. if determineMimeType() will be fast. Otherwise it will have to * find what the mimetype is, which is a possibly slow operation; usually * this is delayed until necessary. */ bool isMimeTypeKnown() const; /** * Returns the user-readable string representing the type of this file, * like "OpenDocument Text File". * @return the type of this KFileItem */ QString mimeComment() const; /** * Returns the full path name to the icon that represents * this mime type. * @return iconName the name of the file's icon */ QString iconName() const; /** * Returns the overlays (bitfield of KIconLoader::*Overlay flags) that are used * for this item's pixmap. Overlays are used to show for example, whether * a file can be modified. * @return the overlays of the pixmap */ QStringList overlays() const; /** * A comment which can contain anything - even rich text. It will * simply be displayed to the user as is. * * @since 4.6 */ QString comment() const; /** * Returns the string to be displayed in the statusbar, * e.g. when the mouse is over this item * @return the status bar information */ QString getStatusBarInfo() const; /** * Returns true if files can be dropped over this item. * Contrary to popular belief, not only dirs will return true :) * Executables, .desktop files, will do so as well. * @return true if you can drop files over the item * * @deprecated This logic is application-dependent, the behavior described above * mostly makes sense for file managers only. * KDirModel has setDropsAllowed for similar (but configurable) logic. */ #ifndef KIOCORE_NO_DEPRECATED KIOCORE_DEPRECATED bool acceptsDrops() const; #endif /** * Returns the UDS entry. Used by the tree view to access all details * by position. * @return the UDS entry */ KIO::UDSEntry entry() const; /** * Return true if this item is a regular file, * false otherwise (directory, link, character/block device, fifo, socket) * @since 4.3 */ bool isRegularFile() const; /** * Somewhat like a comparison operator, but more explicit, * and it can detect that two fileitems differ if any property of the file item * has changed (file size, modification date, etc.). Two items are equal if * all properties are equal. In contrast, operator== only compares URLs. * @param item the item to compare * @return true if all values are equal */ bool cmp(const KFileItem &item) const; /** * Returns true if both items share the same URL. */ bool operator==(const KFileItem &other) const; /** * Returns true if both items do not share the same URL. */ bool operator!=(const KFileItem &other) const; /** * Returns true if this item's URL is lexically less than other's URL; otherwise returns false * @since 5.48 */ bool operator<(const KFileItem &other) const; /** * Returns true if this item's URL is lexically less than url other; otherwise returns false * @since 5.48 */ bool operator<(const QUrl &other) const; /** * Converts this KFileItem to a QVariant, this allows to use KFileItem * in QVariant() constructor */ operator QVariant() const; /** * @deprecated simply use '=' */ #ifndef KIOCORE_NO_DEPRECATED KIOCORE_DEPRECATED void assign(const KFileItem &item); #endif /** * Tries to give a local URL for this file item if possible. * The given boolean indicates if the returned url is local or not. * \since 4.6 */ QUrl mostLocalUrl(bool *local = nullptr) const; /** * @deprecated since 5.0 add '&' in front of your boolean argument */ #ifndef KIOCORE_NO_DEPRECATED QUrl mostLocalUrl(bool &local) const { return mostLocalUrl(&local); } #endif /** * Return true if default-constructed */ bool isNull() const; private: QSharedDataPointer d; /** * Hides the file. */ void setHidden(); private: KIOCORE_EXPORT friend QDataStream &operator<< (QDataStream &s, const KFileItem &a); KIOCORE_EXPORT friend QDataStream &operator>> (QDataStream &s, KFileItem &a); friend class KFileItemTest; friend class KCoreDirListerCache; }; Q_DECLARE_METATYPE(KFileItem) Q_DECLARE_TYPEINFO(KFileItem, Q_MOVABLE_TYPE); inline uint qHash(const KFileItem &item) { return qHash(item.url()); } /** * @class KFileItemList kfileitem.h * * List of KFileItems, which adds a few helper * methods to QList. */ class KIOCORE_EXPORT KFileItemList : public QList { public: /// Creates an empty list of file items. KFileItemList(); /// Creates a new KFileItemList from a QList of file @p items. KFileItemList(const QList &items); /** * Find a KFileItem by name and return it. * @return the item with the given name, or a null-item if none was found * (see KFileItem::isNull()) */ KFileItem findByName(const QString &fileName) const; /** * Find a KFileItem by URL and return it. * @return the item with the given URL, or a null-item if none was found * (see KFileItem::isNull()) */ KFileItem findByUrl(const QUrl &url) const; /// @return the list of URLs that those items represent QList urlList() const; /// @return the list of target URLs that those items represent /// @since 4.2 QList targetUrlList() const; // TODO KDE-5 add d pointer here so that we can merge KFileItemListProperties into KFileItemList }; KIOCORE_EXPORT QDataStream &operator<< (QDataStream &s, const KFileItem &a); KIOCORE_EXPORT QDataStream &operator>> (QDataStream &s, KFileItem &a); /** * Support for qDebug() << aFileItem * \since 4.4 */ KIOCORE_EXPORT QDebug operator<<(QDebug stream, const KFileItem &item); #endif