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

KDE's Doxygen guidelines are available online.