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

KDE's Doxygen guidelines are available online.