Warning, file /frameworks/kio/src/core/kfileitem.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).
0001 /* 0002 This file is part of the KDE project 0003 SPDX-FileCopyrightText: 1999-2011 David Faure <faure@kde.org> 0004 SPDX-FileCopyrightText: 2001 Carsten Pfeiffer <pfeiffer@kde.org> 0005 0006 SPDX-License-Identifier: LGPL-2.0-or-later 0007 */ 0008 0009 #include "kfileitem.h" 0010 0011 #include "config-kiocore.h" 0012 0013 #if HAVE_POSIX_ACL 0014 #include "../aclhelpers_p.h" 0015 #endif 0016 0017 #include "../utils_p.h" 0018 #include "kiocoredebug.h" 0019 #include "kioglobal_p.h" 0020 0021 #include <QDataStream> 0022 #include <QDate> 0023 #include <QDebug> 0024 #include <QDir> 0025 #include <QDirIterator> 0026 #include <QLocale> 0027 #include <QMimeDatabase> 0028 0029 #include <KConfigGroup> 0030 #include <KDesktopFile> 0031 #include <KLocalizedString> 0032 #include <kmountpoint.h> 0033 #ifndef Q_OS_WIN 0034 #include <knfsshare.h> 0035 #include <ksambashare.h> 0036 #endif 0037 #include <KFileSystemType> 0038 #include <KProtocolManager> 0039 #include <KShell> 0040 0041 #define KFILEITEM_DEBUG 0 0042 0043 class KFileItemPrivate : public QSharedData 0044 { 0045 public: 0046 KFileItemPrivate(const KIO::UDSEntry &entry, 0047 mode_t mode, 0048 mode_t permissions, 0049 const QUrl &itemOrDirUrl, 0050 bool urlIsDirectory, 0051 bool delayedMimeTypes, 0052 KFileItem::MimeTypeDetermination mimeTypeDetermination) 0053 : m_entry(entry) 0054 , m_url(itemOrDirUrl) 0055 , m_strName() 0056 , m_strText() 0057 , m_iconName() 0058 , m_strLowerCaseName() 0059 , m_mimeType() 0060 , m_fileMode(mode) 0061 , m_permissions(permissions) 0062 , m_addACL(false) 0063 , m_bLink(false) 0064 , m_bIsLocalUrl(itemOrDirUrl.isLocalFile()) 0065 , m_bMimeTypeKnown(false) 0066 , m_delayedMimeTypes(delayedMimeTypes) 0067 , m_useIconNameCache(false) 0068 , m_hidden(Auto) 0069 , m_slow(SlowUnknown) 0070 , m_bSkipMimeTypeFromContent(mimeTypeDetermination == KFileItem::SkipMimeTypeFromContent) 0071 , m_bInitCalled(false) 0072 { 0073 if (entry.count() != 0) { 0074 readUDSEntry(urlIsDirectory); 0075 } else { 0076 Q_ASSERT(!urlIsDirectory); 0077 m_strName = itemOrDirUrl.fileName(); 0078 m_strText = KIO::decodeFileName(m_strName); 0079 } 0080 } 0081 0082 /** 0083 * Call init() if not yet done. 0084 */ 0085 void ensureInitialized() const; 0086 0087 /** 0088 * Computes the text and mode from the UDSEntry. 0089 */ 0090 void init() const; 0091 0092 QString localPath() const; 0093 KIO::filesize_t size() const; 0094 KIO::filesize_t recursiveSize() const; 0095 QDateTime time(KFileItem::FileTimes which) const; 0096 void setTime(KFileItem::FileTimes which, uint time_t_val) const; 0097 void setTime(KFileItem::FileTimes which, const QDateTime &val) const; 0098 bool cmp(const KFileItemPrivate &item) const; 0099 void printCompareDebug(const KFileItemPrivate &item) const; 0100 bool isSlow() const; 0101 0102 /** 0103 * Extracts the data from the UDSEntry member and updates the KFileItem 0104 * accordingly. 0105 */ 0106 void readUDSEntry(bool _urlIsDirectory); 0107 0108 /** 0109 * Parses the given permission set and provides it for access() 0110 */ 0111 QString parsePermissions(mode_t perm) const; 0112 0113 /** 0114 * Mime type helper 0115 */ 0116 void determineMimeTypeHelper(const QUrl &url) const; 0117 0118 /** 0119 * The UDSEntry that contains the data for this fileitem, if it came from a directory listing. 0120 */ 0121 mutable KIO::UDSEntry m_entry; 0122 /** 0123 * The url of the file 0124 */ 0125 QUrl m_url; 0126 0127 /** 0128 * The text for this item, i.e. the file name without path, 0129 */ 0130 QString m_strName; 0131 0132 /** 0133 * The text for this item, i.e. the file name without path, decoded 0134 * ('%%' becomes '%', '%2F' becomes '/') 0135 */ 0136 QString m_strText; 0137 0138 /** 0139 * The icon name for this item. 0140 */ 0141 mutable QString m_iconName; 0142 0143 /** 0144 * The filename in lower case (to speed up sorting) 0145 */ 0146 mutable QString m_strLowerCaseName; 0147 0148 /** 0149 * The MIME type of the file 0150 */ 0151 mutable QMimeType m_mimeType; 0152 0153 /** 0154 * The file mode 0155 */ 0156 mutable mode_t m_fileMode; 0157 /** 0158 * The permissions 0159 */ 0160 mutable mode_t m_permissions; 0161 0162 /** 0163 * Whether the UDSEntry ACL fields should be added to m_entry. 0164 */ 0165 mutable bool m_addACL : 1; 0166 0167 /** 0168 * Whether the file is a link 0169 */ 0170 mutable bool m_bLink : 1; 0171 /** 0172 * True if local file 0173 */ 0174 bool m_bIsLocalUrl : 1; 0175 0176 mutable bool m_bMimeTypeKnown : 1; 0177 mutable bool m_delayedMimeTypes : 1; 0178 0179 /** True if m_iconName should be used as cache. */ 0180 mutable bool m_useIconNameCache : 1; 0181 0182 // Auto: check leading dot. 0183 enum { Auto, Hidden, Shown } m_hidden : 3; 0184 0185 // Slow? (nfs/smb/ssh) 0186 mutable enum { SlowUnknown, Fast, Slow } m_slow : 3; 0187 0188 /** 0189 * True if MIME type determination by content should be skipped 0190 */ 0191 bool m_bSkipMimeTypeFromContent : 1; 0192 0193 /** 0194 * True if init() was called on demand 0195 */ 0196 mutable bool m_bInitCalled : 1; 0197 0198 // For special case like link to dirs over FTP 0199 QString m_guessedMimeType; 0200 mutable QString m_access; 0201 }; 0202 0203 void KFileItemPrivate::ensureInitialized() const 0204 { 0205 if (!m_bInitCalled) { 0206 init(); 0207 } 0208 } 0209 0210 void KFileItemPrivate::init() const 0211 { 0212 m_access.clear(); 0213 // metaInfo = KFileMetaInfo(); 0214 0215 // stat() local files if needed 0216 const bool shouldStat = (m_fileMode == KFileItem::Unknown || m_permissions == KFileItem::Unknown || m_entry.count() == 0) && m_url.isLocalFile(); 0217 if (shouldStat) { 0218 /* directories may not have a slash at the end if we want to stat() 0219 * them; it requires that we change into it .. which may not be allowed 0220 * stat("/is/unaccessible") -> rwx------ 0221 * stat("/is/unaccessible/") -> EPERM H.Z. 0222 * This is the reason for the StripTrailingSlash 0223 */ 0224 QT_STATBUF buf; 0225 const QString path = m_url.adjusted(QUrl::StripTrailingSlash).toLocalFile(); 0226 const QByteArray pathBA = QFile::encodeName(path); 0227 if (QT_LSTAT(pathBA.constData(), &buf) == 0) { 0228 m_entry.reserve(9); 0229 m_entry.replace(KIO::UDSEntry::UDS_DEVICE_ID, buf.st_dev); 0230 m_entry.replace(KIO::UDSEntry::UDS_INODE, buf.st_ino); 0231 0232 mode_t mode = buf.st_mode; 0233 if (Utils::isLinkMask(buf.st_mode)) { 0234 m_bLink = true; 0235 if (QT_STAT(pathBA.constData(), &buf) == 0) { 0236 mode = buf.st_mode; 0237 } else { // link pointing to nowhere (see FileProtocol::createUDSEntry() in ioslaves/file/file.cpp) 0238 mode = (QT_STAT_MASK - 1) | S_IRWXU | S_IRWXG | S_IRWXO; 0239 } 0240 } 0241 0242 const mode_t type = mode & QT_STAT_MASK; 0243 0244 m_entry.replace(KIO::UDSEntry::UDS_SIZE, buf.st_size); 0245 m_entry.replace(KIO::UDSEntry::UDS_FILE_TYPE, type); // extract file type 0246 m_entry.replace(KIO::UDSEntry::UDS_ACCESS, mode & 07777); // extract permissions 0247 m_entry.replace(KIO::UDSEntry::UDS_MODIFICATION_TIME, buf.st_mtime); // TODO: we could use msecs too... 0248 m_entry.replace(KIO::UDSEntry::UDS_ACCESS_TIME, buf.st_atime); 0249 #ifndef Q_OS_WIN 0250 m_entry.replace(KIO::UDSEntry::UDS_USER, KUser(buf.st_uid).loginName()); 0251 m_entry.replace(KIO::UDSEntry::UDS_GROUP, KUserGroup(buf.st_gid).name()); 0252 #endif 0253 0254 // TODO: these can be removed, we can use UDS_FILE_TYPE and UDS_ACCESS everywhere 0255 if (m_fileMode == KFileItem::Unknown) { 0256 m_fileMode = type; // extract file type 0257 } 0258 if (m_permissions == KFileItem::Unknown) { 0259 m_permissions = mode & 07777; // extract permissions 0260 } 0261 0262 #if HAVE_POSIX_ACL 0263 if (m_addACL) { 0264 appendACLAtoms(pathBA, m_entry, type); 0265 } 0266 #endif 0267 } 0268 } 0269 0270 m_bInitCalled = true; 0271 } 0272 0273 void KFileItemPrivate::readUDSEntry(bool _urlIsDirectory) 0274 { 0275 // extract fields from the KIO::UDS Entry 0276 0277 m_fileMode = m_entry.numberValue(KIO::UDSEntry::UDS_FILE_TYPE, KFileItem::Unknown); 0278 m_permissions = m_entry.numberValue(KIO::UDSEntry::UDS_ACCESS, KFileItem::Unknown); 0279 m_strName = m_entry.stringValue(KIO::UDSEntry::UDS_NAME); 0280 0281 const QString displayName = m_entry.stringValue(KIO::UDSEntry::UDS_DISPLAY_NAME); 0282 if (!displayName.isEmpty()) { 0283 m_strText = displayName; 0284 } else { 0285 m_strText = KIO::decodeFileName(m_strName); 0286 } 0287 0288 const QString urlStr = m_entry.stringValue(KIO::UDSEntry::UDS_URL); 0289 const bool UDS_URL_seen = !urlStr.isEmpty(); 0290 if (UDS_URL_seen) { 0291 m_url = QUrl(urlStr); 0292 if (m_url.isLocalFile()) { 0293 m_bIsLocalUrl = true; 0294 } 0295 } 0296 QMimeDatabase db; 0297 const QString mimeTypeStr = m_entry.stringValue(KIO::UDSEntry::UDS_MIME_TYPE); 0298 m_bMimeTypeKnown = !mimeTypeStr.isEmpty(); 0299 if (m_bMimeTypeKnown) { 0300 m_mimeType = db.mimeTypeForName(mimeTypeStr); 0301 } 0302 0303 m_guessedMimeType = m_entry.stringValue(KIO::UDSEntry::UDS_GUESSED_MIME_TYPE); 0304 m_bLink = !m_entry.stringValue(KIO::UDSEntry::UDS_LINK_DEST).isEmpty(); // we don't store the link dest 0305 0306 const int hiddenVal = m_entry.numberValue(KIO::UDSEntry::UDS_HIDDEN, -1); 0307 m_hidden = hiddenVal == 1 ? Hidden : (hiddenVal == 0 ? Shown : Auto); 0308 0309 if (_urlIsDirectory && !UDS_URL_seen && !m_strName.isEmpty() && m_strName != QLatin1String(".")) { 0310 m_url.setPath(Utils::concatPaths(m_url.path(), m_strName)); 0311 } 0312 0313 m_iconName.clear(); 0314 } 0315 0316 // Inlined because it is used only in one place 0317 inline KIO::filesize_t KFileItemPrivate::size() const 0318 { 0319 ensureInitialized(); 0320 0321 // Extract it from the KIO::UDSEntry 0322 long long fieldVal = m_entry.numberValue(KIO::UDSEntry::UDS_SIZE, -1); 0323 if (fieldVal != -1) { 0324 return fieldVal; 0325 } 0326 0327 // If not in the KIO::UDSEntry, or if UDSEntry empty, use stat() [if local URL] 0328 if (m_bIsLocalUrl) { 0329 return QFileInfo(m_url.toLocalFile()).size(); 0330 } 0331 return 0; 0332 } 0333 0334 KIO::filesize_t KFileItemPrivate::recursiveSize() const 0335 { 0336 // Extract it from the KIO::UDSEntry 0337 long long fieldVal = m_entry.numberValue(KIO::UDSEntry::UDS_RECURSIVE_SIZE, -1); 0338 if (fieldVal != -1) { 0339 return static_cast<KIO::filesize_t>(fieldVal); 0340 } 0341 0342 return 0; 0343 } 0344 0345 static uint udsFieldForTime(KFileItem::FileTimes mappedWhich) 0346 { 0347 switch (mappedWhich) { 0348 case KFileItem::ModificationTime: 0349 return KIO::UDSEntry::UDS_MODIFICATION_TIME; 0350 case KFileItem::AccessTime: 0351 return KIO::UDSEntry::UDS_ACCESS_TIME; 0352 case KFileItem::CreationTime: 0353 return KIO::UDSEntry::UDS_CREATION_TIME; 0354 } 0355 return 0; 0356 } 0357 0358 void KFileItemPrivate::setTime(KFileItem::FileTimes mappedWhich, uint time_t_val) const 0359 { 0360 m_entry.replace(udsFieldForTime(mappedWhich), time_t_val); 0361 } 0362 0363 void KFileItemPrivate::setTime(KFileItem::FileTimes mappedWhich, const QDateTime &val) const 0364 { 0365 const QDateTime dt = val.toLocalTime(); // #160979 0366 setTime(mappedWhich, dt.toSecsSinceEpoch()); 0367 } 0368 0369 QDateTime KFileItemPrivate::time(KFileItem::FileTimes mappedWhich) const 0370 { 0371 ensureInitialized(); 0372 0373 // Extract it from the KIO::UDSEntry 0374 const uint uds = udsFieldForTime(mappedWhich); 0375 if (uds > 0) { 0376 const long long fieldVal = m_entry.numberValue(uds, -1); 0377 if (fieldVal != -1) { 0378 return QDateTime::fromSecsSinceEpoch(fieldVal); 0379 } 0380 } 0381 0382 return QDateTime(); 0383 } 0384 0385 void KFileItemPrivate::printCompareDebug(const KFileItemPrivate &item) const 0386 { 0387 Q_UNUSED(item); 0388 0389 #if KFILEITEM_DEBUG 0390 const KIO::UDSEntry &otherEntry = item.m_entry; 0391 0392 qDebug() << "Comparing" << m_url << "and" << item.m_url; 0393 qDebug() << " name" << (m_strName == item.m_strName); 0394 qDebug() << " local" << (m_bIsLocalUrl == item.m_bIsLocalUrl); 0395 0396 qDebug() << " mode" << (m_fileMode == item.m_fileMode); 0397 qDebug() << " perm" << (m_permissions == item.m_permissions); 0398 qDebug() << " group" << (m_entry.stringValue(KIO::UDSEntry::UDS_GROUP) == otherEntry.stringValue(KIO::UDSEntry::UDS_GROUP)); 0399 qDebug() << " user" << (m_entry.stringValue(KIO::UDSEntry::UDS_USER) == otherEntry.stringValue(KIO::UDSEntry::UDS_USER)); 0400 0401 qDebug() << " UDS_EXTENDED_ACL" << (m_entry.stringValue(KIO::UDSEntry::UDS_EXTENDED_ACL) == otherEntry.stringValue(KIO::UDSEntry::UDS_EXTENDED_ACL)); 0402 qDebug() << " UDS_ACL_STRING" << (m_entry.stringValue(KIO::UDSEntry::UDS_ACL_STRING) == otherEntry.stringValue(KIO::UDSEntry::UDS_ACL_STRING)); 0403 qDebug() << " UDS_DEFAULT_ACL_STRING" 0404 << (m_entry.stringValue(KIO::UDSEntry::UDS_DEFAULT_ACL_STRING) == otherEntry.stringValue(KIO::UDSEntry::UDS_DEFAULT_ACL_STRING)); 0405 0406 qDebug() << " m_bLink" << (m_bLink == item.m_bLink); 0407 qDebug() << " m_hidden" << (m_hidden == item.m_hidden); 0408 0409 qDebug() << " size" << (size() == item.size()); 0410 0411 qDebug() << " ModificationTime" << m_entry.numberValue(KIO::UDSEntry::UDS_MODIFICATION_TIME) 0412 << otherEntry.numberValue(KIO::UDSEntry::UDS_MODIFICATION_TIME); 0413 0414 qDebug() << " UDS_ICON_NAME" << (m_entry.stringValue(KIO::UDSEntry::UDS_ICON_NAME) == otherEntry.stringValue(KIO::UDSEntry::UDS_ICON_NAME)); 0415 #endif 0416 } 0417 0418 // Inlined because it is used only in one place 0419 inline bool KFileItemPrivate::cmp(const KFileItemPrivate &item) const 0420 { 0421 if (item.m_bInitCalled) { 0422 ensureInitialized(); 0423 } 0424 0425 if (m_bInitCalled) { 0426 item.ensureInitialized(); 0427 } 0428 0429 #if KFILEITEM_DEBUG 0430 printCompareDebug(item); 0431 #endif 0432 0433 /* clang-format off */ 0434 return (m_strName == item.m_strName 0435 && m_bIsLocalUrl == item.m_bIsLocalUrl 0436 && m_fileMode == item.m_fileMode 0437 && m_permissions == item.m_permissions 0438 && m_entry.stringValue(KIO::UDSEntry::UDS_GROUP) == item.m_entry.stringValue(KIO::UDSEntry::UDS_GROUP) 0439 && m_entry.stringValue(KIO::UDSEntry::UDS_USER) == item.m_entry.stringValue(KIO::UDSEntry::UDS_USER) 0440 && m_entry.stringValue(KIO::UDSEntry::UDS_EXTENDED_ACL) == item.m_entry.stringValue(KIO::UDSEntry::UDS_EXTENDED_ACL) 0441 && m_entry.stringValue(KIO::UDSEntry::UDS_ACL_STRING) == item.m_entry.stringValue(KIO::UDSEntry::UDS_ACL_STRING) 0442 && m_entry.stringValue(KIO::UDSEntry::UDS_DEFAULT_ACL_STRING) == item.m_entry.stringValue(KIO::UDSEntry::UDS_DEFAULT_ACL_STRING) 0443 && m_bLink == item.m_bLink 0444 && m_hidden == item.m_hidden 0445 && size() == item.size() 0446 && m_entry.numberValue(KIO::UDSEntry::UDS_MODIFICATION_TIME) == item.m_entry.numberValue(KIO::UDSEntry::UDS_MODIFICATION_TIME) 0447 && m_entry.stringValue(KIO::UDSEntry::UDS_ICON_NAME) == item.m_entry.stringValue(KIO::UDSEntry::UDS_ICON_NAME) 0448 && m_entry.stringValue(KIO::UDSEntry::UDS_TARGET_URL) == item.m_entry.stringValue(KIO::UDSEntry::UDS_TARGET_URL) 0449 && m_entry.stringValue(KIO::UDSEntry::UDS_LOCAL_PATH) == item.m_entry.stringValue(KIO::UDSEntry::UDS_LOCAL_PATH)); 0450 /* clang-format on */ 0451 // Don't compare the MIME types here. They might not be known, and we don't want to 0452 // do the slow operation of determining them here. 0453 } 0454 0455 // Inlined because it is used only in one place 0456 inline QString KFileItemPrivate::parsePermissions(mode_t perm) const 0457 { 0458 ensureInitialized(); 0459 0460 static char buffer[12]; 0461 0462 char uxbit; 0463 char gxbit; 0464 char oxbit; 0465 0466 if ((perm & (S_IXUSR | S_ISUID)) == (S_IXUSR | S_ISUID)) { 0467 uxbit = 's'; 0468 } else if ((perm & (S_IXUSR | S_ISUID)) == S_ISUID) { 0469 uxbit = 'S'; 0470 } else if ((perm & (S_IXUSR | S_ISUID)) == S_IXUSR) { 0471 uxbit = 'x'; 0472 } else { 0473 uxbit = '-'; 0474 } 0475 0476 if ((perm & (S_IXGRP | S_ISGID)) == (S_IXGRP | S_ISGID)) { 0477 gxbit = 's'; 0478 } else if ((perm & (S_IXGRP | S_ISGID)) == S_ISGID) { 0479 gxbit = 'S'; 0480 } else if ((perm & (S_IXGRP | S_ISGID)) == S_IXGRP) { 0481 gxbit = 'x'; 0482 } else { 0483 gxbit = '-'; 0484 } 0485 0486 if ((perm & (S_IXOTH | S_ISVTX)) == (S_IXOTH | S_ISVTX)) { 0487 oxbit = 't'; 0488 } else if ((perm & (S_IXOTH | S_ISVTX)) == S_ISVTX) { 0489 oxbit = 'T'; 0490 } else if ((perm & (S_IXOTH | S_ISVTX)) == S_IXOTH) { 0491 oxbit = 'x'; 0492 } else { 0493 oxbit = '-'; 0494 } 0495 0496 // Include the type in the first char like ls does; people are more used to seeing it, 0497 // even though it's not really part of the permissions per se. 0498 if (m_bLink) { 0499 buffer[0] = 'l'; 0500 } else if (m_fileMode != KFileItem::Unknown) { 0501 if (Utils::isDirMask(m_fileMode)) { 0502 buffer[0] = 'd'; 0503 } 0504 #ifdef Q_OS_UNIX 0505 else if (S_ISSOCK(m_fileMode)) { 0506 buffer[0] = 's'; 0507 } else if (S_ISCHR(m_fileMode)) { 0508 buffer[0] = 'c'; 0509 } else if (S_ISBLK(m_fileMode)) { 0510 buffer[0] = 'b'; 0511 } else if (S_ISFIFO(m_fileMode)) { 0512 buffer[0] = 'p'; 0513 } 0514 #endif // Q_OS_UNIX 0515 else { 0516 buffer[0] = '-'; 0517 } 0518 } else { 0519 buffer[0] = '-'; 0520 } 0521 0522 buffer[1] = (((perm & S_IRUSR) == S_IRUSR) ? 'r' : '-'); 0523 buffer[2] = (((perm & S_IWUSR) == S_IWUSR) ? 'w' : '-'); 0524 buffer[3] = uxbit; 0525 buffer[4] = (((perm & S_IRGRP) == S_IRGRP) ? 'r' : '-'); 0526 buffer[5] = (((perm & S_IWGRP) == S_IWGRP) ? 'w' : '-'); 0527 buffer[6] = gxbit; 0528 buffer[7] = (((perm & S_IROTH) == S_IROTH) ? 'r' : '-'); 0529 buffer[8] = (((perm & S_IWOTH) == S_IWOTH) ? 'w' : '-'); 0530 buffer[9] = oxbit; 0531 // if (hasExtendedACL()) 0532 if (m_entry.contains(KIO::UDSEntry::UDS_EXTENDED_ACL)) { 0533 buffer[10] = '+'; 0534 buffer[11] = 0; 0535 } else { 0536 buffer[10] = 0; 0537 } 0538 0539 return QString::fromLatin1(buffer); 0540 } 0541 0542 void KFileItemPrivate::determineMimeTypeHelper(const QUrl &url) const 0543 { 0544 QMimeDatabase db; 0545 if (m_bSkipMimeTypeFromContent) { 0546 const QString scheme = url.scheme(); 0547 if (scheme.startsWith(QLatin1String("http")) || scheme == QLatin1String("mailto")) { 0548 m_mimeType = db.mimeTypeForName(QLatin1String("application/octet-stream")); 0549 } else { 0550 m_mimeType = db.mimeTypeForFile(url.path(), QMimeDatabase::MatchMode::MatchExtension); 0551 } 0552 } else { 0553 m_mimeType = db.mimeTypeForUrl(url); 0554 } 0555 } 0556 0557 /////// 0558 0559 KFileItem::KFileItem() 0560 : d(nullptr) 0561 { 0562 } 0563 0564 KFileItem::KFileItem(const KIO::UDSEntry &entry, const QUrl &itemOrDirUrl, bool delayedMimeTypes, bool urlIsDirectory) 0565 : d(new KFileItemPrivate(entry, 0566 KFileItem::Unknown, 0567 KFileItem::Unknown, 0568 itemOrDirUrl, 0569 urlIsDirectory, 0570 delayedMimeTypes, 0571 KFileItem::NormalMimeTypeDetermination)) 0572 { 0573 } 0574 0575 #if KIOCORE_BUILD_DEPRECATED_SINCE(5, 0) 0576 KFileItem::KFileItem(mode_t mode, mode_t permissions, const QUrl &url, bool delayedMimeTypes) 0577 : d(new KFileItemPrivate(KIO::UDSEntry(), mode, permissions, url, false, delayedMimeTypes, KFileItem::NormalMimeTypeDetermination)) 0578 { 0579 } 0580 #endif 0581 0582 KFileItem::KFileItem(const QUrl &url, const QString &mimeType, mode_t mode) 0583 : d(new KFileItemPrivate(KIO::UDSEntry(), mode, KFileItem::Unknown, url, false, false, KFileItem::NormalMimeTypeDetermination)) 0584 { 0585 d->m_bMimeTypeKnown = !mimeType.simplified().isEmpty(); 0586 if (d->m_bMimeTypeKnown) { 0587 QMimeDatabase db; 0588 d->m_mimeType = db.mimeTypeForName(mimeType); 0589 } 0590 } 0591 0592 KFileItem::KFileItem(const QUrl &url, KFileItem::MimeTypeDetermination mimeTypeDetermination) 0593 : d(new KFileItemPrivate(KIO::UDSEntry(), KFileItem::Unknown, KFileItem::Unknown, url, false, false, mimeTypeDetermination)) 0594 { 0595 } 0596 0597 // Default implementations for: 0598 // - Copy constructor 0599 // - Move constructor 0600 // - Copy assignment 0601 // - Move assignment 0602 // - Destructor 0603 // The compiler will now generate the content of those. 0604 KFileItem::KFileItem(const KFileItem &) = default; 0605 KFileItem::~KFileItem() = default; 0606 KFileItem::KFileItem(KFileItem &&) = default; 0607 KFileItem &KFileItem::operator=(const KFileItem &) = default; 0608 KFileItem &KFileItem::operator=(KFileItem &&) = default; 0609 0610 void KFileItem::refresh() 0611 { 0612 if (!d) { 0613 qCWarning(KIO_CORE) << "null item"; 0614 return; 0615 } 0616 0617 d->m_fileMode = KFileItem::Unknown; 0618 d->m_permissions = KFileItem::Unknown; 0619 d->m_hidden = KFileItemPrivate::Auto; 0620 refreshMimeType(); 0621 0622 #if HAVE_POSIX_ACL 0623 // If the item had ACL, re-add them in init() 0624 d->m_addACL = !d->m_entry.stringValue(KIO::UDSEntry::UDS_ACL_STRING).isEmpty(); 0625 #endif 0626 0627 // Basically, we can't trust any information we got while listing. 0628 // Everything could have changed... 0629 // Clearing m_entry makes it possible to detect changes in the size of the file, 0630 // the time information, etc. 0631 d->m_entry.clear(); 0632 d->init(); // re-populates d->m_entry 0633 } 0634 0635 void KFileItem::refreshMimeType() 0636 { 0637 if (!d) { 0638 return; 0639 } 0640 0641 d->m_mimeType = QMimeType(); 0642 d->m_bMimeTypeKnown = false; 0643 d->m_iconName.clear(); 0644 } 0645 0646 void KFileItem::setDelayedMimeTypes(bool b) 0647 { 0648 if (!d) { 0649 return; 0650 } 0651 d->m_delayedMimeTypes = b; 0652 } 0653 0654 void KFileItem::setUrl(const QUrl &url) 0655 { 0656 if (!d) { 0657 qCWarning(KIO_CORE) << "null item"; 0658 return; 0659 } 0660 0661 d->m_url = url; 0662 setName(url.fileName()); 0663 } 0664 0665 void KFileItem::setLocalPath(const QString &path) 0666 { 0667 if (!d) { 0668 qCWarning(KIO_CORE) << "null item"; 0669 return; 0670 } 0671 0672 d->m_entry.replace(KIO::UDSEntry::UDS_LOCAL_PATH, path); 0673 } 0674 0675 void KFileItem::setName(const QString &name) 0676 { 0677 if (!d) { 0678 qCWarning(KIO_CORE) << "null item"; 0679 return; 0680 } 0681 0682 d->ensureInitialized(); 0683 0684 d->m_strName = name; 0685 if (!d->m_strName.isEmpty()) { 0686 d->m_strText = KIO::decodeFileName(d->m_strName); 0687 } 0688 if (d->m_entry.contains(KIO::UDSEntry::UDS_NAME)) { 0689 d->m_entry.replace(KIO::UDSEntry::UDS_NAME, d->m_strName); // #195385 0690 } 0691 } 0692 0693 QString KFileItem::linkDest() const 0694 { 0695 if (!d) { 0696 return QString(); 0697 } 0698 0699 d->ensureInitialized(); 0700 0701 // Extract it from the KIO::UDSEntry 0702 const QString linkStr = d->m_entry.stringValue(KIO::UDSEntry::UDS_LINK_DEST); 0703 if (!linkStr.isEmpty()) { 0704 return linkStr; 0705 } 0706 0707 // If not in the KIO::UDSEntry, or if UDSEntry empty, use readlink() [if local URL] 0708 if (d->m_bIsLocalUrl) { 0709 return QFile::symLinkTarget(d->m_url.adjusted(QUrl::StripTrailingSlash).toLocalFile()); 0710 } 0711 return QString(); 0712 } 0713 0714 QString KFileItemPrivate::localPath() const 0715 { 0716 if (m_bIsLocalUrl) { 0717 return m_url.toLocalFile(); 0718 } 0719 0720 ensureInitialized(); 0721 0722 // Extract the local path from the KIO::UDSEntry 0723 return m_entry.stringValue(KIO::UDSEntry::UDS_LOCAL_PATH); 0724 } 0725 0726 QString KFileItem::localPath() const 0727 { 0728 if (!d) { 0729 return QString(); 0730 } 0731 0732 return d->localPath(); 0733 } 0734 0735 KIO::filesize_t KFileItem::size() const 0736 { 0737 if (!d) { 0738 return 0; 0739 } 0740 0741 return d->size(); 0742 } 0743 0744 KIO::filesize_t KFileItem::recursiveSize() const 0745 { 0746 if (!d) { 0747 return 0; 0748 } 0749 0750 return d->recursiveSize(); 0751 } 0752 0753 bool KFileItem::hasExtendedACL() const 0754 { 0755 if (!d) { 0756 return false; 0757 } 0758 0759 // Check if the field exists; its value doesn't matter 0760 return entry().contains(KIO::UDSEntry::UDS_EXTENDED_ACL); 0761 } 0762 0763 KACL KFileItem::ACL() const 0764 { 0765 if (!d) { 0766 return KACL(); 0767 } 0768 0769 if (hasExtendedACL()) { 0770 // Extract it from the KIO::UDSEntry 0771 const QString fieldVal = d->m_entry.stringValue(KIO::UDSEntry::UDS_ACL_STRING); 0772 if (!fieldVal.isEmpty()) { 0773 return KACL(fieldVal); 0774 } 0775 } 0776 0777 // create one from the basic permissions 0778 return KACL(d->m_permissions); 0779 } 0780 0781 KACL KFileItem::defaultACL() const 0782 { 0783 if (!d) { 0784 return KACL(); 0785 } 0786 0787 // Extract it from the KIO::UDSEntry 0788 const QString fieldVal = entry().stringValue(KIO::UDSEntry::UDS_DEFAULT_ACL_STRING); 0789 if (!fieldVal.isEmpty()) { 0790 return KACL(fieldVal); 0791 } else { 0792 return KACL(); 0793 } 0794 } 0795 0796 QDateTime KFileItem::time(FileTimes which) const 0797 { 0798 if (!d) { 0799 return QDateTime(); 0800 } 0801 0802 return d->time(which); 0803 } 0804 0805 QString KFileItem::user() const 0806 { 0807 if (!d) { 0808 return QString(); 0809 } 0810 0811 return entry().stringValue(KIO::UDSEntry::UDS_USER); 0812 } 0813 0814 QString KFileItem::group() const 0815 { 0816 if (!d) { 0817 return QString(); 0818 } 0819 0820 return entry().stringValue(KIO::UDSEntry::UDS_GROUP); 0821 } 0822 0823 bool KFileItemPrivate::isSlow() const 0824 { 0825 if (m_slow == SlowUnknown) { 0826 const QString path = localPath(); 0827 if (!path.isEmpty()) { 0828 const KFileSystemType::Type fsType = KFileSystemType::fileSystemType(path); 0829 m_slow = (fsType == KFileSystemType::Nfs || fsType == KFileSystemType::Smb) ? Slow : Fast; 0830 } else { 0831 m_slow = Slow; 0832 } 0833 } 0834 return m_slow == Slow; 0835 } 0836 0837 bool KFileItem::isSlow() const 0838 { 0839 if (!d) { 0840 return false; 0841 } 0842 0843 return d->isSlow(); 0844 } 0845 0846 QString KFileItem::mimetype() const 0847 { 0848 if (!d) { 0849 return QString(); 0850 } 0851 0852 KFileItem *that = const_cast<KFileItem *>(this); 0853 return that->determineMimeType().name(); 0854 } 0855 0856 QMimeType KFileItem::determineMimeType() const 0857 { 0858 if (!d) { 0859 return QMimeType(); 0860 } 0861 0862 if (!d->m_mimeType.isValid() || !d->m_bMimeTypeKnown) { 0863 QMimeDatabase db; 0864 if (isDir()) { 0865 d->m_mimeType = db.mimeTypeForName(QStringLiteral("inode/directory")); 0866 } else { 0867 const auto [url, isLocalUrl] = isMostLocalUrl(); 0868 d->determineMimeTypeHelper(url); 0869 0870 // was: d->m_mimeType = KMimeType::findByUrl( url, d->m_fileMode, isLocalUrl ); 0871 // => we are no longer using d->m_fileMode for remote URLs. 0872 Q_ASSERT(d->m_mimeType.isValid()); 0873 // qDebug() << d << "finding final MIME type for" << url << ":" << d->m_mimeType.name(); 0874 } 0875 d->m_bMimeTypeKnown = true; 0876 } 0877 0878 if (d->m_delayedMimeTypes) { // if we delayed getting the iconName up till now, this is the right point in time to do so 0879 d->m_delayedMimeTypes = false; 0880 d->m_useIconNameCache = false; 0881 (void)iconName(); 0882 } 0883 0884 return d->m_mimeType; 0885 } 0886 0887 bool KFileItem::isMimeTypeKnown() const 0888 { 0889 if (!d) { 0890 return false; 0891 } 0892 0893 // The MIME type isn't known if determineMimeType was never called (on-demand determination) 0894 // or if this fileitem has a guessed MIME type (e.g. ftp symlink) - in which case 0895 // it always remains "not fully determined" 0896 return d->m_bMimeTypeKnown && d->m_guessedMimeType.isEmpty(); 0897 } 0898 0899 static bool isDirectoryMounted(const QUrl &url) 0900 { 0901 // Stating .directory files can cause long freezes when e.g. /home 0902 // uses autofs for every user's home directory, i.e. opening /home 0903 // in a file dialog will mount every single home directory. 0904 // These non-mounted directories can be identified by having 0 size. 0905 // There are also other directories with 0 size, such as /proc, that may 0906 // be mounted, but those are unlikely to contain .directory (and checking 0907 // this would require checking with KMountPoint). 0908 0909 // TODO: maybe this could be checked with KFileSystemType instead? 0910 QFileInfo info(url.toLocalFile()); 0911 if (info.isDir() && info.size() == 0) { 0912 return false; 0913 } 0914 return true; 0915 } 0916 0917 bool KFileItem::isFinalIconKnown() const 0918 { 0919 if (!d) { 0920 return false; 0921 } 0922 return d->m_bMimeTypeKnown && (!d->m_delayedMimeTypes); 0923 } 0924 0925 // KDE5 TODO: merge with comment()? Need to see what lxr says about the usage of both. 0926 QString KFileItem::mimeComment() const 0927 { 0928 if (!d) { 0929 return QString(); 0930 } 0931 0932 const QString displayType = d->m_entry.stringValue(KIO::UDSEntry::UDS_DISPLAY_TYPE); 0933 if (!displayType.isEmpty()) { 0934 return displayType; 0935 } 0936 0937 const auto [url, isLocalUrl] = isMostLocalUrl(); 0938 0939 QMimeType mime = currentMimeType(); 0940 // This cannot move to kio_file (with UDS_DISPLAY_TYPE) because it needs 0941 // the MIME type to be determined, which is done here, and possibly delayed... 0942 if (isLocalUrl && !d->isSlow() && mime.inherits(QStringLiteral("application/x-desktop"))) { 0943 KDesktopFile cfg(url.toLocalFile()); 0944 QString comment = cfg.desktopGroup().readEntry("Comment"); 0945 if (!comment.isEmpty()) { 0946 return comment; 0947 } 0948 } 0949 0950 // Support for .directory file in directories 0951 if (isLocalUrl && isDir() && !d->isSlow() && isDirectoryMounted(url)) { 0952 QUrl u(url); 0953 u.setPath(Utils::concatPaths(u.path(), QStringLiteral(".directory"))); 0954 const KDesktopFile cfg(u.toLocalFile()); 0955 const QString comment = cfg.readComment(); 0956 if (!comment.isEmpty()) { 0957 return comment; 0958 } 0959 } 0960 0961 const QString comment = mime.comment(); 0962 // qDebug() << "finding comment for " << url.url() << " : " << d->m_mimeType->name(); 0963 if (!comment.isEmpty()) { 0964 return comment; 0965 } else { 0966 return mime.name(); 0967 } 0968 } 0969 0970 static QString iconFromDirectoryFile(const QString &path) 0971 { 0972 const QString filePath = path + QLatin1String("/.directory"); 0973 if (!QFileInfo(filePath).isFile()) { // exists -and- is a file 0974 return QString(); 0975 } 0976 0977 KDesktopFile cfg(filePath); 0978 QString icon = cfg.readIcon(); 0979 0980 const KConfigGroup group = cfg.desktopGroup(); 0981 const QString emptyIcon = group.readEntry("EmptyIcon"); 0982 if (!emptyIcon.isEmpty()) { 0983 bool isDirEmpty = true; 0984 QDirIterator dirIt(path, QDir::Dirs | QDir::Files | QDir::NoDotAndDotDot); 0985 while (dirIt.hasNext()) { 0986 dirIt.next(); 0987 if (dirIt.fileName() != QLatin1String(".directory")) { 0988 isDirEmpty = false; 0989 break; 0990 } 0991 } 0992 if (isDirEmpty) { 0993 icon = emptyIcon; 0994 } 0995 } 0996 0997 if (icon.startsWith(QLatin1String("./"))) { 0998 // path is relative with respect to the location of the .directory file (#73463) 0999 return path + QStringView(icon).mid(1); 1000 } 1001 return icon; 1002 } 1003 1004 static QString iconFromDesktopFile(const QString &path) 1005 { 1006 KDesktopFile cfg(path); 1007 const QString icon = cfg.readIcon(); 1008 if (cfg.hasLinkType()) { 1009 const KConfigGroup group = cfg.desktopGroup(); 1010 const QString emptyIcon = group.readEntry("EmptyIcon"); 1011 if (!emptyIcon.isEmpty()) { 1012 const QString u = cfg.readUrl(); 1013 const QUrl url(u); 1014 if (url.scheme() == QLatin1String("trash")) { 1015 // We need to find if the trash is empty, preferably without using a KIO job. 1016 // So instead kio_trash leaves an entry in its config file for us. 1017 KConfig trashConfig(QStringLiteral("trashrc"), KConfig::SimpleConfig); 1018 if (trashConfig.group("Status").readEntry("Empty", true)) { 1019 return emptyIcon; 1020 } 1021 } 1022 } 1023 } 1024 return icon; 1025 } 1026 1027 QString KFileItem::iconName() const 1028 { 1029 if (!d) { 1030 return QString(); 1031 } 1032 1033 if (d->m_useIconNameCache && !d->m_iconName.isEmpty()) { 1034 return d->m_iconName; 1035 } 1036 1037 d->m_iconName = d->m_entry.stringValue(KIO::UDSEntry::UDS_ICON_NAME); 1038 if (!d->m_iconName.isEmpty()) { 1039 d->m_useIconNameCache = d->m_bMimeTypeKnown; 1040 return d->m_iconName; 1041 } 1042 1043 const auto [url, isLocalUrl] = isMostLocalUrl(); 1044 1045 QMimeDatabase db; 1046 QMimeType mime; 1047 // Use guessed MIME type for the icon 1048 if (!d->m_guessedMimeType.isEmpty()) { 1049 mime = db.mimeTypeForName(d->m_guessedMimeType); 1050 } else { 1051 mime = currentMimeType(); 1052 } 1053 1054 const bool delaySlowOperations = d->m_delayedMimeTypes; 1055 1056 if (isLocalUrl && !delaySlowOperations) { 1057 const QString &localFile = url.toLocalFile(); 1058 1059 if (mime.inherits(QStringLiteral("application/x-desktop"))) { 1060 d->m_iconName = iconFromDesktopFile(localFile); 1061 if (!d->m_iconName.isEmpty()) { 1062 d->m_useIconNameCache = d->m_bMimeTypeKnown; 1063 return d->m_iconName; 1064 } 1065 } 1066 1067 if (isDir()) { 1068 if (isDirectoryMounted(url)) { 1069 d->m_iconName = iconFromDirectoryFile(localFile); 1070 if (!d->m_iconName.isEmpty()) { 1071 d->m_useIconNameCache = d->m_bMimeTypeKnown; 1072 return d->m_iconName; 1073 } 1074 } 1075 1076 d->m_iconName = KIOPrivate::iconForStandardPath(localFile); 1077 if (!d->m_iconName.isEmpty()) { 1078 d->m_useIconNameCache = d->m_bMimeTypeKnown; 1079 return d->m_iconName; 1080 } 1081 } 1082 } 1083 1084 d->m_iconName = mime.iconName(); 1085 d->m_useIconNameCache = d->m_bMimeTypeKnown; 1086 return d->m_iconName; 1087 } 1088 1089 /** 1090 * Returns true if this is a desktop file. 1091 * MIME type determination is optional. 1092 */ 1093 static bool checkDesktopFile(const KFileItem &item, bool _determineMimeType) 1094 { 1095 // Only local files 1096 if (!item.isMostLocalUrl().local) { 1097 return false; 1098 } 1099 1100 // only regular files 1101 if (!item.isRegularFile()) { 1102 return false; 1103 } 1104 1105 // only if readable 1106 if (!item.isReadable()) { 1107 return false; 1108 } 1109 1110 // return true if desktop file 1111 QMimeType mime = _determineMimeType ? item.determineMimeType() : item.currentMimeType(); 1112 return mime.inherits(QStringLiteral("application/x-desktop")); 1113 } 1114 1115 QStringList KFileItem::overlays() const 1116 { 1117 if (!d) { 1118 return QStringList(); 1119 } 1120 1121 d->ensureInitialized(); 1122 1123 QStringList names = d->m_entry.stringValue(KIO::UDSEntry::UDS_ICON_OVERLAY_NAMES).split(QLatin1Char(','), Qt::SkipEmptyParts); 1124 1125 if (d->m_bLink) { 1126 names.append(QStringLiteral("emblem-symbolic-link")); 1127 } 1128 1129 if (!isReadable()) { 1130 names.append(QStringLiteral("emblem-locked")); 1131 } 1132 1133 if (checkDesktopFile(*this, false)) { 1134 KDesktopFile cfg(localPath()); 1135 const KConfigGroup group = cfg.desktopGroup(); 1136 1137 // Add a warning emblem if this is an executable desktop file 1138 // which is untrusted. 1139 if (group.hasKey("Exec") && !KDesktopFile::isAuthorizedDesktopFile(localPath())) { 1140 names.append(QStringLiteral("emblem-important")); 1141 } 1142 1143 #if KIOCORE_BUILD_DEPRECATED_SINCE(5, 82) 1144 if (cfg.hasDeviceType()) { 1145 QT_WARNING_PUSH 1146 QT_WARNING_DISABLE_DEPRECATED 1147 const QString dev = cfg.readDevice(); 1148 QT_WARNING_POP 1149 if (!dev.isEmpty()) { 1150 KMountPoint::Ptr mountPoint = KMountPoint::currentMountPoints().findByDevice(dev); 1151 if (mountPoint) { // mounted? 1152 names.append(QStringLiteral("emblem-mounted")); 1153 } 1154 } 1155 } 1156 #endif 1157 } 1158 1159 if (isHidden()) { 1160 names.append(QStringLiteral("hidden")); 1161 } 1162 #ifndef Q_OS_WIN 1163 if (isDir()) { 1164 const auto [url, isLocalUrl] = isMostLocalUrl(); 1165 if (isLocalUrl) { 1166 const QString path = url.toLocalFile(); 1167 if (KSambaShare::instance()->isDirectoryShared(path) || KNFSShare::instance()->isDirectoryShared(path)) { 1168 names.append(QStringLiteral("emblem-shared")); 1169 } 1170 } 1171 } 1172 #endif // Q_OS_WIN 1173 1174 return names; 1175 } 1176 1177 QString KFileItem::comment() const 1178 { 1179 if (!d) { 1180 return QString(); 1181 } 1182 1183 return d->m_entry.stringValue(KIO::UDSEntry::UDS_COMMENT); 1184 } 1185 1186 bool KFileItem::isReadable() const 1187 { 1188 if (!d) { 1189 return false; 1190 } 1191 1192 d->ensureInitialized(); 1193 1194 /* 1195 struct passwd * user = getpwuid( geteuid() ); 1196 bool isMyFile = (QString::fromLocal8Bit(user->pw_name) == d->m_user); 1197 // This gets ugly for the group.... 1198 // Maybe we want a static QString for the user and a static QStringList 1199 // for the groups... then we need to handle the deletion properly... 1200 */ 1201 1202 if (d->m_permissions != KFileItem::Unknown) { 1203 const mode_t readMask = S_IRUSR | S_IRGRP | S_IROTH; 1204 // No read permission at all 1205 if ((d->m_permissions & readMask) == 0) { 1206 return false; 1207 } 1208 1209 // Read permissions for all: save a stat call 1210 if ((d->m_permissions & readMask) == readMask) { 1211 return true; 1212 } 1213 } 1214 1215 // Or if we can't read it - not network transparent 1216 if (d->m_bIsLocalUrl && !QFileInfo(d->m_url.toLocalFile()).isReadable()) { 1217 return false; 1218 } 1219 1220 return true; 1221 } 1222 1223 bool KFileItem::isWritable() const 1224 { 1225 if (!d) { 1226 return false; 1227 } 1228 1229 d->ensureInitialized(); 1230 1231 /* 1232 struct passwd * user = getpwuid( geteuid() ); 1233 bool isMyFile = (QString::fromLocal8Bit(user->pw_name) == d->m_user); 1234 // This gets ugly for the group.... 1235 // Maybe we want a static QString for the user and a static QStringList 1236 // for the groups... then we need to handle the deletion properly... 1237 */ 1238 1239 if (d->m_permissions != KFileItem::Unknown) { 1240 // No write permission at all 1241 if (!(S_IWUSR & d->m_permissions) && !(S_IWGRP & d->m_permissions) && !(S_IWOTH & d->m_permissions)) { 1242 return false; 1243 } 1244 } 1245 1246 // Or if we can't write it - not network transparent 1247 if (d->m_bIsLocalUrl) { 1248 return QFileInfo(d->m_url.toLocalFile()).isWritable(); 1249 } else { 1250 return KProtocolManager::supportsWriting(d->m_url); 1251 } 1252 } 1253 1254 bool KFileItem::isHidden() const 1255 { 1256 if (!d) { 1257 return false; 1258 } 1259 1260 // The KIO worker can specify explicitly that a file is hidden or shown 1261 if (d->m_hidden != KFileItemPrivate::Auto) { 1262 return d->m_hidden == KFileItemPrivate::Hidden; 1263 } 1264 1265 // Prefer the filename that is part of the URL, in case the display name is different. 1266 QString fileName = d->m_url.fileName(); 1267 if (fileName.isEmpty()) { // e.g. "trash:/" 1268 fileName = d->m_strName; 1269 } 1270 return fileName.length() > 1 && fileName[0] == QLatin1Char('.'); // Just "." is current directory, not hidden. 1271 } 1272 1273 void KFileItem::setHidden() 1274 { 1275 if (d) { 1276 d->m_hidden = KFileItemPrivate::Hidden; 1277 } 1278 } 1279 1280 bool KFileItem::isDir() const 1281 { 1282 if (!d) { 1283 return false; 1284 } 1285 1286 if (d->m_bMimeTypeKnown && d->m_mimeType.isValid()) { 1287 return d->m_mimeType.inherits(QStringLiteral("inode/directory")); 1288 } 1289 1290 if (d->m_bSkipMimeTypeFromContent) { 1291 return false; 1292 } 1293 1294 d->ensureInitialized(); 1295 1296 if (d->m_fileMode == KFileItem::Unknown) { 1297 // Probably the file was deleted already, and KDirLister hasn't told the world yet. 1298 // qDebug() << d << url() << "can't say -> false"; 1299 return false; // can't say for sure, so no 1300 } 1301 return Utils::isDirMask(d->m_fileMode); 1302 } 1303 1304 bool KFileItem::isFile() const 1305 { 1306 if (!d) { 1307 return false; 1308 } 1309 1310 return !isDir(); 1311 } 1312 1313 #if KIOCORE_BUILD_DEPRECATED_SINCE(4, 0) 1314 bool KFileItem::acceptsDrops() const 1315 { 1316 // A directory ? 1317 if (isDir()) { 1318 return isWritable(); 1319 } 1320 1321 // But only local .desktop files and executables 1322 if (!d->m_bIsLocalUrl) { 1323 return false; 1324 } 1325 1326 if (mimetype() == QLatin1String("application/x-desktop")) { 1327 return true; 1328 } 1329 1330 // Executable, shell script ... ? 1331 if (QFileInfo(d->m_url.toLocalFile()).isExecutable()) { 1332 return true; 1333 } 1334 1335 return false; 1336 } 1337 #endif 1338 1339 QString KFileItem::getStatusBarInfo() const 1340 { 1341 if (!d) { 1342 return QString(); 1343 } 1344 1345 auto toDisplayUrl = [this](const QUrl &url) { 1346 QString dest; 1347 if (url.isLocalFile()) { 1348 dest = KShell::tildeCollapse(url.toLocalFile()); 1349 } else { 1350 dest = targetUrl().toDisplayString(); 1351 } 1352 return dest; 1353 }; 1354 1355 QString text = d->m_strText; 1356 const QString comment = mimeComment(); 1357 1358 if (d->m_bLink) { 1359 text += QLatin1Char(' '); 1360 QString linkText = toDisplayUrl(QUrl::fromUserInput(linkDest())); 1361 if (comment.isEmpty()) { 1362 text += i18n("(Symbolic Link to %1)", linkText); 1363 } else { 1364 text += i18n("(%1, Link to %2)", comment, linkText); 1365 } 1366 } else if (targetUrl() != url()) { 1367 text += i18n(" (Points to %1)", toDisplayUrl(targetUrl())); 1368 } else if (Utils::isRegFileMask(d->m_fileMode)) { 1369 text += QStringLiteral(" (%1, %2)").arg(comment, KIO::convertSize(size())); 1370 } else { 1371 text += QStringLiteral(" (%1)").arg(comment); 1372 } 1373 return text; 1374 } 1375 1376 bool KFileItem::cmp(const KFileItem &item) const 1377 { 1378 if (!d && !item.d) { 1379 return true; 1380 } 1381 1382 if (!d || !item.d) { 1383 return false; 1384 } 1385 1386 return d->cmp(*item.d); 1387 } 1388 1389 bool KFileItem::operator==(const KFileItem &other) const 1390 { 1391 if (!d && !other.d) { 1392 return true; 1393 } 1394 1395 if (!d || !other.d) { 1396 return false; 1397 } 1398 1399 return d->m_url == other.d->m_url; 1400 } 1401 1402 bool KFileItem::operator!=(const KFileItem &other) const 1403 { 1404 return !operator==(other); 1405 } 1406 1407 bool KFileItem::operator<(const KFileItem &other) const 1408 { 1409 if (!other.d) { 1410 return false; 1411 } 1412 if (!d) { 1413 return other.d->m_url.isValid(); 1414 } 1415 return d->m_url < other.d->m_url; 1416 } 1417 1418 bool KFileItem::operator<(const QUrl &other) const 1419 { 1420 if (!d) { 1421 return other.isValid(); 1422 } 1423 return d->m_url < other; 1424 } 1425 1426 KFileItem::operator QVariant() const 1427 { 1428 return QVariant::fromValue(*this); 1429 } 1430 1431 QString KFileItem::permissionsString() const 1432 { 1433 if (!d) { 1434 return QString(); 1435 } 1436 1437 d->ensureInitialized(); 1438 1439 if (d->m_access.isNull() && d->m_permissions != KFileItem::Unknown) { 1440 d->m_access = d->parsePermissions(d->m_permissions); 1441 } 1442 1443 return d->m_access; 1444 } 1445 1446 // check if we need to cache this 1447 QString KFileItem::timeString(FileTimes which) const 1448 { 1449 if (!d) { 1450 return QString(); 1451 } 1452 1453 return QLocale::system().toString(d->time(which), QLocale::LongFormat); 1454 } 1455 1456 #if KIOCORE_BUILD_DEPRECATED_SINCE(4, 0) 1457 QString KFileItem::timeString(unsigned int which) const 1458 { 1459 if (!d) { 1460 return QString(); 1461 } 1462 1463 switch (which) { 1464 case KIO::UDSEntry::UDS_ACCESS_TIME: 1465 return timeString(AccessTime); 1466 case KIO::UDSEntry::UDS_CREATION_TIME: 1467 return timeString(CreationTime); 1468 case KIO::UDSEntry::UDS_MODIFICATION_TIME: 1469 default: 1470 return timeString(ModificationTime); 1471 } 1472 } 1473 #endif 1474 1475 #if KIOCORE_BUILD_DEPRECATED_SINCE(4, 0) 1476 void KFileItem::assign(const KFileItem &item) 1477 { 1478 *this = item; 1479 } 1480 #endif 1481 1482 QUrl KFileItem::mostLocalUrl(bool *local) const 1483 { 1484 if (!d) { 1485 return {}; 1486 } 1487 1488 const auto [url, isLocal] = isMostLocalUrl(); 1489 if (local) { 1490 *local = isLocal; 1491 } 1492 return url; 1493 } 1494 1495 KFileItem::MostLocalUrlResult KFileItem::isMostLocalUrl() const 1496 { 1497 if (!d) { 1498 return {QUrl(), false}; 1499 } 1500 1501 const QString local_path = localPath(); 1502 if (!local_path.isEmpty()) { 1503 return {QUrl::fromLocalFile(local_path), true}; 1504 } else { 1505 return {d->m_url, d->m_bIsLocalUrl}; 1506 } 1507 } 1508 1509 QDataStream &operator<<(QDataStream &s, const KFileItem &a) 1510 { 1511 if (a.d) { 1512 // We don't need to save/restore anything that refresh() invalidates, 1513 // since that means we can re-determine those by ourselves. 1514 s << a.d->m_url; 1515 s << a.d->m_strName; 1516 s << a.d->m_strText; 1517 } else { 1518 s << QUrl(); 1519 s << QString(); 1520 s << QString(); 1521 } 1522 1523 return s; 1524 } 1525 1526 QDataStream &operator>>(QDataStream &s, KFileItem &a) 1527 { 1528 QUrl url; 1529 QString strName; 1530 QString strText; 1531 1532 s >> url; 1533 s >> strName; 1534 s >> strText; 1535 1536 if (!a.d) { 1537 qCWarning(KIO_CORE) << "null item"; 1538 return s; 1539 } 1540 1541 if (url.isEmpty()) { 1542 a.d = nullptr; 1543 return s; 1544 } 1545 1546 a.d->m_url = url; 1547 a.d->m_strName = strName; 1548 a.d->m_strText = strText; 1549 a.d->m_bIsLocalUrl = a.d->m_url.isLocalFile(); 1550 a.d->m_bMimeTypeKnown = false; 1551 a.refresh(); 1552 1553 return s; 1554 } 1555 1556 QUrl KFileItem::url() const 1557 { 1558 if (!d) { 1559 return QUrl(); 1560 } 1561 1562 return d->m_url; 1563 } 1564 1565 mode_t KFileItem::permissions() const 1566 { 1567 if (!d) { 1568 return 0; 1569 } 1570 1571 d->ensureInitialized(); 1572 1573 return d->m_permissions; 1574 } 1575 1576 mode_t KFileItem::mode() const 1577 { 1578 if (!d) { 1579 return 0; 1580 } 1581 1582 d->ensureInitialized(); 1583 1584 return d->m_fileMode; 1585 } 1586 1587 bool KFileItem::isLink() const 1588 { 1589 if (!d) { 1590 return false; 1591 } 1592 1593 d->ensureInitialized(); 1594 1595 return d->m_bLink; 1596 } 1597 1598 bool KFileItem::isLocalFile() const 1599 { 1600 if (!d) { 1601 return false; 1602 } 1603 1604 return d->m_bIsLocalUrl; 1605 } 1606 1607 QString KFileItem::text() const 1608 { 1609 if (!d) { 1610 return QString(); 1611 } 1612 1613 return d->m_strText; 1614 } 1615 1616 QString KFileItem::name(bool lowerCase) const 1617 { 1618 if (!d) { 1619 return QString(); 1620 } 1621 1622 if (!lowerCase) { 1623 return d->m_strName; 1624 } else if (d->m_strLowerCaseName.isNull()) { 1625 d->m_strLowerCaseName = d->m_strName.toLower(); 1626 } 1627 return d->m_strLowerCaseName; 1628 } 1629 1630 QUrl KFileItem::targetUrl() const 1631 { 1632 if (!d) { 1633 return QUrl(); 1634 } 1635 1636 const QString targetUrlStr = d->m_entry.stringValue(KIO::UDSEntry::UDS_TARGET_URL); 1637 if (!targetUrlStr.isEmpty()) { 1638 return QUrl(targetUrlStr); 1639 } else { 1640 return url(); 1641 } 1642 } 1643 1644 /* 1645 * MIME type handling. 1646 * 1647 * Initial state: m_mimeType = QMimeType(). 1648 * When currentMimeType() is called first: fast MIME type determination, 1649 * might either find an accurate MIME type (-> Final state), otherwise we 1650 * set m_mimeType but not m_bMimeTypeKnown (-> Intermediate state) 1651 * Intermediate state: determineMimeType() does the real determination -> Final state. 1652 * 1653 * If delayedMimeTypes isn't set, then we always go to the Final state directly. 1654 */ 1655 1656 QMimeType KFileItem::currentMimeType() const 1657 { 1658 if (!d || d->m_url.isEmpty()) { 1659 return QMimeType(); 1660 } 1661 1662 if (!d->m_mimeType.isValid()) { 1663 // On-demand fast (but not always accurate) MIME type determination 1664 QMimeDatabase db; 1665 if (isDir()) { 1666 d->m_mimeType = db.mimeTypeForName(QStringLiteral("inode/directory")); 1667 return d->m_mimeType; 1668 } 1669 const QUrl url = mostLocalUrl(); 1670 if (d->m_delayedMimeTypes) { 1671 const QList<QMimeType> mimeTypes = db.mimeTypesForFileName(url.path()); 1672 if (mimeTypes.isEmpty()) { 1673 d->m_mimeType = db.mimeTypeForName(QStringLiteral("application/octet-stream")); 1674 d->m_bMimeTypeKnown = false; 1675 } else { 1676 d->m_mimeType = mimeTypes.first(); 1677 // If there were conflicting globs. determineMimeType will be able to do better. 1678 d->m_bMimeTypeKnown = (mimeTypes.count() == 1); 1679 } 1680 } else { 1681 // ## d->m_fileMode isn't used anymore (for remote urls) 1682 d->determineMimeTypeHelper(url); 1683 d->m_bMimeTypeKnown = true; 1684 } 1685 } 1686 return d->m_mimeType; 1687 } 1688 1689 KIO::UDSEntry KFileItem::entry() const 1690 { 1691 if (!d) { 1692 return KIO::UDSEntry(); 1693 } 1694 1695 d->ensureInitialized(); 1696 1697 return d->m_entry; 1698 } 1699 1700 bool KFileItem::isNull() const 1701 { 1702 return d == nullptr; 1703 } 1704 1705 KFileItemList::KFileItemList() 1706 { 1707 } 1708 1709 KFileItemList::KFileItemList(const QList<KFileItem> &items) 1710 : QList<KFileItem>(items) 1711 { 1712 } 1713 1714 KFileItemList::KFileItemList(std::initializer_list<KFileItem> items) 1715 : QList<KFileItem>(items) 1716 { 1717 } 1718 1719 KFileItem KFileItemList::findByName(const QString &fileName) const 1720 { 1721 auto it = std::find_if(cbegin(), cend(), [&fileName](const KFileItem &item) { 1722 return item.name() == fileName; 1723 }); 1724 1725 return it != cend() ? *it : KFileItem(); 1726 } 1727 1728 KFileItem KFileItemList::findByUrl(const QUrl &url) const 1729 { 1730 auto it = std::find_if(cbegin(), cend(), [&url](const KFileItem &item) { 1731 return item.url() == url; 1732 }); 1733 1734 return it != cend() ? *it : KFileItem(); 1735 } 1736 1737 QList<QUrl> KFileItemList::urlList() const 1738 { 1739 QList<QUrl> lst; 1740 lst.reserve(size()); 1741 1742 for (const auto &item : *this) { 1743 lst.append(item.url()); 1744 } 1745 return lst; 1746 } 1747 1748 QList<QUrl> KFileItemList::targetUrlList() const 1749 { 1750 QList<QUrl> lst; 1751 lst.reserve(size()); 1752 1753 for (const auto &item : *this) { 1754 lst.append(item.targetUrl()); 1755 } 1756 return lst; 1757 } 1758 1759 bool KFileItem::isDesktopFile() const 1760 { 1761 return checkDesktopFile(*this, true); 1762 } 1763 1764 bool KFileItem::isRegularFile() const 1765 { 1766 if (!d) { 1767 return false; 1768 } 1769 1770 d->ensureInitialized(); 1771 1772 return Utils::isRegFileMask(d->m_fileMode); 1773 } 1774 1775 QDebug operator<<(QDebug stream, const KFileItem &item) 1776 { 1777 QDebugStateSaver saver(stream); 1778 stream.nospace(); 1779 if (item.isNull()) { 1780 stream << "[null KFileItem]"; 1781 } else { 1782 stream << "[KFileItem for " << item.url() << "]"; 1783 } 1784 return stream; 1785 } 1786 1787 #include "moc_kfileitem.cpp"