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 // We cache the group name strings.
862 // will often be the same for many entries in a row. Caching them
863 // permits to use implicit sharing to save memory.
864 thread_local static QMap<quint64, QString> cachedStrings;
865 if (!cachedStrings.contains(gid)) {
866 const auto groupName = KUserGroup(gid).name();
867 cachedStrings.insert(gid, groupName);
868 }
869 return cachedStrings.value(gid);
870 }
871#endif
872 }
873 return QString();
874}
875
877{
878 if (!d) {
879 return -1;
880 }
881
883}
884
885bool KFileItemPrivate::isSlow() const
886{
887 if (m_slow == SlowUnknown) {
888 const QString path = localPath();
889 if (!path.isEmpty()) {
891 m_slow = (fsType == KFileSystemType::Nfs || fsType == KFileSystemType::Smb) ? Slow : Fast;
892 } else {
893 m_slow = Slow;
894 }
895 }
896 return m_slow == Slow;
897}
898
899bool KFileItem::isSlow() const
900{
901 if (!d) {
902 return false;
903 }
904
905 return d->isSlow();
906}
907
908QString KFileItem::mimetype() const
909{
910 if (!d) {
911 return QString();
912 }
913
914 KFileItem *that = const_cast<KFileItem *>(this);
915 return that->determineMimeType().name();
916}
917
918QMimeType KFileItem::determineMimeType() const
919{
920 if (!d) {
921 return QMimeType();
922 }
923
924 if (!d->m_mimeType.isValid() || !d->m_bMimeTypeKnown) {
925 QMimeDatabase db;
926 if (isDir()) {
927 d->m_mimeType = db.mimeTypeForName(QStringLiteral("inode/directory"));
928 } else {
929 const auto [url, isLocalUrl] = isMostLocalUrl();
930 d->determineMimeTypeHelper(url);
931
932 // was: d->m_mimeType = KMimeType::findByUrl( url, d->m_fileMode, isLocalUrl );
933 // => we are no longer using d->m_fileMode for remote URLs.
934 Q_ASSERT(d->m_mimeType.isValid());
935 // qDebug() << d << "finding final MIME type for" << url << ":" << d->m_mimeType.name();
936 }
937 d->m_bMimeTypeKnown = true;
938 }
939
940 if (d->m_delayedMimeTypes) { // if we delayed getting the iconName up till now, this is the right point in time to do so
941 d->m_delayedMimeTypes = false;
942 d->m_useIconNameCache = false;
943 (void)iconName();
944 }
945
946 return d->m_mimeType;
947}
948
949bool KFileItem::isMimeTypeKnown() const
950{
951 if (!d) {
952 return false;
953 }
954
955 // The MIME type isn't known if determineMimeType was never called (on-demand determination)
956 // or if this fileitem has a guessed MIME type (e.g. ftp symlink) - in which case
957 // it always remains "not fully determined"
958 return d->m_bMimeTypeKnown && d->m_guessedMimeType.isEmpty();
959}
960
961static bool isDirectoryMounted(const QUrl &url)
962{
963 // Stating .directory files can cause long freezes when e.g. /home
964 // uses autofs for every user's home directory, i.e. opening /home
965 // in a file dialog will mount every single home directory.
966 // These non-mounted directories can be identified by having 0 size.
967 // There are also other directories with 0 size, such as /proc, that may
968 // be mounted, but those are unlikely to contain .directory (and checking
969 // this would require checking with KMountPoint).
970
971 // TODO: maybe this could be checked with KFileSystemType instead?
972 QFileInfo info(url.toLocalFile());
973 if (info.isDir() && info.size() == 0) {
974 return false;
975 }
976 return true;
977}
978
979bool KFileItem::isFinalIconKnown() const
980{
981 if (!d) {
982 return false;
983 }
984 return d->m_bMimeTypeKnown && (!d->m_delayedMimeTypes);
985}
986
987// KDE5 TODO: merge with comment()? Need to see what lxr says about the usage of both.
988QString KFileItem::mimeComment() const
989{
990 if (!d) {
991 return QString();
992 }
993
994 const QString displayType = d->m_entry.stringValue(KIO::UDSEntry::UDS_DISPLAY_TYPE);
995 if (!displayType.isEmpty()) {
996 return displayType;
997 }
998
999 const auto [url, isLocalUrl] = isMostLocalUrl();
1000
1001 QMimeType mime = currentMimeType();
1002 // This cannot move to kio_file (with UDS_DISPLAY_TYPE) because it needs
1003 // the MIME type to be determined, which is done here, and possibly delayed...
1004 if (isLocalUrl && !d->isSlow() && mime.inherits(QStringLiteral("application/x-desktop"))) {
1005 KDesktopFile cfg(url.toLocalFile());
1006 QString comment = cfg.desktopGroup().readEntry("Comment");
1007 if (!comment.isEmpty()) {
1008 return comment;
1009 }
1010 }
1011
1012 // Support for .directory file in directories
1013 if (isLocalUrl && isDir() && !d->isSlow() && isDirectoryMounted(url)) {
1014 QUrl u(url);
1015 u.setPath(Utils::concatPaths(u.path(), QStringLiteral(".directory")));
1016 const KDesktopFile cfg(u.toLocalFile());
1017 const QString comment = cfg.readComment();
1018 if (!comment.isEmpty()) {
1019 return comment;
1020 }
1021 }
1022
1023 const QString comment = mime.comment();
1024 // qDebug() << "finding comment for " << url.url() << " : " << d->m_mimeType->name();
1025 if (!comment.isEmpty()) {
1026 return comment;
1027 } else {
1028 return mime.name();
1029 }
1030}
1031
1032static QString iconFromDirectoryFile(const QString &path)
1033{
1034 const QString filePath = path + QLatin1String("/.directory");
1035 if (!QFileInfo(filePath).isFile()) { // exists -and- is a file
1036 return QString();
1037 }
1038
1039 KDesktopFile cfg(filePath);
1040 QString icon = cfg.readIcon();
1041
1042 const KConfigGroup group = cfg.desktopGroup();
1043 const QString emptyIcon = group.readEntry("EmptyIcon");
1044 if (!emptyIcon.isEmpty()) {
1045 bool isDirEmpty = true;
1047 while (dirIt.hasNext()) {
1048 dirIt.next();
1049 if (dirIt.fileName() != QLatin1String(".directory")) {
1050 isDirEmpty = false;
1051 break;
1052 }
1053 }
1054 if (isDirEmpty) {
1055 icon = emptyIcon;
1056 }
1057 }
1058
1059 if (icon.startsWith(QLatin1String("./"))) {
1060 // path is relative with respect to the location of the .directory file (#73463)
1061 return path + QStringView(icon).mid(1);
1062 }
1063 return icon;
1064}
1065
1066static QString iconFromDesktopFile(const QString &path)
1067{
1068 KDesktopFile cfg(path);
1069 const QString icon = cfg.readIcon();
1070 if (cfg.hasLinkType()) {
1071 const KConfigGroup group = cfg.desktopGroup();
1072 const QString emptyIcon = group.readEntry("EmptyIcon");
1073 if (!emptyIcon.isEmpty()) {
1074 const QString u = cfg.readUrl();
1075 const QUrl url(u);
1076 if (url.scheme() == QLatin1String("trash")) {
1077 // We need to find if the trash is empty, preferably without using a KIO job.
1078 // So instead kio_trash leaves an entry in its config file for us.
1079 KConfig trashConfig(QStringLiteral("trashrc"), KConfig::SimpleConfig);
1080 if (trashConfig.group(QStringLiteral("Status")).readEntry("Empty", true)) {
1081 return emptyIcon;
1082 }
1083 }
1084 }
1085 }
1086 return icon;
1087}
1088
1089QString KFileItem::iconName() const
1090{
1091 if (!d) {
1092 return QString();
1093 }
1094
1095 if (d->m_useIconNameCache && !d->m_iconName.isEmpty()) {
1096 return d->m_iconName;
1097 }
1098
1099 d->m_iconName = d->m_entry.stringValue(KIO::UDSEntry::UDS_ICON_NAME);
1100 if (!d->m_iconName.isEmpty()) {
1101 d->m_useIconNameCache = d->m_bMimeTypeKnown;
1102 return d->m_iconName;
1103 }
1104
1105 const auto [url, isLocalUrl] = isMostLocalUrl();
1106
1107 QMimeDatabase db;
1108 QMimeType mime;
1109 // Use guessed MIME type for the icon
1110 if (!d->m_guessedMimeType.isEmpty()) {
1111 mime = db.mimeTypeForName(d->m_guessedMimeType);
1112 } else {
1113 mime = currentMimeType();
1114 }
1115
1116 const bool delaySlowOperations = d->m_delayedMimeTypes;
1117
1118 if (isLocalUrl && !delaySlowOperations) {
1119 const QString &localFile = url.toLocalFile();
1120
1121 if (mime.inherits(QStringLiteral("application/x-desktop"))) {
1122 d->m_iconName = iconFromDesktopFile(localFile);
1123 if (!d->m_iconName.isEmpty()) {
1124 d->m_useIconNameCache = d->m_bMimeTypeKnown;
1125 return d->m_iconName;
1126 }
1127 }
1128
1129 if (isDir()) {
1130 if (isDirectoryMounted(url)) {
1131 d->m_iconName = iconFromDirectoryFile(localFile);
1132 if (!d->m_iconName.isEmpty()) {
1133 d->m_useIconNameCache = d->m_bMimeTypeKnown;
1134 return d->m_iconName;
1135 }
1136 }
1137
1138 d->m_iconName = KIOPrivate::iconForStandardPath(localFile);
1139 if (!d->m_iconName.isEmpty()) {
1140 d->m_useIconNameCache = d->m_bMimeTypeKnown;
1141 return d->m_iconName;
1142 }
1143 }
1144 }
1145
1146 d->m_iconName = mime.iconName();
1147 d->m_useIconNameCache = d->m_bMimeTypeKnown;
1148 return d->m_iconName;
1149}
1150
1151/**
1152 * Returns true if this is a desktop file.
1153 * MIME type determination is optional.
1154 */
1155static bool checkDesktopFile(const KFileItem &item, bool _determineMimeType)
1156{
1157 // Only local files
1158 if (!item.isMostLocalUrl().local) {
1159 return false;
1160 }
1161
1162 // only regular files
1163 if (!item.isRegularFile()) {
1164 return false;
1165 }
1166
1167 // only if readable
1168 if (!item.isReadable()) {
1169 return false;
1170 }
1171
1172 // return true if desktop file
1173 QMimeType mime = _determineMimeType ? item.determineMimeType() : item.currentMimeType();
1174 return mime.inherits(QStringLiteral("application/x-desktop"));
1175}
1176
1177QStringList KFileItem::overlays() const
1178{
1179 if (!d) {
1180 return QStringList();
1181 }
1182
1183 d->ensureInitialized();
1184
1185 QStringList names = d->m_entry.stringValue(KIO::UDSEntry::UDS_ICON_OVERLAY_NAMES).split(QLatin1Char(','), Qt::SkipEmptyParts);
1186
1187 if (d->m_bLink) {
1188 names.append(QStringLiteral("emblem-symbolic-link"));
1189 }
1190
1191 if (!isReadable()) {
1192 names.append(QStringLiteral("emblem-locked"));
1193 }
1194
1195 if (checkDesktopFile(*this, false)) {
1196 KDesktopFile cfg(localPath());
1197 const KConfigGroup group = cfg.desktopGroup();
1198
1199 // Add a warning emblem if this is an executable desktop file
1200 // which is untrusted.
1201 if (group.hasKey("Exec") && !KDesktopFile::isAuthorizedDesktopFile(localPath())) {
1202 names.append(QStringLiteral("emblem-important"));
1203 }
1204 }
1205
1206 if (isHidden()) {
1207 names.append(QStringLiteral("hidden"));
1208 }
1209#ifndef Q_OS_WIN
1210 if (isDir()) {
1211 const auto [url, isLocalUrl] = isMostLocalUrl();
1212 if (isLocalUrl) {
1213 const QString path = url.toLocalFile();
1214 if (KSambaShare::instance()->isDirectoryShared(path) || KNFSShare::instance()->isDirectoryShared(path)) {
1215 names.append(QStringLiteral("emblem-shared"));
1216 }
1217 }
1218 }
1219#endif // Q_OS_WIN
1220
1221 return names;
1222}
1223
1224QString KFileItem::comment() const
1225{
1226 if (!d) {
1227 return QString();
1228 }
1229
1230 return d->m_entry.stringValue(KIO::UDSEntry::UDS_COMMENT);
1231}
1232
1233bool KFileItem::isReadable() const
1234{
1235 if (!d) {
1236 return false;
1237 }
1238
1239 d->ensureInitialized();
1240
1241 if (d->m_permissions != KFileItem::Unknown) {
1242 const mode_t readMask = S_IRUSR | S_IRGRP | S_IROTH;
1243 // No read permission at all
1244 if ((d->m_permissions & readMask) == 0) {
1245 return false;
1246 }
1247
1248 // Read permissions for all: save a stat call
1249 if ((d->m_permissions & readMask) == readMask) {
1250 return true;
1251 }
1252
1253#ifndef Q_OS_WIN
1254 const auto uidOfItem = userId();
1255 if (uidOfItem != -1) {
1256 const auto currentUser = KUserId::currentUserId();
1257 if (((uint)uidOfItem) == currentUser.nativeId()) {
1258 return S_IRUSR & d->m_permissions;
1259 }
1260 const auto gidOfItem = groupId();
1261 if (gidOfItem != -1) {
1262 if (KUser(currentUser).groups().contains(KUserGroup(gidOfItem))) {
1263 return S_IRGRP & d->m_permissions;
1264 }
1265
1266 return S_IROTH & d->m_permissions;
1267 }
1268 }
1269#else
1270 // simple special case
1271 return S_IRUSR & d->m_permissions;
1272#endif
1273 }
1274
1275 // Or if we can't read it - not network transparent
1276 if (d->m_bIsLocalUrl && !QFileInfo(d->m_url.toLocalFile()).isReadable()) {
1277 return false;
1278 }
1279
1280 return true;
1281}
1282
1283bool KFileItem::isWritable() const
1284{
1285 if (!d) {
1286 return false;
1287 }
1288
1289 d->ensureInitialized();
1290
1291 if (d->m_permissions != KFileItem::Unknown) {
1292 // No write permission at all
1293 if ((d->m_permissions & (S_IWUSR | S_IWGRP | S_IWOTH)) == 0) {
1294 return false;
1295 }
1296
1297#ifndef Q_OS_WIN
1298 const auto uidOfItem = userId();
1299 if (uidOfItem != -1) {
1300 const auto currentUser = KUserId::currentUserId();
1301 if (((uint)uidOfItem) == currentUser.nativeId()) {
1302 return S_IWUSR & d->m_permissions;
1303 }
1304 const auto gidOfItem = groupId();
1305 if (gidOfItem != -1) {
1306 if (KUser(currentUser).groups().contains(KUserGroup(gidOfItem))) {
1307 return S_IWGRP & d->m_permissions;
1308 }
1309
1310 if (S_IWOTH & d->m_permissions) {
1311 return true;
1312 }
1313 }
1314 }
1315#else
1316 // simple special case
1317 return S_IWUSR & d->m_permissions;
1318#endif
1319 }
1320
1321 // Or if we can't write it - not network transparent
1322 if (d->m_bIsLocalUrl) {
1323 return QFileInfo(d->m_url.toLocalFile()).isWritable();
1324 } else {
1325 return KProtocolManager::supportsWriting(d->m_url);
1326 }
1327}
1328
1329bool KFileItem::isHidden() const
1330{
1331 if (!d) {
1332 return false;
1333 }
1334
1335 // The KIO worker can specify explicitly that a file is hidden or shown
1336 if (d->m_hidden != KFileItemPrivate::Auto) {
1337 return d->m_hidden == KFileItemPrivate::Hidden;
1338 }
1339
1340 // Prefer the filename that is part of the URL, in case the display name is different.
1341 QString fileName = d->m_url.fileName();
1342 if (fileName.isEmpty()) { // e.g. "trash:/"
1343 fileName = d->m_strName;
1344 }
1345 return fileName.length() > 1 && fileName[0] == QLatin1Char('.'); // Just "." is current directory, not hidden.
1346}
1347
1348void KFileItem::setHidden()
1349{
1350 if (d) {
1351 d->m_hidden = KFileItemPrivate::Hidden;
1352 }
1353}
1354
1355bool KFileItem::isDir() const
1356{
1357 if (!d) {
1358 return false;
1359 }
1360
1361 if (d->m_fileMode != KFileItem::Unknown) {
1362 // File mode is known so we can use that.
1363 return Utils::isDirMask(d->m_fileMode);
1364 }
1365
1366 if (d->m_bMimeTypeKnown && d->m_mimeType.isValid()) {
1367 // File mode is not known but we do know the mime type, so use that to
1368 // avoid doing a stat.
1369 return d->m_mimeType.inherits(QStringLiteral("inode/directory"));
1370 }
1371
1372 if (d->m_bSkipMimeTypeFromContent) {
1373 return false;
1374 }
1375
1376 d->ensureInitialized();
1377
1378 if (d->m_fileMode == KFileItem::Unknown) {
1379 // Probably the file was deleted already, and KDirLister hasn't told the world yet.
1380 // qDebug() << d << url() << "can't say -> false";
1381 return false; // can't say for sure, so no
1382 }
1383 return Utils::isDirMask(d->m_fileMode);
1384}
1385
1386bool KFileItem::isFile() const
1387{
1388 if (!d) {
1389 return false;
1390 }
1391
1392 return !isDir();
1393}
1394
1395QString KFileItem::getStatusBarInfo() const
1396{
1397 if (!d) {
1398 return QString();
1399 }
1400
1401 auto toDisplayUrl = [](const QUrl &url) {
1402 QString dest;
1403 if (url.isLocalFile()) {
1404 dest = KShell::tildeCollapse(url.toLocalFile());
1405 } else {
1406 dest = url.toDisplayString();
1407 }
1408 return dest;
1409 };
1410
1411 QString text = d->m_strText;
1412 const QString comment = mimeComment();
1413
1414 if (d->m_bLink) {
1415 auto linkText = linkDest();
1416 if (!linkText.startsWith(QStringLiteral("anon_inode:"))) {
1417 linkText = toDisplayUrl(d->m_url.resolved(QUrl(linkText)));
1418 }
1419 text += QLatin1Char(' ');
1420 if (comment.isEmpty()) {
1421 text += i18n("(Symbolic Link to %1)", linkText);
1422 } else {
1423 text += i18n("(%1, Link to %2)", comment, linkText);
1424 }
1425 } else if (targetUrl() != url()) {
1426 text += i18n(" (Points to %1)", toDisplayUrl(targetUrl()));
1427 } else if (Utils::isRegFileMask(d->m_fileMode)) {
1428 text += QStringLiteral(" (%1, %2)").arg(comment, KIO::convertSize(size()));
1429 } else {
1430 text += QStringLiteral(" (%1)").arg(comment);
1431 }
1432 return text;
1433}
1434
1435bool KFileItem::cmp(const KFileItem &item) const
1436{
1437 if (!d && !item.d) {
1438 return true;
1439 }
1440
1441 if (!d || !item.d) {
1442 return false;
1443 }
1444
1445 return d->cmp(*item.d);
1446}
1447
1448bool KFileItem::operator==(const KFileItem &other) const
1449{
1450 if (!d && !other.d) {
1451 return true;
1452 }
1453
1454 if (!d || !other.d) {
1455 return false;
1456 }
1457
1458 return d->m_url == other.d->m_url;
1459}
1460
1461bool KFileItem::operator!=(const KFileItem &other) const
1462{
1463 return !operator==(other);
1464}
1465
1466bool KFileItem::operator<(const KFileItem &other) const
1467{
1468 if (!other.d) {
1469 return false;
1470 }
1471 if (!d) {
1472 return other.d->m_url.isValid();
1473 }
1474 return d->m_url < other.d->m_url;
1475}
1476
1477bool KFileItem::operator<(const QUrl &other) const
1478{
1479 if (!d) {
1480 return other.isValid();
1481 }
1482 return d->m_url < other;
1483}
1484
1485KFileItem::operator QVariant() const
1486{
1487 return QVariant::fromValue(*this);
1488}
1489
1491{
1492 if (!d) {
1493 return QString();
1494 }
1495
1496 d->ensureInitialized();
1497
1498 if (d->m_access.isNull() && d->m_permissions != KFileItem::Unknown) {
1499 d->m_access = d->parsePermissions(d->m_permissions);
1500 }
1501
1502 return d->m_access;
1503}
1504
1505// check if we need to cache this
1507{
1508 if (!d) {
1509 return QString();
1510 }
1511
1512 return QLocale::system().toString(d->time(which), QLocale::LongFormat);
1513}
1514
1516{
1517 if (!d) {
1518 return {};
1519 }
1520
1521 const auto [url, isLocal] = isMostLocalUrl();
1522 if (local) {
1523 *local = isLocal;
1524 }
1525 return url;
1526}
1527
1528KFileItem::MostLocalUrlResult KFileItem::isMostLocalUrl() const
1529{
1530 if (!d) {
1531 return {QUrl(), false};
1532 }
1533
1534 const QString local_path = localPath();
1535 if (!local_path.isEmpty()) {
1536 return {QUrl::fromLocalFile(local_path), true};
1537 } else {
1538 return {d->m_url, d->m_bIsLocalUrl};
1539 }
1540}
1541
1542QDataStream &operator<<(QDataStream &s, const KFileItem &a)
1543{
1544 if (a.d) {
1545 // We don't need to save/restore anything that refresh() invalidates,
1546 // since that means we can re-determine those by ourselves.
1547 s << a.d->m_url;
1548 s << a.d->m_strName;
1549 s << a.d->m_strText;
1550 } else {
1551 s << QUrl();
1552 s << QString();
1553 s << QString();
1554 }
1555
1556 return s;
1557}
1558
1560{
1561 QUrl url;
1562 QString strName;
1563 QString strText;
1564
1565 s >> url;
1566 s >> strName;
1567 s >> strText;
1568
1569 if (!a.d) {
1570 qCWarning(KIO_CORE) << "null item";
1571 return s;
1572 }
1573
1574 if (url.isEmpty()) {
1575 a.d = nullptr;
1576 return s;
1577 }
1578
1579 a.d->m_url = url;
1580 a.d->m_strName = strName;
1581 a.d->m_strText = strText;
1582 a.d->m_bIsLocalUrl = a.d->m_url.isLocalFile();
1583 a.d->m_bMimeTypeKnown = false;
1584 a.refresh();
1585
1586 return s;
1587}
1588
1589QUrl KFileItem::url() const
1590{
1591 if (!d) {
1592 return QUrl();
1593 }
1594
1595 return d->m_url;
1596}
1597
1599{
1600 if (!d) {
1601 return 0;
1602 }
1603
1604 d->ensureInitialized();
1605
1606 return d->m_permissions;
1607}
1608
1609mode_t KFileItem::mode() const
1610{
1611 if (!d) {
1612 return 0;
1613 }
1614
1615 d->ensureInitialized();
1616
1617 return d->m_fileMode;
1618}
1619
1620bool KFileItem::isLink() const
1621{
1622 if (!d) {
1623 return false;
1624 }
1625
1626 d->ensureInitialized();
1627
1628 return d->m_bLink;
1629}
1630
1631bool KFileItem::isLocalFile() const
1632{
1633 if (!d) {
1634 return false;
1635 }
1636
1637 return d->m_bIsLocalUrl;
1638}
1639
1640QString KFileItem::text() const
1641{
1642 if (!d) {
1643 return QString();
1644 }
1645
1646 return d->m_strText;
1647}
1648
1649QString KFileItem::name(bool lowerCase) const
1650{
1651 if (!d) {
1652 return QString();
1653 }
1654
1655 if (!lowerCase) {
1656 return d->m_strName;
1657 } else if (d->m_strLowerCaseName.isNull()) {
1658 d->m_strLowerCaseName = d->m_strName.toLower();
1659 }
1660 return d->m_strLowerCaseName;
1661}
1662
1663QUrl KFileItem::targetUrl() const
1664{
1665 if (!d) {
1666 return QUrl();
1667 }
1668
1669 const QString targetUrlStr = d->m_entry.stringValue(KIO::UDSEntry::UDS_TARGET_URL);
1670 if (!targetUrlStr.isEmpty()) {
1671 return QUrl(targetUrlStr);
1672 } else {
1673 return url();
1674 }
1675}
1676
1677/*
1678 * MIME type handling.
1679 *
1680 * Initial state: m_mimeType = QMimeType().
1681 * When currentMimeType() is called first: fast MIME type determination,
1682 * might either find an accurate MIME type (-> Final state), otherwise we
1683 * set m_mimeType but not m_bMimeTypeKnown (-> Intermediate state)
1684 * Intermediate state: determineMimeType() does the real determination -> Final state.
1685 *
1686 * If delayedMimeTypes isn't set, then we always go to the Final state directly.
1687 */
1688
1689QMimeType KFileItem::currentMimeType() const
1690{
1691 if (!d || d->m_url.isEmpty()) {
1692 return QMimeType();
1693 }
1694
1695 if (!d->m_mimeType.isValid()) {
1696 // On-demand fast (but not always accurate) MIME type determination
1697 QMimeDatabase db;
1698 if (isDir()) {
1699 d->m_mimeType = db.mimeTypeForName(QStringLiteral("inode/directory"));
1700 return d->m_mimeType;
1701 }
1702 const QUrl url = mostLocalUrl();
1703 if (d->m_delayedMimeTypes) {
1704 const QList<QMimeType> mimeTypes = db.mimeTypesForFileName(url.path());
1705 if (mimeTypes.isEmpty()) {
1706 d->m_mimeType = db.mimeTypeForName(QStringLiteral("application/octet-stream"));
1707 d->m_bMimeTypeKnown = false;
1708 } else {
1709 d->m_mimeType = mimeTypes.first();
1710 // If there were conflicting globs. determineMimeType will be able to do better.
1711 d->m_bMimeTypeKnown = (mimeTypes.count() == 1);
1712 }
1713 } else {
1714 // ## d->m_fileMode isn't used anymore (for remote urls)
1715 d->determineMimeTypeHelper(url);
1716 d->m_bMimeTypeKnown = true;
1717 }
1718 }
1719 return d->m_mimeType;
1720}
1721
1723{
1724 if (!d) {
1725 return KIO::UDSEntry();
1726 }
1727
1728 d->ensureInitialized();
1729
1730 return d->m_entry;
1731}
1732
1734{
1735 return d == nullptr;
1736}
1737
1739{
1740 if (!d) {
1741 return false;
1742 }
1743 if (!d->m_bInitCalled) {
1744 qCWarning(KIO_CORE) << "KFileItem: exists called when not initialised" << d->m_url;
1745 return false;
1746 }
1747 return d->m_fileMode != KFileItem::Unknown;
1748}
1749
1751{
1752 if (!d) {
1753 return false;
1754 }
1755
1756 d->ensureInitialized();
1757
1758 if (d->m_permissions == KFileItem::Unknown) {
1759 return false;
1760 }
1761
1762 const mode_t executableMask = S_IXGRP | S_IXUSR | S_IXOTH;
1763 if ((d->m_permissions & executableMask) == 0) {
1764 return false;
1765 }
1766
1767#ifndef Q_OS_WIN
1768 const auto uid = userId();
1769 if (uid != -1) {
1770 if (((uint)uid) == KUserId::currentUserId().nativeId()) {
1771 return S_IXUSR & d->m_permissions;
1772 }
1773 const auto gid = groupId();
1774 if (gid != -1) {
1775 const KUser kuser = KUser(uid);
1776 if (kuser.groups().contains(KUserGroup(gid))) {
1777 return S_IXGRP & d->m_permissions;
1778 }
1779
1780 return S_IXOTH & d->m_permissions;
1781 }
1782 }
1783 return false;
1784#else
1785 // simple special case
1786 return S_IXUSR & d->m_permissions;
1787#endif
1788}
1789
1793
1795 : QList<KFileItem>(items)
1796{
1797}
1798
1799KFileItemList::KFileItemList(std::initializer_list<KFileItem> items)
1800 : QList<KFileItem>(items)
1801{
1802}
1803
1805{
1806 auto it = std::find_if(cbegin(), cend(), [&fileName](const KFileItem &item) {
1807 return item.name() == fileName;
1808 });
1809
1810 return it != cend() ? *it : KFileItem();
1811}
1812
1814{
1815 auto it = std::find_if(cbegin(), cend(), [&url](const KFileItem &item) {
1816 return item.url() == url;
1817 });
1818
1819 return it != cend() ? *it : KFileItem();
1820}
1821
1823{
1824 QList<QUrl> lst;
1825 lst.reserve(size());
1826
1827 for (const auto &item : *this) {
1828 lst.append(item.url());
1829 }
1830 return lst;
1831}
1832
1834{
1835 QList<QUrl> lst;
1836 lst.reserve(size());
1837
1838 for (const auto &item : *this) {
1839 lst.append(item.targetUrl());
1840 }
1841 return lst;
1842}
1843
1844bool KFileItem::isDesktopFile() const
1845{
1846 return checkDesktopFile(*this, true);
1847}
1848
1849bool KFileItem::isRegularFile() const
1850{
1851 if (!d) {
1852 return false;
1853 }
1854
1855 d->ensureInitialized();
1856
1857 return Utils::isRegFileMask(d->m_fileMode);
1858}
1859
1861{
1862 if (!d || isDir()) {
1863 return QString();
1864 }
1865
1866 const int lastDot = d->m_strText.lastIndexOf(QStringLiteral("."));
1867 if (lastDot > 0) {
1868 return d->m_strText.mid(lastDot + 1);
1869 } else {
1870 return QString();
1871 }
1872}
1873
1874QDebug operator<<(QDebug stream, const KFileItem &item)
1875{
1876 QDebugStateSaver saver(stream);
1877 stream.nospace();
1878 if (item.isNull()) {
1879 stream << "[null KFileItem]";
1880 } else {
1881 stream << "[KFileItem for " << item.url() << "]";
1882 }
1883 return stream;
1884}
1885
1886#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
bool contains(const Key &key) const const
iterator insert(const Key &key, const T &value)
T value(const Key &key, const T &defaultValue) 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 17 2024 11:53:29 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.