KIO

kfileitem.cpp
1/*
2 This file is part of the KDE project
3 SPDX-FileCopyrightText: 1999-2011 David Faure <faure@kde.org>
4 SPDX-FileCopyrightText: 2001 Carsten Pfeiffer <pfeiffer@kde.org>
5
6 SPDX-License-Identifier: LGPL-2.0-or-later
7*/
8
9#include "kfileitem.h"
10
11#include "../kioworkers/file/stat_unix.h"
12#include "config-kiocore.h"
13
14#if HAVE_POSIX_ACL
15#include "../aclhelpers_p.h"
16#endif
17
18#include "../utils_p.h"
19#include "kiocoredebug.h"
20#include "kioglobal_p.h"
21
22#include <QDataStream>
23#include <QDate>
24#include <QDebug>
25#include <QDir>
26#include <QDirIterator>
27#include <QLocale>
28#include <QMimeDatabase>
29
30#include <KConfigGroup>
31#include <KDesktopFile>
32#include <KLocalizedString>
33#include <kmountpoint.h>
34#ifndef Q_OS_WIN
35#include <knfsshare.h>
36#include <ksambashare.h>
37#endif
38#include <KFileSystemType>
39#include <KProtocolManager>
40#include <KShell>
41
42#define KFILEITEM_DEBUG 0
43
44class KFileItemPrivate : public QSharedData
45{
46public:
47 KFileItemPrivate(const KIO::UDSEntry &entry,
48 mode_t mode,
49 mode_t permissions,
50 const QUrl &itemOrDirUrl,
51 bool urlIsDirectory,
52 bool delayedMimeTypes,
53 KFileItem::MimeTypeDetermination mimeTypeDetermination)
54 : m_entry(entry)
55 , m_url(itemOrDirUrl)
56 , m_strName()
57 , m_strText()
58 , m_iconName()
59 , m_strLowerCaseName()
60 , m_mimeType()
61 , m_fileMode(mode)
62 , m_permissions(permissions)
63 , m_addACL(false)
64 , m_bLink(false)
65 , m_bIsLocalUrl(itemOrDirUrl.isLocalFile())
66 , m_bMimeTypeKnown(false)
67 , m_delayedMimeTypes(delayedMimeTypes)
68 , m_useIconNameCache(false)
69 , m_hidden(Auto)
70 , m_slow(SlowUnknown)
71 , m_bSkipMimeTypeFromContent(mimeTypeDetermination == KFileItem::SkipMimeTypeFromContent)
72 , m_bInitCalled(false)
73 {
74 if (entry.count() != 0) {
75 readUDSEntry(urlIsDirectory);
76 } else {
77 Q_ASSERT(!urlIsDirectory);
78 m_strName = itemOrDirUrl.fileName();
79 m_strText = KIO::decodeFileName(m_strName);
80 }
81 }
82
83 /**
84 * Call init() if not yet done.
85 */
86 void ensureInitialized() const;
87
88 /**
89 * Computes the text and mode from the UDSEntry.
90 */
91 void init() const;
92
93 QString localPath() const;
94 KIO::filesize_t size() const;
95 KIO::filesize_t recursiveSize() const;
96 QDateTime time(KFileItem::FileTimes which) const;
97 void setTime(KFileItem::FileTimes which, uint time_t_val) const;
98 void setTime(KFileItem::FileTimes which, const QDateTime &val) const;
99 bool cmp(const KFileItemPrivate &item) const;
100 void printCompareDebug(const KFileItemPrivate &item) const;
101 bool isSlow() const;
102
103 /**
104 * Extracts the data from the UDSEntry member and updates the KFileItem
105 * accordingly.
106 */
107 void readUDSEntry(bool _urlIsDirectory);
108
109 /**
110 * Parses the given permission set and provides it for access()
111 */
112 QString parsePermissions(mode_t perm) const;
113
114 /**
115 * Mime type helper
116 */
117 void determineMimeTypeHelper(const QUrl &url) const;
118
119 /**
120 * The UDSEntry that contains the data for this fileitem, if it came from a directory listing.
121 */
122 mutable KIO::UDSEntry m_entry;
123 /**
124 * The url of the file
125 */
126 QUrl m_url;
127
128 /**
129 * The text for this item, i.e. the file name without path,
130 */
131 QString m_strName;
132
133 /**
134 * The text for this item, i.e. the file name without path, decoded
135 * ('%%' becomes '%', '%2F' becomes '/')
136 */
137 QString m_strText;
138
139 /**
140 * The icon name for this item.
141 */
142 mutable QString m_iconName;
143
144 /**
145 * The filename in lower case (to speed up sorting)
146 */
147 mutable QString m_strLowerCaseName;
148
149 /**
150 * The MIME type of the file
151 */
152 mutable QMimeType m_mimeType;
153
154 /**
155 * The file mode
156 */
157 mutable mode_t m_fileMode;
158 /**
159 * The permissions
160 */
161 mutable mode_t m_permissions;
162
163 /**
164 * Whether the UDSEntry ACL fields should be added to m_entry.
165 */
166 mutable bool m_addACL : 1;
167
168 /**
169 * Whether the file is a link
170 */
171 mutable bool m_bLink : 1;
172 /**
173 * True if local file
174 */
175 bool m_bIsLocalUrl : 1;
176
177 mutable bool m_bMimeTypeKnown : 1;
178 mutable bool m_delayedMimeTypes : 1;
179
180 /** True if m_iconName should be used as cache. */
181 mutable bool m_useIconNameCache : 1;
182
183 // Auto: check leading dot.
184 enum { Auto, Hidden, Shown } m_hidden : 3;
185
186 // Slow? (nfs/smb/ssh)
187 mutable enum { SlowUnknown, Fast, Slow } m_slow : 3;
188
189 /**
190 * True if MIME type determination by content should be skipped
191 */
192 bool m_bSkipMimeTypeFromContent : 1;
193
194 /**
195 * True if init() was called on demand
196 */
197 mutable bool m_bInitCalled : 1;
198
199 // For special case like link to dirs over FTP
200 QString m_guessedMimeType;
201 mutable QString m_access;
202};
203
204void KFileItemPrivate::ensureInitialized() const
205{
206 if (!m_bInitCalled) {
207 init();
208 }
209}
210
211void KFileItemPrivate::init() const
212{
213 m_access.clear();
214 // metaInfo = KFileMetaInfo();
215
216 // stat() local files if needed
217 const bool shouldStat = (m_fileMode == KFileItem::Unknown || m_permissions == KFileItem::Unknown || m_entry.count() == 0) && m_url.isLocalFile();
218 if (shouldStat) {
219 /* directories may not have a slash at the end if we want to stat()
220 * them; it requires that we change into it .. which may not be allowed
221 * stat("/is/unaccessible") -> rwx------
222 * stat("/is/unaccessible/") -> EPERM H.Z.
223 * This is the reason for the StripTrailingSlash
224 */
225
226#if HAVE_STATX
227 // statx syscall is available
228 struct statx buff;
229#else
230 QT_STATBUF buff;
231#endif
233 const QByteArray pathBA = QFile::encodeName(path);
234 if (LSTAT(pathBA.constData(), &buff, KIO::StatDefaultDetails) == 0) {
235 m_entry.reserve(9);
236 m_entry.replace(KIO::UDSEntry::UDS_DEVICE_ID, stat_dev(buff));
237 m_entry.replace(KIO::UDSEntry::UDS_INODE, stat_ino(buff));
238
239 mode_t mode = stat_mode(buff);
240 if (Utils::isLinkMask(mode)) {
241 m_bLink = true;
242 if (STAT(pathBA.constData(), &buff, KIO::StatDefaultDetails) == 0) {
243 mode = stat_mode(buff);
244 } else { // link pointing to nowhere (see FileProtocol::createUDSEntry() in kioworkers/file/file.cpp)
245 mode = (QT_STAT_MASK - 1) | S_IRWXU | S_IRWXG | S_IRWXO;
246 }
247 }
248
249 const mode_t type = mode & QT_STAT_MASK;
250
251 m_entry.replace(KIO::UDSEntry::UDS_SIZE, stat_size(buff));
252 m_entry.replace(KIO::UDSEntry::UDS_FILE_TYPE, type); // extract file type
253 m_entry.replace(KIO::UDSEntry::UDS_ACCESS, mode & 07777); // extract permissions
254 m_entry.replace(KIO::UDSEntry::UDS_MODIFICATION_TIME, stat_mtime(buff)); // TODO: we could use msecs too...
255 m_entry.replace(KIO::UDSEntry::UDS_ACCESS_TIME, stat_atime(buff));
256#if HAVE_STATX
257 m_entry.replace(KIO::UDSEntry::UDS_CREATION_TIME, buff.stx_btime.tv_sec);
258#endif
259
260#ifndef Q_OS_WIN
261 const auto uid = stat_uid(buff);
262 const auto gid = stat_gid(buff);
265#endif
266
267 // TODO: these can be removed, we can use UDS_FILE_TYPE and UDS_ACCESS everywhere
268 if (m_fileMode == KFileItem::Unknown) {
269 m_fileMode = type; // extract file type
270 }
271 if (m_permissions == KFileItem::Unknown) {
272 m_permissions = mode & 07777; // extract permissions
273 }
274
275#if HAVE_POSIX_ACL
276 if (m_addACL) {
277 appendACLAtoms(pathBA, m_entry, type);
278 }
279#endif
280 } else {
281 if (errno != ENOENT) {
282 // another error
283 qCDebug(KIO_CORE) << QStringLiteral("KFileItem: error %1: %2").arg(errno).arg(QString::fromLatin1(strerror(errno))) << "when refreshing"
284 << m_url;
285 }
286 }
287 }
288
289 m_bInitCalled = true;
290}
291
292void KFileItemPrivate::readUDSEntry(bool _urlIsDirectory)
293{
294 // extract fields from the KIO::UDS Entry
295
296 m_fileMode = m_entry.numberValue(KIO::UDSEntry::UDS_FILE_TYPE, KFileItem::Unknown);
297 m_permissions = m_entry.numberValue(KIO::UDSEntry::UDS_ACCESS, KFileItem::Unknown);
298 m_strName = m_entry.stringValue(KIO::UDSEntry::UDS_NAME);
299
301 if (!displayName.isEmpty()) {
302 m_strText = displayName;
303 } else {
304 m_strText = KIO::decodeFileName(m_strName);
305 }
306
307 const QString urlStr = m_entry.stringValue(KIO::UDSEntry::UDS_URL);
308 const bool UDS_URL_seen = !urlStr.isEmpty();
309 if (UDS_URL_seen) {
310 m_url = QUrl(urlStr);
311 if (m_url.isLocalFile()) {
312 m_bIsLocalUrl = true;
313 }
314 }
315 QMimeDatabase db;
316 const QString mimeTypeStr = m_entry.stringValue(KIO::UDSEntry::UDS_MIME_TYPE);
317 m_bMimeTypeKnown = !mimeTypeStr.isEmpty();
318 if (m_bMimeTypeKnown) {
319 m_mimeType = db.mimeTypeForName(mimeTypeStr);
320 }
321
322 m_guessedMimeType = m_entry.stringValue(KIO::UDSEntry::UDS_GUESSED_MIME_TYPE);
323 m_bLink = !m_entry.stringValue(KIO::UDSEntry::UDS_LINK_DEST).isEmpty(); // we don't store the link dest
324
325 const int hiddenVal = m_entry.numberValue(KIO::UDSEntry::UDS_HIDDEN, -1);
326 m_hidden = hiddenVal == 1 ? Hidden : (hiddenVal == 0 ? Shown : Auto);
327
328 if (_urlIsDirectory && !UDS_URL_seen && !m_strName.isEmpty() && m_strName != QLatin1String(".")) {
329 auto path = m_url.path();
330 if (path.isEmpty()) {
331 // empty path means root dir, useful for protocols than redirect their root / to empty dir, i.e nfs
332 path = QStringLiteral("/");
333 }
334 m_url.setPath(Utils::concatPaths(path, m_strName));
335 }
336
337 m_iconName.clear();
338}
339
340// Inlined because it is used only in one place
341inline KIO::filesize_t KFileItemPrivate::size() const
342{
343 ensureInitialized();
344
345 // Extract it from the KIO::UDSEntry
346 long long fieldVal = m_entry.numberValue(KIO::UDSEntry::UDS_SIZE, -1);
347 if (fieldVal != -1) {
348 return fieldVal;
349 }
350
351 // If not in the KIO::UDSEntry, or if UDSEntry empty, use stat() [if local URL]
352 if (m_bIsLocalUrl) {
353 return QFileInfo(m_url.toLocalFile()).size();
354 }
355 return 0;
356}
357
358KIO::filesize_t KFileItemPrivate::recursiveSize() const
359{
360 // Extract it from the KIO::UDSEntry
361 long long fieldVal = m_entry.numberValue(KIO::UDSEntry::UDS_RECURSIVE_SIZE, -1);
362 if (fieldVal != -1) {
363 return static_cast<KIO::filesize_t>(fieldVal);
364 }
365
366 return 0;
367}
368
369static uint udsFieldForTime(KFileItem::FileTimes mappedWhich)
370{
371 switch (mappedWhich) {
372 case KFileItem::ModificationTime:
374 case KFileItem::AccessTime:
376 case KFileItem::CreationTime:
378 }
379 return 0;
380}
381
382void KFileItemPrivate::setTime(KFileItem::FileTimes mappedWhich, uint time_t_val) const
383{
384 m_entry.replace(udsFieldForTime(mappedWhich), time_t_val);
385}
386
387void KFileItemPrivate::setTime(KFileItem::FileTimes mappedWhich, const QDateTime &val) const
388{
389 const QDateTime dt = val.toLocalTime(); // #160979
390 setTime(mappedWhich, dt.toSecsSinceEpoch());
391}
392
393QDateTime KFileItemPrivate::time(KFileItem::FileTimes mappedWhich) const
394{
395 ensureInitialized();
396
397 // Extract it from the KIO::UDSEntry
398 const uint uds = udsFieldForTime(mappedWhich);
399 if (uds > 0) {
400 const long long fieldVal = m_entry.numberValue(uds, -1);
401 if (fieldVal != -1) {
402 return QDateTime::fromSecsSinceEpoch(fieldVal);
403 }
404 }
405
406 return QDateTime();
407}
408
409void KFileItemPrivate::printCompareDebug(const KFileItemPrivate &item) const
410{
411 Q_UNUSED(item);
412
413#if KFILEITEM_DEBUG
414 const KIO::UDSEntry &otherEntry = item.m_entry;
415
416 qDebug() << "Comparing" << m_url << "and" << item.m_url;
417 qDebug() << " name" << (m_strName == item.m_strName);
418 qDebug() << " local" << (m_bIsLocalUrl == item.m_bIsLocalUrl);
419
420 qDebug() << " mode" << (m_fileMode == item.m_fileMode);
421 qDebug() << " perm" << (m_permissions == item.m_permissions);
422 qDebug() << " group" << (m_entry.stringValue(KIO::UDSEntry::UDS_GROUP) == otherEntry.stringValue(KIO::UDSEntry::UDS_GROUP));
423 qDebug() << " user" << (m_entry.stringValue(KIO::UDSEntry::UDS_USER) == otherEntry.stringValue(KIO::UDSEntry::UDS_USER));
424
425 qDebug() << " UDS_EXTENDED_ACL" << (m_entry.stringValue(KIO::UDSEntry::UDS_EXTENDED_ACL) == otherEntry.stringValue(KIO::UDSEntry::UDS_EXTENDED_ACL));
426 qDebug() << " UDS_ACL_STRING" << (m_entry.stringValue(KIO::UDSEntry::UDS_ACL_STRING) == otherEntry.stringValue(KIO::UDSEntry::UDS_ACL_STRING));
427 qDebug() << " UDS_DEFAULT_ACL_STRING"
429
430 qDebug() << " m_bLink" << (m_bLink == item.m_bLink);
431 qDebug() << " m_hidden" << (m_hidden == item.m_hidden);
432
433 qDebug() << " size" << (size() == item.size());
434
435 qDebug() << " ModificationTime" << m_entry.numberValue(KIO::UDSEntry::UDS_MODIFICATION_TIME)
437
438 qDebug() << " UDS_ICON_NAME" << (m_entry.stringValue(KIO::UDSEntry::UDS_ICON_NAME) == otherEntry.stringValue(KIO::UDSEntry::UDS_ICON_NAME));
439#endif
440}
441
442// Inlined because it is used only in one place
443inline bool KFileItemPrivate::cmp(const KFileItemPrivate &item) const
444{
445 if (item.m_bInitCalled) {
446 ensureInitialized();
447 }
448
449 if (m_bInitCalled) {
450 item.ensureInitialized();
451 }
452
453#if KFILEITEM_DEBUG
454 printCompareDebug(item);
455#endif
456
457 /* clang-format off */
458 return (m_strName == item.m_strName
459 && m_bIsLocalUrl == item.m_bIsLocalUrl
460 && m_fileMode == item.m_fileMode
461 && m_permissions == item.m_permissions
467 && m_bLink == item.m_bLink
468 && m_hidden == item.m_hidden
469 && size() == item.size()
474 /* clang-format on */
475 // Don't compare the MIME types here. They might not be known, and we don't want to
476 // do the slow operation of determining them here.
477}
478
479// Inlined because it is used only in one place
480inline QString KFileItemPrivate::parsePermissions(mode_t perm) const
481{
482 ensureInitialized();
483
484 static char buffer[12];
485
486 char uxbit;
487 char gxbit;
488 char oxbit;
489
490 if ((perm & (S_IXUSR | S_ISUID)) == (S_IXUSR | S_ISUID)) {
491 uxbit = 's';
492 } else if ((perm & (S_IXUSR | S_ISUID)) == S_ISUID) {
493 uxbit = 'S';
494 } else if ((perm & (S_IXUSR | S_ISUID)) == S_IXUSR) {
495 uxbit = 'x';
496 } else {
497 uxbit = '-';
498 }
499
500 if ((perm & (S_IXGRP | S_ISGID)) == (S_IXGRP | S_ISGID)) {
501 gxbit = 's';
502 } else if ((perm & (S_IXGRP | S_ISGID)) == S_ISGID) {
503 gxbit = 'S';
504 } else if ((perm & (S_IXGRP | S_ISGID)) == S_IXGRP) {
505 gxbit = 'x';
506 } else {
507 gxbit = '-';
508 }
509
510 if ((perm & (S_IXOTH | S_ISVTX)) == (S_IXOTH | S_ISVTX)) {
511 oxbit = 't';
512 } else if ((perm & (S_IXOTH | S_ISVTX)) == S_ISVTX) {
513 oxbit = 'T';
514 } else if ((perm & (S_IXOTH | S_ISVTX)) == S_IXOTH) {
515 oxbit = 'x';
516 } else {
517 oxbit = '-';
518 }
519
520 // Include the type in the first char like ls does; people are more used to seeing it,
521 // even though it's not really part of the permissions per se.
522 if (m_bLink) {
523 buffer[0] = 'l';
524 } else if (m_fileMode != KFileItem::Unknown) {
525 if (Utils::isDirMask(m_fileMode)) {
526 buffer[0] = 'd';
527 }
528#ifdef Q_OS_UNIX
529 else if (S_ISSOCK(m_fileMode)) {
530 buffer[0] = 's';
531 } else if (S_ISCHR(m_fileMode)) {
532 buffer[0] = 'c';
533 } else if (S_ISBLK(m_fileMode)) {
534 buffer[0] = 'b';
535 } else if (S_ISFIFO(m_fileMode)) {
536 buffer[0] = 'p';
537 }
538#endif // Q_OS_UNIX
539 else {
540 buffer[0] = '-';
541 }
542 } else {
543 buffer[0] = '-';
544 }
545
546 buffer[1] = (((perm & S_IRUSR) == S_IRUSR) ? 'r' : '-');
547 buffer[2] = (((perm & S_IWUSR) == S_IWUSR) ? 'w' : '-');
548 buffer[3] = uxbit;
549 buffer[4] = (((perm & S_IRGRP) == S_IRGRP) ? 'r' : '-');
550 buffer[5] = (((perm & S_IWGRP) == S_IWGRP) ? 'w' : '-');
551 buffer[6] = gxbit;
552 buffer[7] = (((perm & S_IROTH) == S_IROTH) ? 'r' : '-');
553 buffer[8] = (((perm & S_IWOTH) == S_IWOTH) ? 'w' : '-');
554 buffer[9] = oxbit;
555 // if (hasExtendedACL())
557 buffer[10] = '+';
558 buffer[11] = 0;
559 } else {
560 buffer[10] = 0;
561 }
562
563 return QString::fromLatin1(buffer);
564}
565
566void KFileItemPrivate::determineMimeTypeHelper(const QUrl &url) const
567{
568 QMimeDatabase db;
569 if (m_bSkipMimeTypeFromContent || isSlow()) {
570 const QString scheme = url.scheme();
571 if (scheme.startsWith(QLatin1String("http")) || scheme == QLatin1String("mailto")) {
572 m_mimeType = db.mimeTypeForName(QLatin1String("application/octet-stream"));
573 } else {
574 m_mimeType = db.mimeTypeForFile(url.path(), QMimeDatabase::MatchMode::MatchExtension);
575 }
576 } else {
577 m_mimeType = db.mimeTypeForUrl(url);
578 }
579}
580
581///////
582
584 : d(nullptr)
585{
586}
587
588KFileItem::KFileItem(const KIO::UDSEntry &entry, const QUrl &itemOrDirUrl, bool delayedMimeTypes, bool urlIsDirectory)
589 : d(new KFileItemPrivate(entry,
590 KFileItem::Unknown,
591 KFileItem::Unknown,
592 itemOrDirUrl,
593 urlIsDirectory,
594 delayedMimeTypes,
595 KFileItem::NormalMimeTypeDetermination))
596{
597}
598
599KFileItem::KFileItem(const QUrl &url, const QString &mimeType, mode_t mode)
600 : d(new KFileItemPrivate(KIO::UDSEntry(), mode, KFileItem::Unknown, url, false, false, KFileItem::NormalMimeTypeDetermination))
601{
602 d->m_bMimeTypeKnown = !mimeType.simplified().isEmpty();
603 if (d->m_bMimeTypeKnown) {
604 QMimeDatabase db;
605 d->m_mimeType = db.mimeTypeForName(mimeType);
606 }
607}
608
609KFileItem::KFileItem(const QUrl &url, KFileItem::MimeTypeDetermination mimeTypeDetermination)
610 : d(new KFileItemPrivate(KIO::UDSEntry(), KFileItem::Unknown, KFileItem::Unknown, url, false, false, mimeTypeDetermination))
611{
612}
613
614// Default implementations for:
615// - Copy constructor
616// - Move constructor
617// - Copy assignment
618// - Move assignment
619// - Destructor
620// The compiler will now generate the content of those.
621KFileItem::KFileItem(const KFileItem &) = default;
622KFileItem::~KFileItem() = default;
623KFileItem::KFileItem(KFileItem &&) = default;
624KFileItem &KFileItem::operator=(const KFileItem &) = default;
626
628{
629 if (!d) {
630 qCWarning(KIO_CORE) << "null item";
631 return;
632 }
633
634 d->m_fileMode = KFileItem::Unknown;
635 d->m_permissions = KFileItem::Unknown;
636 d->m_hidden = KFileItemPrivate::Auto;
638
639#if HAVE_POSIX_ACL
640 // If the item had ACL, re-add them in init()
641 d->m_addACL = !d->m_entry.stringValue(KIO::UDSEntry::UDS_ACL_STRING).isEmpty();
642#endif
643
644 // Basically, we can't trust any information we got while listing.
645 // Everything could have changed...
646 // Clearing m_entry makes it possible to detect changes in the size of the file,
647 // the time information, etc.
648 d->m_entry.clear();
649 d->init(); // re-populates d->m_entry
650}
651
653{
654 if (!d) {
655 return;
656 }
657
658 d->m_mimeType = QMimeType();
659 d->m_bMimeTypeKnown = false;
660 d->m_iconName.clear();
661}
662
664{
665 if (!d) {
666 return;
667 }
668 d->m_delayedMimeTypes = b;
669}
670
671void KFileItem::setUrl(const QUrl &url)
672{
673 if (!d) {
674 qCWarning(KIO_CORE) << "null item";
675 return;
676 }
677
678 d->m_url = url;
679 setName(url.fileName());
680}
681
683{
684 if (!d) {
685 qCWarning(KIO_CORE) << "null item";
686 return;
687 }
688
689 d->m_entry.replace(KIO::UDSEntry::UDS_LOCAL_PATH, path);
690}
691
693{
694 if (!d) {
695 qCWarning(KIO_CORE) << "null item";
696 return;
697 }
698
699 d->ensureInitialized();
700
701 d->m_strName = name;
702 if (!d->m_strName.isEmpty()) {
703 d->m_strText = KIO::decodeFileName(d->m_strName);
704 }
705 if (d->m_entry.contains(KIO::UDSEntry::UDS_NAME)) {
706 d->m_entry.replace(KIO::UDSEntry::UDS_NAME, d->m_strName); // #195385
707 }
708}
709
710QString KFileItem::linkDest() const
711{
712 if (!d) {
713 return QString();
714 }
715
716 d->ensureInitialized();
717
718 // Extract it from the KIO::UDSEntry
719 const QString linkStr = d->m_entry.stringValue(KIO::UDSEntry::UDS_LINK_DEST);
720 if (!linkStr.isEmpty()) {
721 return linkStr;
722 }
723
724 // If not in the KIO::UDSEntry, or if UDSEntry empty, use readlink() [if local URL]
725 if (d->m_bIsLocalUrl) {
726 return QFile::symLinkTarget(d->m_url.adjusted(QUrl::StripTrailingSlash).toLocalFile());
727 }
728 return QString();
729}
730
731QString KFileItemPrivate::localPath() const
732{
733 if (m_bIsLocalUrl) {
734 return m_url.toLocalFile();
735 }
736
737 ensureInitialized();
738
739 // Extract the local path from the KIO::UDSEntry
741}
742
743QString KFileItem::localPath() const
744{
745 if (!d) {
746 return QString();
747 }
748
749 return d->localPath();
750}
751
753{
754 if (!d) {
755 return 0;
756 }
757
758 return d->size();
759}
760
762{
763 if (!d) {
764 return 0;
765 }
766
767 return d->recursiveSize();
768}
769
771{
772 if (!d) {
773 return false;
774 }
775
776 // Check if the field exists; its value doesn't matter
778}
779
781{
782 if (!d) {
783 return KACL();
784 }
785
786 if (hasExtendedACL()) {
787 // Extract it from the KIO::UDSEntry
788 const QString fieldVal = d->m_entry.stringValue(KIO::UDSEntry::UDS_ACL_STRING);
789 if (!fieldVal.isEmpty()) {
790 return KACL(fieldVal);
791 }
792 }
793
794 // create one from the basic permissions
795 return KACL(d->m_permissions);
796}
797
799{
800 if (!d) {
801 return KACL();
802 }
803
804 // Extract it from the KIO::UDSEntry
806 if (!fieldVal.isEmpty()) {
807 return KACL(fieldVal);
808 } else {
809 return KACL();
810 }
811}
812
814{
815 if (!d) {
816 return QDateTime();
817 }
818
819 return d->time(which);
820}
821
822QString KFileItem::user() const
823{
824 if (!d) {
825 return QString();
826 }
827 if (entry().contains(KIO::UDSEntry::UDS_USER)) {
829 } else {
830#ifdef Q_OS_UNIX
832 if (uid != -1) {
833 return KUser(uid).loginName();
834 }
835#endif
836 }
837 return QString();
838}
839
841{
842 if (!d) {
843 return -1;
844 }
845
847}
848
849QString KFileItem::group() const
850{
851 if (!d) {
852 return QString();
853 }
854
855 if (entry().contains(KIO::UDSEntry::UDS_GROUP)) {
857 } else {
858#ifdef Q_OS_UNIX
860 if (gid != -1) {
861 return KUserGroup(gid).name();
862 }
863#endif
864 }
865 return QString();
866}
867
869{
870 if (!d) {
871 return -1;
872 }
873
875}
876
877bool KFileItemPrivate::isSlow() const
878{
879 if (m_slow == SlowUnknown) {
880 const QString path = localPath();
881 if (!path.isEmpty()) {
883 m_slow = (fsType == KFileSystemType::Nfs || fsType == KFileSystemType::Smb) ? Slow : Fast;
884 } else {
885 m_slow = Slow;
886 }
887 }
888 return m_slow == Slow;
889}
890
891bool KFileItem::isSlow() const
892{
893 if (!d) {
894 return false;
895 }
896
897 return d->isSlow();
898}
899
900QString KFileItem::mimetype() const
901{
902 if (!d) {
903 return QString();
904 }
905
906 KFileItem *that = const_cast<KFileItem *>(this);
907 return that->determineMimeType().name();
908}
909
910QMimeType KFileItem::determineMimeType() const
911{
912 if (!d) {
913 return QMimeType();
914 }
915
916 if (!d->m_mimeType.isValid() || !d->m_bMimeTypeKnown) {
917 QMimeDatabase db;
918 if (isDir()) {
919 d->m_mimeType = db.mimeTypeForName(QStringLiteral("inode/directory"));
920 } else {
921 const auto [url, isLocalUrl] = isMostLocalUrl();
922 d->determineMimeTypeHelper(url);
923
924 // was: d->m_mimeType = KMimeType::findByUrl( url, d->m_fileMode, isLocalUrl );
925 // => we are no longer using d->m_fileMode for remote URLs.
926 Q_ASSERT(d->m_mimeType.isValid());
927 // qDebug() << d << "finding final MIME type for" << url << ":" << d->m_mimeType.name();
928 }
929 d->m_bMimeTypeKnown = true;
930 }
931
932 if (d->m_delayedMimeTypes) { // if we delayed getting the iconName up till now, this is the right point in time to do so
933 d->m_delayedMimeTypes = false;
934 d->m_useIconNameCache = false;
935 (void)iconName();
936 }
937
938 return d->m_mimeType;
939}
940
941bool KFileItem::isMimeTypeKnown() const
942{
943 if (!d) {
944 return false;
945 }
946
947 // The MIME type isn't known if determineMimeType was never called (on-demand determination)
948 // or if this fileitem has a guessed MIME type (e.g. ftp symlink) - in which case
949 // it always remains "not fully determined"
950 return d->m_bMimeTypeKnown && d->m_guessedMimeType.isEmpty();
951}
952
953static bool isDirectoryMounted(const QUrl &url)
954{
955 // Stating .directory files can cause long freezes when e.g. /home
956 // uses autofs for every user's home directory, i.e. opening /home
957 // in a file dialog will mount every single home directory.
958 // These non-mounted directories can be identified by having 0 size.
959 // There are also other directories with 0 size, such as /proc, that may
960 // be mounted, but those are unlikely to contain .directory (and checking
961 // this would require checking with KMountPoint).
962
963 // TODO: maybe this could be checked with KFileSystemType instead?
964 QFileInfo info(url.toLocalFile());
965 if (info.isDir() && info.size() == 0) {
966 return false;
967 }
968 return true;
969}
970
971bool KFileItem::isFinalIconKnown() const
972{
973 if (!d) {
974 return false;
975 }
976 return d->m_bMimeTypeKnown && (!d->m_delayedMimeTypes);
977}
978
979// KDE5 TODO: merge with comment()? Need to see what lxr says about the usage of both.
980QString KFileItem::mimeComment() const
981{
982 if (!d) {
983 return QString();
984 }
985
986 const QString displayType = d->m_entry.stringValue(KIO::UDSEntry::UDS_DISPLAY_TYPE);
987 if (!displayType.isEmpty()) {
988 return displayType;
989 }
990
991 const auto [url, isLocalUrl] = isMostLocalUrl();
992
993 QMimeType mime = currentMimeType();
994 // This cannot move to kio_file (with UDS_DISPLAY_TYPE) because it needs
995 // the MIME type to be determined, which is done here, and possibly delayed...
996 if (isLocalUrl && !d->isSlow() && mime.inherits(QStringLiteral("application/x-desktop"))) {
997 KDesktopFile cfg(url.toLocalFile());
998 QString comment = cfg.desktopGroup().readEntry("Comment");
999 if (!comment.isEmpty()) {
1000 return comment;
1001 }
1002 }
1003
1004 // Support for .directory file in directories
1005 if (isLocalUrl && isDir() && !d->isSlow() && isDirectoryMounted(url)) {
1006 QUrl u(url);
1007 u.setPath(Utils::concatPaths(u.path(), QStringLiteral(".directory")));
1008 const KDesktopFile cfg(u.toLocalFile());
1009 const QString comment = cfg.readComment();
1010 if (!comment.isEmpty()) {
1011 return comment;
1012 }
1013 }
1014
1015 const QString comment = mime.comment();
1016 // qDebug() << "finding comment for " << url.url() << " : " << d->m_mimeType->name();
1017 if (!comment.isEmpty()) {
1018 return comment;
1019 } else {
1020 return mime.name();
1021 }
1022}
1023
1024static QString iconFromDirectoryFile(const QString &path)
1025{
1026 const QString filePath = path + QLatin1String("/.directory");
1027 if (!QFileInfo(filePath).isFile()) { // exists -and- is a file
1028 return QString();
1029 }
1030
1031 KDesktopFile cfg(filePath);
1032 QString icon = cfg.readIcon();
1033
1034 const KConfigGroup group = cfg.desktopGroup();
1035 const QString emptyIcon = group.readEntry("EmptyIcon");
1036 if (!emptyIcon.isEmpty()) {
1037 bool isDirEmpty = true;
1039 while (dirIt.hasNext()) {
1040 dirIt.next();
1041 if (dirIt.fileName() != QLatin1String(".directory")) {
1042 isDirEmpty = false;
1043 break;
1044 }
1045 }
1046 if (isDirEmpty) {
1047 icon = emptyIcon;
1048 }
1049 }
1050
1051 if (icon.startsWith(QLatin1String("./"))) {
1052 // path is relative with respect to the location of the .directory file (#73463)
1053 return path + QStringView(icon).mid(1);
1054 }
1055 return icon;
1056}
1057
1058static QString iconFromDesktopFile(const QString &path)
1059{
1060 KDesktopFile cfg(path);
1061 const QString icon = cfg.readIcon();
1062 if (cfg.hasLinkType()) {
1063 const KConfigGroup group = cfg.desktopGroup();
1064 const QString emptyIcon = group.readEntry("EmptyIcon");
1065 if (!emptyIcon.isEmpty()) {
1066 const QString u = cfg.readUrl();
1067 const QUrl url(u);
1068 if (url.scheme() == QLatin1String("trash")) {
1069 // We need to find if the trash is empty, preferably without using a KIO job.
1070 // So instead kio_trash leaves an entry in its config file for us.
1071 KConfig trashConfig(QStringLiteral("trashrc"), KConfig::SimpleConfig);
1072 if (trashConfig.group(QStringLiteral("Status")).readEntry("Empty", true)) {
1073 return emptyIcon;
1074 }
1075 }
1076 }
1077 }
1078 return icon;
1079}
1080
1081QString KFileItem::iconName() const
1082{
1083 if (!d) {
1084 return QString();
1085 }
1086
1087 if (d->m_useIconNameCache && !d->m_iconName.isEmpty()) {
1088 return d->m_iconName;
1089 }
1090
1091 d->m_iconName = d->m_entry.stringValue(KIO::UDSEntry::UDS_ICON_NAME);
1092 if (!d->m_iconName.isEmpty()) {
1093 d->m_useIconNameCache = d->m_bMimeTypeKnown;
1094 return d->m_iconName;
1095 }
1096
1097 const auto [url, isLocalUrl] = isMostLocalUrl();
1098
1099 QMimeDatabase db;
1100 QMimeType mime;
1101 // Use guessed MIME type for the icon
1102 if (!d->m_guessedMimeType.isEmpty()) {
1103 mime = db.mimeTypeForName(d->m_guessedMimeType);
1104 } else {
1105 mime = currentMimeType();
1106 }
1107
1108 const bool delaySlowOperations = d->m_delayedMimeTypes;
1109
1110 if (isLocalUrl && !delaySlowOperations) {
1111 const QString &localFile = url.toLocalFile();
1112
1113 if (mime.inherits(QStringLiteral("application/x-desktop"))) {
1114 d->m_iconName = iconFromDesktopFile(localFile);
1115 if (!d->m_iconName.isEmpty()) {
1116 d->m_useIconNameCache = d->m_bMimeTypeKnown;
1117 return d->m_iconName;
1118 }
1119 }
1120
1121 if (isDir()) {
1122 if (isDirectoryMounted(url)) {
1123 d->m_iconName = iconFromDirectoryFile(localFile);
1124 if (!d->m_iconName.isEmpty()) {
1125 d->m_useIconNameCache = d->m_bMimeTypeKnown;
1126 return d->m_iconName;
1127 }
1128 }
1129
1130 d->m_iconName = KIOPrivate::iconForStandardPath(localFile);
1131 if (!d->m_iconName.isEmpty()) {
1132 d->m_useIconNameCache = d->m_bMimeTypeKnown;
1133 return d->m_iconName;
1134 }
1135 }
1136 }
1137
1138 d->m_iconName = mime.iconName();
1139 d->m_useIconNameCache = d->m_bMimeTypeKnown;
1140 return d->m_iconName;
1141}
1142
1143/**
1144 * Returns true if this is a desktop file.
1145 * MIME type determination is optional.
1146 */
1147static bool checkDesktopFile(const KFileItem &item, bool _determineMimeType)
1148{
1149 // Only local files
1150 if (!item.isMostLocalUrl().local) {
1151 return false;
1152 }
1153
1154 // only regular files
1155 if (!item.isRegularFile()) {
1156 return false;
1157 }
1158
1159 // only if readable
1160 if (!item.isReadable()) {
1161 return false;
1162 }
1163
1164 // return true if desktop file
1165 QMimeType mime = _determineMimeType ? item.determineMimeType() : item.currentMimeType();
1166 return mime.inherits(QStringLiteral("application/x-desktop"));
1167}
1168
1169QStringList KFileItem::overlays() const
1170{
1171 if (!d) {
1172 return QStringList();
1173 }
1174
1175 d->ensureInitialized();
1176
1177 QStringList names = d->m_entry.stringValue(KIO::UDSEntry::UDS_ICON_OVERLAY_NAMES).split(QLatin1Char(','), Qt::SkipEmptyParts);
1178
1179 if (d->m_bLink) {
1180 names.append(QStringLiteral("emblem-symbolic-link"));
1181 }
1182
1183 if (!isReadable()) {
1184 names.append(QStringLiteral("emblem-locked"));
1185 }
1186
1187 if (checkDesktopFile(*this, false)) {
1188 KDesktopFile cfg(localPath());
1189 const KConfigGroup group = cfg.desktopGroup();
1190
1191 // Add a warning emblem if this is an executable desktop file
1192 // which is untrusted.
1193 if (group.hasKey("Exec") && !KDesktopFile::isAuthorizedDesktopFile(localPath())) {
1194 names.append(QStringLiteral("emblem-important"));
1195 }
1196 }
1197
1198 if (isHidden()) {
1199 names.append(QStringLiteral("hidden"));
1200 }
1201#ifndef Q_OS_WIN
1202 if (isDir()) {
1203 const auto [url, isLocalUrl] = isMostLocalUrl();
1204 if (isLocalUrl) {
1205 const QString path = url.toLocalFile();
1206 if (KSambaShare::instance()->isDirectoryShared(path) || KNFSShare::instance()->isDirectoryShared(path)) {
1207 names.append(QStringLiteral("emblem-shared"));
1208 }
1209 }
1210 }
1211#endif // Q_OS_WIN
1212
1213 return names;
1214}
1215
1216QString KFileItem::comment() const
1217{
1218 if (!d) {
1219 return QString();
1220 }
1221
1222 return d->m_entry.stringValue(KIO::UDSEntry::UDS_COMMENT);
1223}
1224
1225bool KFileItem::isReadable() const
1226{
1227 if (!d) {
1228 return false;
1229 }
1230
1231 d->ensureInitialized();
1232
1233 if (d->m_permissions != KFileItem::Unknown) {
1234 const mode_t readMask = S_IRUSR | S_IRGRP | S_IROTH;
1235 // No read permission at all
1236 if ((d->m_permissions & readMask) == 0) {
1237 return false;
1238 }
1239
1240 // Read permissions for all: save a stat call
1241 if ((d->m_permissions & readMask) == readMask) {
1242 return true;
1243 }
1244
1245#ifndef Q_OS_WIN
1246 const auto uidOfItem = userId();
1247 if (uidOfItem != -1) {
1248 const auto currentUser = KUserId::currentUserId();
1249 if (((uint)uidOfItem) == currentUser.nativeId()) {
1250 return S_IRUSR & d->m_permissions;
1251 }
1252 const auto gidOfItem = groupId();
1253 if (gidOfItem != -1) {
1254 if (KUser(currentUser).groups().contains(KUserGroup(gidOfItem))) {
1255 return S_IRGRP & d->m_permissions;
1256 }
1257
1258 return S_IROTH & d->m_permissions;
1259 }
1260 }
1261#else
1262 // simple special case
1263 return S_IRUSR & d->m_permissions;
1264#endif
1265 }
1266
1267 // Or if we can't read it - not network transparent
1268 if (d->m_bIsLocalUrl && !QFileInfo(d->m_url.toLocalFile()).isReadable()) {
1269 return false;
1270 }
1271
1272 return true;
1273}
1274
1275bool KFileItem::isWritable() const
1276{
1277 if (!d) {
1278 return false;
1279 }
1280
1281 d->ensureInitialized();
1282
1283 if (d->m_permissions != KFileItem::Unknown) {
1284 // No write permission at all
1285 if ((d->m_permissions & (S_IWUSR | S_IWGRP | S_IWOTH)) == 0) {
1286 return false;
1287 }
1288
1289#ifndef Q_OS_WIN
1290 const auto uidOfItem = userId();
1291 if (uidOfItem != -1) {
1292 const auto currentUser = KUserId::currentUserId();
1293 if (((uint)uidOfItem) == currentUser.nativeId()) {
1294 return S_IWUSR & d->m_permissions;
1295 }
1296 const auto gidOfItem = groupId();
1297 if (gidOfItem != -1) {
1298 if (KUser(currentUser).groups().contains(KUserGroup(gidOfItem))) {
1299 return S_IWGRP & d->m_permissions;
1300 }
1301
1302 if (S_IWOTH & d->m_permissions) {
1303 return true;
1304 }
1305 }
1306 }
1307#else
1308 // simple special case
1309 return S_IWUSR & d->m_permissions;
1310#endif
1311 }
1312
1313 // Or if we can't write it - not network transparent
1314 if (d->m_bIsLocalUrl) {
1315 return QFileInfo(d->m_url.toLocalFile()).isWritable();
1316 } else {
1317 return KProtocolManager::supportsWriting(d->m_url);
1318 }
1319}
1320
1321bool KFileItem::isHidden() const
1322{
1323 if (!d) {
1324 return false;
1325 }
1326
1327 // The KIO worker can specify explicitly that a file is hidden or shown
1328 if (d->m_hidden != KFileItemPrivate::Auto) {
1329 return d->m_hidden == KFileItemPrivate::Hidden;
1330 }
1331
1332 // Prefer the filename that is part of the URL, in case the display name is different.
1333 QString fileName = d->m_url.fileName();
1334 if (fileName.isEmpty()) { // e.g. "trash:/"
1335 fileName = d->m_strName;
1336 }
1337 return fileName.length() > 1 && fileName[0] == QLatin1Char('.'); // Just "." is current directory, not hidden.
1338}
1339
1340void KFileItem::setHidden()
1341{
1342 if (d) {
1343 d->m_hidden = KFileItemPrivate::Hidden;
1344 }
1345}
1346
1347bool KFileItem::isDir() const
1348{
1349 if (!d) {
1350 return false;
1351 }
1352
1353 if (d->m_fileMode != KFileItem::Unknown) {
1354 // File mode is known so we can use that.
1355 return Utils::isDirMask(d->m_fileMode);
1356 }
1357
1358 if (d->m_bMimeTypeKnown && d->m_mimeType.isValid()) {
1359 // File mode is not known but we do know the mime type, so use that to
1360 // avoid doing a stat.
1361 return d->m_mimeType.inherits(QStringLiteral("inode/directory"));
1362 }
1363
1364 if (d->m_bSkipMimeTypeFromContent) {
1365 return false;
1366 }
1367
1368 d->ensureInitialized();
1369
1370 if (d->m_fileMode == KFileItem::Unknown) {
1371 // Probably the file was deleted already, and KDirLister hasn't told the world yet.
1372 // qDebug() << d << url() << "can't say -> false";
1373 return false; // can't say for sure, so no
1374 }
1375 return Utils::isDirMask(d->m_fileMode);
1376}
1377
1378bool KFileItem::isFile() const
1379{
1380 if (!d) {
1381 return false;
1382 }
1383
1384 return !isDir();
1385}
1386
1387QString KFileItem::getStatusBarInfo() const
1388{
1389 if (!d) {
1390 return QString();
1391 }
1392
1393 auto toDisplayUrl = [](const QUrl &url) {
1394 QString dest;
1395 if (url.isLocalFile()) {
1396 dest = KShell::tildeCollapse(url.toLocalFile());
1397 } else {
1398 dest = url.toDisplayString();
1399 }
1400 return dest;
1401 };
1402
1403 QString text = d->m_strText;
1404 const QString comment = mimeComment();
1405
1406 if (d->m_bLink) {
1407 auto linkText = linkDest();
1408 if (!linkText.startsWith(QStringLiteral("anon_inode:"))) {
1409 linkText = toDisplayUrl(d->m_url.resolved(QUrl(linkText)));
1410 }
1411 text += QLatin1Char(' ');
1412 if (comment.isEmpty()) {
1413 text += i18n("(Symbolic Link to %1)", linkText);
1414 } else {
1415 text += i18n("(%1, Link to %2)", comment, linkText);
1416 }
1417 } else if (targetUrl() != url()) {
1418 text += i18n(" (Points to %1)", toDisplayUrl(targetUrl()));
1419 } else if (Utils::isRegFileMask(d->m_fileMode)) {
1420 text += QStringLiteral(" (%1, %2)").arg(comment, KIO::convertSize(size()));
1421 } else {
1422 text += QStringLiteral(" (%1)").arg(comment);
1423 }
1424 return text;
1425}
1426
1427bool KFileItem::cmp(const KFileItem &item) const
1428{
1429 if (!d && !item.d) {
1430 return true;
1431 }
1432
1433 if (!d || !item.d) {
1434 return false;
1435 }
1436
1437 return d->cmp(*item.d);
1438}
1439
1440bool KFileItem::operator==(const KFileItem &other) const
1441{
1442 if (!d && !other.d) {
1443 return true;
1444 }
1445
1446 if (!d || !other.d) {
1447 return false;
1448 }
1449
1450 return d->m_url == other.d->m_url;
1451}
1452
1453bool KFileItem::operator!=(const KFileItem &other) const
1454{
1455 return !operator==(other);
1456}
1457
1458bool KFileItem::operator<(const KFileItem &other) const
1459{
1460 if (!other.d) {
1461 return false;
1462 }
1463 if (!d) {
1464 return other.d->m_url.isValid();
1465 }
1466 return d->m_url < other.d->m_url;
1467}
1468
1469bool KFileItem::operator<(const QUrl &other) const
1470{
1471 if (!d) {
1472 return other.isValid();
1473 }
1474 return d->m_url < other;
1475}
1476
1477KFileItem::operator QVariant() const
1478{
1479 return QVariant::fromValue(*this);
1480}
1481
1483{
1484 if (!d) {
1485 return QString();
1486 }
1487
1488 d->ensureInitialized();
1489
1490 if (d->m_access.isNull() && d->m_permissions != KFileItem::Unknown) {
1491 d->m_access = d->parsePermissions(d->m_permissions);
1492 }
1493
1494 return d->m_access;
1495}
1496
1497// check if we need to cache this
1499{
1500 if (!d) {
1501 return QString();
1502 }
1503
1504 return QLocale::system().toString(d->time(which), QLocale::LongFormat);
1505}
1506
1508{
1509 if (!d) {
1510 return {};
1511 }
1512
1513 const auto [url, isLocal] = isMostLocalUrl();
1514 if (local) {
1515 *local = isLocal;
1516 }
1517 return url;
1518}
1519
1520KFileItem::MostLocalUrlResult KFileItem::isMostLocalUrl() const
1521{
1522 if (!d) {
1523 return {QUrl(), false};
1524 }
1525
1526 const QString local_path = localPath();
1527 if (!local_path.isEmpty()) {
1528 return {QUrl::fromLocalFile(local_path), true};
1529 } else {
1530 return {d->m_url, d->m_bIsLocalUrl};
1531 }
1532}
1533
1534QDataStream &operator<<(QDataStream &s, const KFileItem &a)
1535{
1536 if (a.d) {
1537 // We don't need to save/restore anything that refresh() invalidates,
1538 // since that means we can re-determine those by ourselves.
1539 s << a.d->m_url;
1540 s << a.d->m_strName;
1541 s << a.d->m_strText;
1542 } else {
1543 s << QUrl();
1544 s << QString();
1545 s << QString();
1546 }
1547
1548 return s;
1549}
1550
1552{
1553 QUrl url;
1554 QString strName;
1555 QString strText;
1556
1557 s >> url;
1558 s >> strName;
1559 s >> strText;
1560
1561 if (!a.d) {
1562 qCWarning(KIO_CORE) << "null item";
1563 return s;
1564 }
1565
1566 if (url.isEmpty()) {
1567 a.d = nullptr;
1568 return s;
1569 }
1570
1571 a.d->m_url = url;
1572 a.d->m_strName = strName;
1573 a.d->m_strText = strText;
1574 a.d->m_bIsLocalUrl = a.d->m_url.isLocalFile();
1575 a.d->m_bMimeTypeKnown = false;
1576 a.refresh();
1577
1578 return s;
1579}
1580
1581QUrl KFileItem::url() const
1582{
1583 if (!d) {
1584 return QUrl();
1585 }
1586
1587 return d->m_url;
1588}
1589
1591{
1592 if (!d) {
1593 return 0;
1594 }
1595
1596 d->ensureInitialized();
1597
1598 return d->m_permissions;
1599}
1600
1601mode_t KFileItem::mode() const
1602{
1603 if (!d) {
1604 return 0;
1605 }
1606
1607 d->ensureInitialized();
1608
1609 return d->m_fileMode;
1610}
1611
1612bool KFileItem::isLink() const
1613{
1614 if (!d) {
1615 return false;
1616 }
1617
1618 d->ensureInitialized();
1619
1620 return d->m_bLink;
1621}
1622
1623bool KFileItem::isLocalFile() const
1624{
1625 if (!d) {
1626 return false;
1627 }
1628
1629 return d->m_bIsLocalUrl;
1630}
1631
1632QString KFileItem::text() const
1633{
1634 if (!d) {
1635 return QString();
1636 }
1637
1638 return d->m_strText;
1639}
1640
1641QString KFileItem::name(bool lowerCase) const
1642{
1643 if (!d) {
1644 return QString();
1645 }
1646
1647 if (!lowerCase) {
1648 return d->m_strName;
1649 } else if (d->m_strLowerCaseName.isNull()) {
1650 d->m_strLowerCaseName = d->m_strName.toLower();
1651 }
1652 return d->m_strLowerCaseName;
1653}
1654
1655QUrl KFileItem::targetUrl() const
1656{
1657 if (!d) {
1658 return QUrl();
1659 }
1660
1661 const QString targetUrlStr = d->m_entry.stringValue(KIO::UDSEntry::UDS_TARGET_URL);
1662 if (!targetUrlStr.isEmpty()) {
1663 return QUrl(targetUrlStr);
1664 } else {
1665 return url();
1666 }
1667}
1668
1669/*
1670 * MIME type handling.
1671 *
1672 * Initial state: m_mimeType = QMimeType().
1673 * When currentMimeType() is called first: fast MIME type determination,
1674 * might either find an accurate MIME type (-> Final state), otherwise we
1675 * set m_mimeType but not m_bMimeTypeKnown (-> Intermediate state)
1676 * Intermediate state: determineMimeType() does the real determination -> Final state.
1677 *
1678 * If delayedMimeTypes isn't set, then we always go to the Final state directly.
1679 */
1680
1681QMimeType KFileItem::currentMimeType() const
1682{
1683 if (!d || d->m_url.isEmpty()) {
1684 return QMimeType();
1685 }
1686
1687 if (!d->m_mimeType.isValid()) {
1688 // On-demand fast (but not always accurate) MIME type determination
1689 QMimeDatabase db;
1690 if (isDir()) {
1691 d->m_mimeType = db.mimeTypeForName(QStringLiteral("inode/directory"));
1692 return d->m_mimeType;
1693 }
1694 const QUrl url = mostLocalUrl();
1695 if (d->m_delayedMimeTypes) {
1696 const QList<QMimeType> mimeTypes = db.mimeTypesForFileName(url.path());
1697 if (mimeTypes.isEmpty()) {
1698 d->m_mimeType = db.mimeTypeForName(QStringLiteral("application/octet-stream"));
1699 d->m_bMimeTypeKnown = false;
1700 } else {
1701 d->m_mimeType = mimeTypes.first();
1702 // If there were conflicting globs. determineMimeType will be able to do better.
1703 d->m_bMimeTypeKnown = (mimeTypes.count() == 1);
1704 }
1705 } else {
1706 // ## d->m_fileMode isn't used anymore (for remote urls)
1707 d->determineMimeTypeHelper(url);
1708 d->m_bMimeTypeKnown = true;
1709 }
1710 }
1711 return d->m_mimeType;
1712}
1713
1715{
1716 if (!d) {
1717 return KIO::UDSEntry();
1718 }
1719
1720 d->ensureInitialized();
1721
1722 return d->m_entry;
1723}
1724
1726{
1727 return d == nullptr;
1728}
1729
1731{
1732 if (!d) {
1733 return false;
1734 }
1735 if (!d->m_bInitCalled) {
1736 qCWarning(KIO_CORE) << "KFileItem: exists called when not initialised" << d->m_url;
1737 return false;
1738 }
1739 return d->m_fileMode != KFileItem::Unknown;
1740}
1741
1743{
1744 if (!d) {
1745 return false;
1746 }
1747
1748 d->ensureInitialized();
1749
1750 if (d->m_permissions == KFileItem::Unknown) {
1751 return false;
1752 }
1753
1754 const mode_t executableMask = S_IXGRP | S_IXUSR | S_IXOTH;
1755 if ((d->m_permissions & executableMask) == 0) {
1756 return false;
1757 }
1758
1759#ifndef Q_OS_WIN
1760 const auto uid = userId();
1761 if (uid != -1) {
1762 if (((uint)uid) == KUserId::currentUserId().nativeId()) {
1763 return S_IXUSR & d->m_permissions;
1764 }
1765 const auto gid = groupId();
1766 if (gid != -1) {
1767 const KUser kuser = KUser(uid);
1768 if (kuser.groups().contains(KUserGroup(gid))) {
1769 return S_IXGRP & d->m_permissions;
1770 }
1771
1772 return S_IXOTH & d->m_permissions;
1773 }
1774 }
1775 return false;
1776#else
1777 // simple special case
1778 return S_IXUSR & d->m_permissions;
1779#endif
1780}
1781
1785
1787 : QList<KFileItem>(items)
1788{
1789}
1790
1791KFileItemList::KFileItemList(std::initializer_list<KFileItem> items)
1792 : QList<KFileItem>(items)
1793{
1794}
1795
1797{
1798 auto it = std::find_if(cbegin(), cend(), [&fileName](const KFileItem &item) {
1799 return item.name() == fileName;
1800 });
1801
1802 return it != cend() ? *it : KFileItem();
1803}
1804
1806{
1807 auto it = std::find_if(cbegin(), cend(), [&url](const KFileItem &item) {
1808 return item.url() == url;
1809 });
1810
1811 return it != cend() ? *it : KFileItem();
1812}
1813
1815{
1816 QList<QUrl> lst;
1817 lst.reserve(size());
1818
1819 for (const auto &item : *this) {
1820 lst.append(item.url());
1821 }
1822 return lst;
1823}
1824
1826{
1827 QList<QUrl> lst;
1828 lst.reserve(size());
1829
1830 for (const auto &item : *this) {
1831 lst.append(item.targetUrl());
1832 }
1833 return lst;
1834}
1835
1836bool KFileItem::isDesktopFile() const
1837{
1838 return checkDesktopFile(*this, true);
1839}
1840
1841bool KFileItem::isRegularFile() const
1842{
1843 if (!d) {
1844 return false;
1845 }
1846
1847 d->ensureInitialized();
1848
1849 return Utils::isRegFileMask(d->m_fileMode);
1850}
1851
1853{
1854 if (!d || isDir()) {
1855 return QString();
1856 }
1857
1858 const int lastDot = d->m_strText.lastIndexOf(QStringLiteral("."));
1859 if (lastDot > 0) {
1860 return d->m_strText.mid(lastDot + 1);
1861 } else {
1862 return QString();
1863 }
1864}
1865
1866QDebug operator<<(QDebug stream, const KFileItem &item)
1867{
1868 QDebugStateSaver saver(stream);
1869 stream.nospace();
1870 if (item.isNull()) {
1871 stream << "[null KFileItem]";
1872 } else {
1873 stream << "[KFileItem for " << item.url() << "]";
1874 }
1875 return stream;
1876}
1877
1878#include "moc_kfileitem.cpp"
The KACL class encapsulates a POSIX Access Control List.
Definition kacl.h:38
bool hasKey(const char *key) const
QString readEntry(const char *key, const char *aDefault=nullptr) const
QString readComment() const
KConfigGroup desktopGroup() const
static bool isAuthorizedDesktopFile(const QString &path)
KFileItem findByUrl(const QUrl &url) const
Find a KFileItem by URL and return it.
KFileItem findByName(const QString &fileName) const
Find a KFileItem by name and return it.
KFileItemList()
Creates an empty list of file items.
QList< QUrl > targetUrlList() const
QList< QUrl > urlList() const
A KFileItem is a generic class to handle a file, local or remote.
Definition kfileitem.h:36
KFileItem & operator=(const KFileItem &)
Copy assignment.
int userId() const
Returns the file's owner's user id.
int groupId() const
Returns the file's owner's group id.
~KFileItem()
Destructor.
QUrl mostLocalUrl(bool *local=nullptr) const
Tries to return a local URL for this file item if possible.
bool operator==(const KFileItem &other) const
Returns true if both items share the same URL.
void setUrl(const QUrl &url)
Sets the item's URL.
bool operator!=(const KFileItem &other) const
Returns true if both items do not share the same URL.
KIO::filesize_t size() const
Returns the size of the file, if known.
Q_INVOKABLE QString timeString(KFileItem::FileTimes which=ModificationTime) const
Requests the modification, access or creation time as a string, depending on which.
FileTimes
The timestamps associated with a file.
Definition kfileitem.h:77
Q_INVOKABLE QDateTime time(KFileItem::FileTimes which) const
Requests the modification, access or creation time, depending on which.
bool cmp(const KFileItem &item) const
Somewhat like a comparison operator, but more explicit, and it can detect that two fileitems differ i...
bool hasExtendedACL() const
Tells if the file has extended access level information ( Posix ACL )
KACL defaultACL() const
Returns the default access control list for the directory.
mode_t permissions() const
Returns the permissions of the file (stat.st_mode containing only permissions).
KIO::filesize_t recursiveSize() const
For folders, its recursive size: the size of its files plus the recursiveSize of its folder.
KFileItem()
Null KFileItem.
KACL ACL() const
Returns the access control list for the file.
void refreshMimeType()
Re-reads MIME type information.
MostLocalUrlResult isMostLocalUrl() const
Returns a MostLocalUrlResult, with the local Url for this item if possible (otherwise an empty Url),...
bool exists() const
returns whether the KFileItem exists on-disk Call only after initialization (i.e KIO::stat or refresh...
bool isNull() const
Return true if default-constructed.
KIO::UDSEntry entry() const
Returns the UDS entry.
bool isExecutable() const
Return true if the file has executable permission.
QString suffix() const
Returns the file extension Similar to QFileInfo::suffix except it takes into account UDS_DISPLAY_NAME...
mode_t mode() const
Returns the file type (stat.st_mode containing only S_IFDIR, S_IFLNK, ...).
QString permissionsString() const
Returns the access permissions for the file as a string.
void setLocalPath(const QString &path)
Sets the item's local path (UDS_LOCAL_PATH).
void setDelayedMimeTypes(bool b)
Sets MIME type determination to be immediate or on demand.
void refresh()
Throw away and re-read (for local files) all information about the file.
bool operator<(const KFileItem &other) const
Returns true if this item's URL is lexically less than other's URL; otherwise returns false.
void setName(const QString &name)
Sets the item's name (i.e. the filename).
Universal Directory Service.
Definition udsentry.h:78
void reserve(int size)
Calling this function before inserting items into an empty UDSEntry may save time and memory.
Definition udsentry.cpp:385
QString stringValue(uint field) const
Definition udsentry.cpp:365
long long numberValue(uint field, long long defaultValue=0) const
Definition udsentry.cpp:370
@ UDS_LOCAL_USER_ID
User ID of the file owner.
Definition udsentry.h:309
@ UDS_CREATION_TIME
The time the file was created. Required time format: seconds since UNIX epoch.
Definition udsentry.h:238
@ UDS_ICON_OVERLAY_NAMES
A comma-separated list of supplementary icon overlays which will be added to the list of overlays cre...
Definition udsentry.h:288
@ UDS_HIDDEN
Treat the file as a hidden file (if set to 1) or as a normal file (if set to 0).
Definition udsentry.h:230
@ UDS_URL
An alternative URL (If different from the caption).
Definition udsentry.h:251
@ UDS_GROUP
Group Name of the file owner Not present on local fs, use UDS_LOCAL_GROUP_ID.
Definition udsentry.h:214
@ UDS_LINK_DEST
Name of the file where the link points to Allows to check for a symlink (don't use S_ISLNK !...
Definition udsentry.h:245
@ UDS_LOCAL_GROUP_ID
Group ID of the file owner.
Definition udsentry.h:312
@ UDS_MIME_TYPE
A MIME type; the KIO worker should set it if it's known.
Definition udsentry.h:253
@ UDS_LOCAL_PATH
A local file path if the KIO worker display files sitting on the local filesystem (but in another hie...
Definition udsentry.h:227
@ UDS_FILE_TYPE
File type, part of the mode returned by stat (for a link, this returns the file type of the pointed i...
Definition udsentry.h:242
@ UDS_DISPLAY_TYPE
User-readable type of file (if not specified, the MIME type's description is used)
Definition udsentry.h:281
@ UDS_MODIFICATION_TIME
The last time the file was modified. Required time format: seconds since UNIX epoch.
Definition udsentry.h:234
@ UDS_COMMENT
A comment which will be displayed as is to the user.
Definition udsentry.h:294
@ UDS_SIZE
Size of the file.
Definition udsentry.h:203
@ UDS_DEVICE_ID
Device number for this file, used to detect hardlinks.
Definition udsentry.h:298
@ UDS_ACCESS_TIME
The last time the file was opened. Required time format: seconds since UNIX epoch.
Definition udsentry.h:236
@ UDS_DISPLAY_NAME
If set, contains the label to display instead of the 'real name' in UDS_NAME.
Definition udsentry.h:272
@ UDS_DEFAULT_ACL_STRING
The default access control list serialized into a single string.
Definition udsentry.h:267
@ UDS_NAME
Filename - as displayed in directory listings etc.
Definition udsentry.h:224
@ UDS_TARGET_URL
This file is a shortcut or mount, pointing to an URL in a different hierarchy.
Definition udsentry.h:276
@ UDS_ICON_NAME
Name of the icon, that should be used for displaying.
Definition udsentry.h:211
@ UDS_ACL_STRING
The access control list serialized into a single string.
Definition udsentry.h:264
@ UDS_RECURSIVE_SIZE
For folders, the recursize size of its content.
Definition udsentry.h:305
@ UDS_GUESSED_MIME_TYPE
A MIME type to be used for displaying only.
Definition udsentry.h:257
@ UDS_INODE
Inode number for this file, used to detect hardlinks.
Definition udsentry.h:301
@ UDS_USER
User Name of the file owner Not present on local fs, use UDS_LOCAL_USER_ID.
Definition udsentry.h:208
@ UDS_EXTENDED_ACL
Indicates that the entry has extended ACL entries.
Definition udsentry.h:262
@ UDS_ACCESS
Access permissions (part of the mode returned by stat)
Definition udsentry.h:232
bool contains(uint field) const
check existence of a field
Definition udsentry.cpp:420
void replace(uint field, const QString &value)
Replace or insert field with string value.
Definition udsentry.cpp:400
int count() const
count fields
Definition udsentry.cpp:415
static KNFSShare * instance()
Returns the one and only instance of KNFSShare.
static bool supportsWriting(const QUrl &url)
Returns whether the protocol can store data to URLs.
static KSambaShare * instance()
QString name() const
QList< KUserGroup > groups(uint maxCount=KCOREADDONS_UINT_MAX) const
QString loginName() const
QString i18n(const char *text, const TYPE &arg...)
AKONADI_CALENDAR_EXPORT QString displayName(Akonadi::ETMCalendar *calendar, const Akonadi::Collection &collection)
Type type(const QSqlDatabase &db)
KCALENDARCORE_EXPORT QDataStream & operator>>(QDataStream &in, const KCalendarCore::Alarm::Ptr &)
KCOREADDONS_EXPORT Type fileSystemType(const QString &path)
A namespace for KIO globals.
KIOCORE_EXPORT QString convertSize(KIO::filesize_t size)
Converts size from bytes to the string representation.
Definition global.cpp:43
qulonglong filesize_t
64-bit file size
Definition global.h:35
@ StatDefaultDetails
Default StatDetail flag when creating a StatJob.
Definition global.h:271
KIOCORE_EXPORT QString decodeFileName(const QString &str)
Decodes (from the filename to the text displayed) This doesn't do anything anymore,...
Definition global.cpp:120
QString path(const QString &relativePath)
KCOREADDONS_EXPORT QString tildeCollapse(const QString &path)
const char * constData() const const
QDateTime fromSecsSinceEpoch(qint64 secs)
QDateTime toLocalTime() const const
qint64 toSecsSinceEpoch() const const
QDebug & nospace()
QByteArray encodeName(const QString &fileName)
QString symLinkTarget() const const
bool isReadable() const const
bool isWritable() const const
qint64 size() const const
void append(QList< T > &&value)
const_iterator cbegin() const const
const_iterator cend() const const
qsizetype count() const const
T & first()
bool isEmpty() const const
void reserve(qsizetype size)
qsizetype size() const const
QLocale system()
QString toString(QDate date, FormatType format) const const
QMimeType mimeTypeForFile(const QFileInfo &fileInfo, MatchMode mode) const const
QMimeType mimeTypeForName(const QString &nameOrAlias) const const
QMimeType mimeTypeForUrl(const QUrl &url) const const
QList< QMimeType > mimeTypesForFileName(const QString &fileName) const const
bool inherits(const QString &mimeTypeName) const const
QString arg(Args &&... args) const const
void clear()
QString fromLatin1(QByteArrayView str)
bool isEmpty() const const
qsizetype length() const const
QString simplified() const const
bool startsWith(QChar c, Qt::CaseSensitivity cs) const const
QStringView mid(qsizetype start, qsizetype length) const const
SkipEmptyParts
StripTrailingSlash
QUrl adjusted(FormattingOptions options) const const
QString fileName(ComponentFormattingOptions options) const const
QUrl fromLocalFile(const QString &localFile)
bool isEmpty() const const
bool isLocalFile() const const
bool isValid() const const
QString path(ComponentFormattingOptions options) const const
QString scheme() const const
void setPath(const QString &path, ParsingMode mode)
QString toDisplayString(FormattingOptions options) const const
QString toLocalFile() const const
QVariant fromValue(T &&value)
static KUserId currentUserId()
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Fri May 3 2024 11:49:40 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.