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

KDE's Doxygen guidelines are available online.