KIO

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

KDE's Doxygen guidelines are available online.