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

KDE's Doxygen guidelines are available online.